Skip to content

Commit e2a09df

Browse files
committed
## TASK-043-002: Migrate TurboHttp.StreamTests RFC1945 → Http10/
Move all 9 stream test files from `TurboHttp.StreamTests/RFC1945/` to `TurboHttp.StreamTests/Http10/`, applying post-Feature-040 conventions: - Namespace: `TurboHttp.StreamTests.Http10` - Class names: `Spec` suffix, `sealed` - Method names: BDD style (`Subject_should_behavior()`) - RFC traceability: `[Trait("RFC", "RFC1945-...")]` on each test - No `DisplayName` RFC tags - Numeric file prefixes dropped Files renamed: 01_Http10EncoderStageTests.cs → Http10EncoderSpec.cs 02_Http10EncoderStageWireFormatTests → Http10EncoderWireFormatSpec.cs 03_Http10DecoderStageTests.cs → Http10DecoderSpec.cs 04_Http10DecoderStageResponseParsing → Http10DecoderResponseParsingSpec.cs 05_Http10RoundTripMethodTests.cs → Http10RoundTripMethodSpec.cs 06_Http10RoundTripHeaderBodyPres... → Http10RoundTripHeaderBodySpec.cs 07_Http10TcpFragmentationReassembly → Http10TcpFragmentationSpec.cs 08_Http10EngineEndToEndTests.cs → Http10EngineEndToEndSpec.cs 09_Http10DecompressionPipeline... → Http10DecompressionPipelineSpec.cs RFC1945/ folder deleted. All 658 StreamTests pass.
1 parent 51bb27a commit e2a09df

10 files changed

Lines changed: 182 additions & 145 deletions

.maggus/features/feature_043.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ StreamTests/Http10/ (encoder/decoder stages, round-trip, TCP fragmentation).
6464
**Parallel:** yes — can run alongside TASK-043-001
6565

6666
**Acceptance Criteria:**
67-
- [ ] All 9 files in `TurboHttp.StreamTests/RFC1945/` moved to `TurboHttp.StreamTests/Http10/`
68-
- [ ] Namespaces updated to `TurboHttp.StreamTests.Http10`
69-
- [ ] All class names end with `Spec`
70-
- [ ] All method names follow BDD pattern
71-
- [ ] No `DisplayName` with RFC tags remain in migrated files
72-
- [ ] `[Trait("RFC", "RFC1945-...")]` present on each test
73-
- [ ] `dotnet test --project TurboHttp.StreamTests/TurboHttp.StreamTests.csproj` green
67+
- [x] All 9 files in `TurboHttp.StreamTests/RFC1945/` moved to `TurboHttp.StreamTests/Http10/`
68+
- [x] Namespaces updated to `TurboHttp.StreamTests.Http10`
69+
- [x] All class names end with `Spec`
70+
- [x] All method names follow BDD pattern
71+
- [x] No `DisplayName` with RFC tags remain in migrated files
72+
- [x] `[Trait("RFC", "RFC1945-...")]` present on each test
73+
- [x] `dotnet test --project TurboHttp.StreamTests/TurboHttp.StreamTests.csproj` green
7474

7575
---
7676

src/TurboHttp.StreamTests/RFC1945/04_Http10DecoderStageResponseParsingTests.cs renamed to src/TurboHttp.StreamTests/Http10/Http10DecoderResponseParsingSpec.cs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
using System.Text;
1+
using System.Text;
22
using Akka.Streams.Dsl;
33
using TurboHttp.Internal;
44
using TurboHttp.Streams.Stages.Decoding;
55

6-
namespace TurboHttp.StreamTests.RFC1945;
6+
namespace TurboHttp.StreamTests.Http10;
77

88
/// <summary>
99
/// RFC-tagged tests for the HTTP/1.0 response decoder stage per RFC 1945.
@@ -13,7 +13,7 @@ namespace TurboHttp.StreamTests.RFC1945;
1313
/// Stage under test: <see cref="Http10DecoderStage"/>.
1414
/// RFC 1945 §6: HTTP/1.0 response message format, status lines, and header fields.
1515
/// </remarks>
16-
public sealed class Http10DecoderStageResponseParsingTests : StreamTestBase
16+
public sealed class Http10DecoderResponseParsingSpec : StreamTestBase
1717
{
1818
private static IInputItem Chunk(string ascii)
1919
{
@@ -37,26 +37,28 @@ private async Task<IReadOnlyList<HttpResponseMessage>> DecodeAllAsync(params str
3737
.RunWith(Sink.Seq<HttpResponseMessage>(), Materializer);
3838
}
3939

40-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-6.1-10DS-001: Status-line HTTP/1.0 200 OK → StatusCode=200, Version=1.0")]
41-
public async Task Should_ParseStatusCode200AndVersion10_When_StatusLineIs200OK()
40+
[Fact(Timeout = 10_000)]
41+
[Trait("RFC", "RFC1945-6.1")]
42+
public async Task Http10DecoderResponseParsing_should_parse_status_code_200_and_version_10_when_status_line_is_200ok()
4243
{
4344
var response = await DecodeAsync("HTTP/1.0 200 OK\r\n\r\n");
4445

4546
Assert.Equal(200, (int)response.StatusCode);
4647
Assert.Equal(new Version(1, 0), response.Version);
4748
}
4849

49-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-6.1-10DS-002: Status-line HTTP/1.0 404 Not Found → StatusCode=404")]
50-
public async Task Should_ParseStatusCode404_When_StatusLineIs404NotFound()
50+
[Fact(Timeout = 10_000)]
51+
[Trait("RFC", "RFC1945-6.1")]
52+
public async Task Http10DecoderResponseParsing_should_parse_status_code_404_when_status_line_is_404_not_found()
5153
{
5254
var response = await DecodeAsync("HTTP/1.0 404 Not Found\r\n\r\n");
5355

5456
Assert.Equal(404, (int)response.StatusCode);
5557
}
5658

57-
[Fact(Timeout = 10_000,
58-
DisplayName = "RFC1945-6.2-10DS-003: Response headers Content-Type and Content-Length correctly parsed")]
59-
public async Task Should_ParseContentTypeAndContentLength_When_ResponseHeadersPresent()
59+
[Fact(Timeout = 10_000)]
60+
[Trait("RFC", "RFC1945-6.2")]
61+
public async Task Http10DecoderResponseParsing_should_parse_content_type_and_content_length_when_headers_present()
6062
{
6163
const string raw =
6264
"HTTP/1.0 200 OK\r\n" +
@@ -72,8 +74,9 @@ public async Task Should_ParseContentTypeAndContentLength_When_ResponseHeadersPr
7274
Assert.Equal(4L, response.Content.Headers.ContentLength);
7375
}
7476

75-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-7-10DS-004: Body with Content-Length correctly read")]
76-
public async Task Should_ReadBodyCorrectly_When_ContentLengthHeaderPresent()
77+
[Fact(Timeout = 10_000)]
78+
[Trait("RFC", "RFC1945-7")]
79+
public async Task Http10DecoderResponseParsing_should_read_body_correctly_when_content_length_header_present()
7780
{
7881
const string raw =
7982
"HTTP/1.0 200 OK\r\n" +
@@ -87,9 +90,9 @@ public async Task Should_ReadBodyCorrectly_When_ContentLengthHeaderPresent()
8790
Assert.Equal("Hello, World!", body);
8891
}
8992

90-
[Fact(Timeout = 10_000,
91-
DisplayName = "RFC1945-6-10DS-005: Connection-Close — stream ends after body, exactly 1 response emitted")]
92-
public async Task Should_EmitExactlyOneResponse_When_ConnectionClosesAfterBody()
93+
[Fact(Timeout = 10_000)]
94+
[Trait("RFC", "RFC1945-6")]
95+
public async Task Http10DecoderResponseParsing_should_emit_exactly_one_response_when_connection_closes_after_body()
9396
{
9497
// HTTP/1.0 has no persistent connections; after the body the connection closes.
9598
// The decoder stage must emit exactly one response and then complete cleanly.
@@ -106,4 +109,4 @@ public async Task Should_EmitExactlyOneResponse_When_ConnectionClosesAfterBody()
106109
var body = await responses[0].Content.ReadAsStringAsync(TestContext.Current.CancellationToken);
107110
Assert.Equal("hello", body);
108111
}
109-
}
112+
}

src/TurboHttp.StreamTests/RFC1945/03_Http10DecoderStageTests.cs renamed to src/TurboHttp.StreamTests/Http10/Http10DecoderSpec.cs

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
using System.Net;
1+
using System.Net;
22
using System.Text;
33
using Akka.Streams.Dsl;
44
using TurboHttp.Internal;
55
using TurboHttp.Streams.Stages.Decoding;
66

7-
namespace TurboHttp.StreamTests.RFC1945;
7+
namespace TurboHttp.StreamTests.Http10;
88

99
/// <summary>
1010
/// Tests the HTTP/1.0 response decoder stage per RFC 1945.
@@ -14,7 +14,7 @@ namespace TurboHttp.StreamTests.RFC1945;
1414
/// Stage under test: <see cref="Http10DecoderStage"/>.
1515
/// RFC 1945 §6: HTTP/1.0 response message format and parsing.
1616
/// </remarks>
17-
public sealed class Http10DecoderStageTests : StreamTestBase
17+
public sealed class Http10DecoderSpec : StreamTestBase
1818
{
1919
private static IInputItem Chunk(string ascii)
2020
{
@@ -30,26 +30,29 @@ private async Task<HttpResponseMessage> DecodeAsync(params string[] chunks)
3030
.RunWith(Sink.First<HttpResponseMessage>(), Materializer);
3131
}
3232

33-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-6.1-10DS-001: Status-Line decoded to StatusCode and Version")]
34-
public async Task Should_DecodeStatusLineToStatusCodeAndVersion_When_ValidHttp10Response()
33+
[Fact(Timeout = 10_000)]
34+
[Trait("RFC", "RFC1945-6.1")]
35+
public async Task Http10Decoder_should_decode_status_line_to_status_code_and_version_when_valid_response()
3536
{
3637
var response = await DecodeAsync("HTTP/1.0 200 OK\r\n\r\n");
3738

3839
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
3940
Assert.Equal(HttpVersion.Version10, response.Version);
4041
}
4142

42-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-6.2-10DS-002: Response header decoded to response.Headers")]
43-
public async Task Should_DecodeResponseHeaderToResponseHeaders_When_HeaderPresent()
43+
[Fact(Timeout = 10_000)]
44+
[Trait("RFC", "RFC1945-6.2")]
45+
public async Task Http10Decoder_should_decode_response_header_to_response_headers_when_header_present()
4446
{
4547
var response = await DecodeAsync("HTTP/1.0 200 OK\r\nX-Custom: test\r\n\r\n");
4648

4749
Assert.True(response.Headers.TryGetValues("X-Custom", out var values));
4850
Assert.Equal("test", values.First());
4951
}
5052

51-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-7-10DS-003: Body delimited by Content-Length decoded correctly")]
52-
public async Task Should_DecodeBodyCorrectly_When_DelimitedByContentLength()
53+
[Fact(Timeout = 10_000)]
54+
[Trait("RFC", "RFC1945-7")]
55+
public async Task Http10Decoder_should_decode_body_correctly_when_delimited_by_content_length()
5356
{
5457
var response = await DecodeAsync("HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nhello");
5558

@@ -58,16 +61,18 @@ public async Task Should_DecodeBodyCorrectly_When_DelimitedByContentLength()
5861
Assert.Equal("hello", Encoding.ASCII.GetString(body));
5962
}
6063

61-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-6.1-10DS-004: 404 response decoded to HttpStatusCode.NotFound")]
62-
public async Task Should_Decode404ToNotFound_When_StatusCodeIs404()
64+
[Fact(Timeout = 10_000)]
65+
[Trait("RFC", "RFC1945-6.1")]
66+
public async Task Http10Decoder_should_decode_404_to_not_found_when_status_code_is_404()
6367
{
6468
var response = await DecodeAsync("HTTP/1.0 404 Not Found\r\n\r\n");
6569

6670
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
6771
}
6872

69-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-7-10DS-005: Response split across two TCP chunks reassembled")]
70-
public async Task Should_ReassembleFragmentedResponse_When_SplitAcrossTwoChunks()
73+
[Fact(Timeout = 10_000)]
74+
[Trait("RFC", "RFC1945-7")]
75+
public async Task Http10Decoder_should_reassemble_fragmented_response_when_split_across_two_chunks()
7176
{
7277
// Body split: first chunk has partial body ("he"), second chunk has remainder ("llo")
7378
var response = await DecodeAsync("HTTP/1.0 200 OK\r\nContent-Length: 5\r\n\r\nhe", "llo");
@@ -76,4 +81,4 @@ public async Task Should_ReassembleFragmentedResponse_When_SplitAcrossTwoChunks(
7681
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
7782
Assert.Equal("hello", Encoding.ASCII.GetString(body));
7883
}
79-
}
84+
}

src/TurboHttp.StreamTests/RFC1945/09_Http10DecompressionPipelineTests.cs renamed to src/TurboHttp.StreamTests/Http10/Http10DecompressionPipelineSpec.cs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.IO.Compression;
1+
using System.IO.Compression;
22
using System.Net;
33
using System.Text;
44
using Akka;
@@ -7,7 +7,7 @@
77
using TurboHttp.Streams;
88
using TurboHttp.Streams.Stages.Features;
99

10-
namespace TurboHttp.StreamTests.RFC1945;
10+
namespace TurboHttp.StreamTests.Http10;
1111

1212
/// <summary>
1313
/// Verifies that the HTTP/1.0 engine pipeline correctly decompresses
@@ -18,7 +18,7 @@ namespace TurboHttp.StreamTests.RFC1945;
1818
/// RFC 9110 §8.4: Content-Encoding and transparent decompression.
1919
/// RFC 1945 §10.3: Content-Encoding entity-header in HTTP/1.0.
2020
/// </remarks>
21-
public sealed class Http10DecompressionPipelineTests : EngineTestBase
21+
public sealed class Http10DecompressionPipelineSpec : EngineTestBase
2222
{
2323
private static readonly Http10Engine Engine = new();
2424

@@ -65,8 +65,9 @@ private static byte[] BuildHttp10Response(byte[] body, string? contentEncoding =
6565

6666
// gzip decompression
6767

68-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-001: Content-Encoding: gzip → body decompressed through Http10Engine")]
69-
public async Task Should_DecompressGzipBody_When_ContentEncodingIsGzip()
68+
[Fact(Timeout = 10_000)]
69+
[Trait("RFC", "RFC1945-10.3")]
70+
public async Task Http10DecompressionPipeline_should_decompress_gzip_body_when_content_encoding_is_gzip()
7071
{
7172
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/gzip")
7273
{
@@ -87,8 +88,9 @@ public async Task Should_DecompressGzipBody_When_ContentEncodingIsGzip()
8788
Assert.Equal(expectedBody, body);
8889
}
8990

90-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-002: Content-Encoding header removed after gzip decompression")]
91-
public async Task Should_RemoveContentEncodingHeader_When_GzipDecompressed()
91+
[Fact(Timeout = 10_000)]
92+
[Trait("RFC", "RFC1945-10.3")]
93+
public async Task Http10DecompressionPipeline_should_remove_content_encoding_header_when_gzip_decompressed()
9294
{
9395
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/gzip-header")
9496
{
@@ -106,8 +108,9 @@ public async Task Should_RemoveContentEncodingHeader_When_GzipDecompressed()
106108
Assert.False(response.Content.Headers.Contains("Content-Encoding"));
107109
}
108110

109-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-003: Content-Length updated to decompressed size after gzip")]
110-
public async Task Should_UpdateContentLength_When_GzipDecompressed()
111+
[Fact(Timeout = 10_000)]
112+
[Trait("RFC", "RFC1945-10.3")]
113+
public async Task Http10DecompressionPipeline_should_update_content_length_when_gzip_decompressed()
111114
{
112115
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/gzip-len")
113116
{
@@ -128,8 +131,9 @@ public async Task Should_UpdateContentLength_When_GzipDecompressed()
128131

129132
// x-gzip decompression
130133

131-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-004: Content-Encoding: x-gzip → body decompressed through Http10Engine")]
132-
public async Task Should_DecompressBody_When_ContentEncodingIsXGzip()
134+
[Fact(Timeout = 10_000)]
135+
[Trait("RFC", "RFC1945-10.3")]
136+
public async Task Http10DecompressionPipeline_should_decompress_body_when_content_encoding_is_x_gzip()
133137
{
134138
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/x-gzip")
135139
{
@@ -150,8 +154,9 @@ public async Task Should_DecompressBody_When_ContentEncodingIsXGzip()
150154
Assert.Equal(expectedBody, body);
151155
}
152156

153-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-005: Content-Encoding header removed after x-gzip decompression")]
154-
public async Task Should_RemoveContentEncodingHeader_When_XGzipDecompressed()
157+
[Fact(Timeout = 10_000)]
158+
[Trait("RFC", "RFC1945-10.3")]
159+
public async Task Http10DecompressionPipeline_should_remove_content_encoding_header_when_x_gzip_decompressed()
155160
{
156161
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/x-gzip-header")
157162
{
@@ -171,8 +176,9 @@ public async Task Should_RemoveContentEncodingHeader_When_XGzipDecompressed()
171176

172177
// identity encoding (pass-through)
173178

174-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-006: Content-Encoding: identity → body passes through unchanged")]
175-
public async Task Should_PassBodyUnchanged_When_ContentEncodingIsIdentity()
179+
[Fact(Timeout = 10_000)]
180+
[Trait("RFC", "RFC1945-10.3")]
181+
public async Task Http10DecompressionPipeline_should_pass_body_unchanged_when_content_encoding_is_identity()
176182
{
177183
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/identity")
178184
{
@@ -193,8 +199,9 @@ public async Task Should_PassBodyUnchanged_When_ContentEncodingIsIdentity()
193199
Assert.Equal(expectedBody, body);
194200
}
195201

196-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-007: no Content-Encoding header → body passes through unchanged")]
197-
public async Task Should_PassBodyUnchanged_When_NoContentEncoding()
202+
[Fact(Timeout = 10_000)]
203+
[Trait("RFC", "RFC1945-10.3")]
204+
public async Task Http10DecompressionPipeline_should_pass_body_unchanged_when_no_content_encoding()
198205
{
199206
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/plain")
200207
{
@@ -217,8 +224,9 @@ public async Task Should_PassBodyUnchanged_When_NoContentEncoding()
217224

218225
// Content-Type preservation
219226

220-
[Fact(Timeout = 10_000, DisplayName = "RFC1945-10.3-10DEC-008: Content-Type preserved after gzip decompression")]
221-
public async Task Should_PreserveContentType_When_GzipDecompressed()
227+
[Fact(Timeout = 10_000)]
228+
[Trait("RFC", "RFC1945-10.3")]
229+
public async Task Http10DecompressionPipeline_should_preserve_content_type_when_gzip_decompressed()
222230
{
223231
var request = new HttpRequestMessage(HttpMethod.Get, "http://example.com/json-gzip")
224232
{

0 commit comments

Comments
 (0)