diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c1e7906b..4cfa1f36 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,11 @@ jobs: with: dotnet-version: '8.0.x' + - name: Setup .NET 10 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + - name: 'WireMock.Net.Tests' run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0 @@ -31,7 +36,7 @@ jobs: run: dotnet test './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' -c Release - name: 'WireMock.Net.TUnitTests' - run: dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0 -p:UseVSTest=false + run: dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release -p:UseVSTest=false - name: 'WireMock.Net.Middleware.Tests' run: dotnet test './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release --framework net8.0 @@ -51,6 +56,11 @@ jobs: with: dotnet-version: '8.0.x' + - name: Setup .NET 10 + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '10.0.x' + - name: 'WireMock.Net.Tests' run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0 @@ -58,7 +68,7 @@ jobs: run: dotnet test './test/WireMock.Net.Tests.UsingNuGet/WireMock.Net.Tests.UsingNuGet.csproj' -c Release - name: 'WireMock.Net.TUnitTests' - run: dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release --framework net8.0 + run: dotnet test './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj' -c Release -p:UseVSTest=false - name: 'WireMock.Net.Middleware.Tests' run: dotnet test './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj' -c Release --framework net8.0 diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml index a2b2a4a4..d7a79f5c 100644 --- a/azure-pipelines-ci.yml +++ b/azure-pipelines-ci.yml @@ -53,7 +53,7 @@ jobs: - task: CmdLine@2 inputs: script: | - dotnet-coverage collect "dotnet test ./test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj --configuration Debug -p:UseVSTest=false --no-build --framework net8.0" -f xml -o "wiremock-coverage-tunit.xml" + dotnet-coverage collect "dotnet test ./test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj --configuration Debug -p:UseVSTest=false --no-build" -f xml -o "wiremock-coverage-tunit.xml" displayName: 'WireMock.Net.TUnitTests with Coverage' - task: CmdLine@2 @@ -109,6 +109,12 @@ jobs: packageType: 'sdk' version: '8.0.x' + - task: UseDotNet@2 + displayName: Use .NET 10.0 + inputs: + packageType: 'sdk' + version: '10.0.x' + - task: DotNetCoreCLI@2 displayName: 'WireMock.Net.Tests.UsingNuGet' inputs: diff --git a/test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj b/test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj index 18c2582f..2ad8ca45 100644 --- a/test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj +++ b/test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj @@ -1,7 +1,7 @@  - net8.0 + net10.0 Exe enable enable diff --git a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.GetMappings.cs b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.GetMappings.cs index 3e24312c..2a73f7b0 100644 --- a/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.GetMappings.cs +++ b/test/WireMock.Net.Tests/AdminApi/WireMockAdminApiTests.GetMappings.cs @@ -50,7 +50,7 @@ message HelloReply { // Act var client = server.CreateClient(); - var getMappingsResult = await client.GetStringAsync("/__admin/mappings", TestContext.Current.CancellationToken); + var getMappingsResult = await client.GetStringAsync("/__admin/mappings"); await VerifyJson(getMappingsResult, VerifySettings); } diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs index 9933cafa..c787b87b 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs @@ -332,7 +332,7 @@ public class ResponseWithBodyTests var responseBuilder = Response.Create().WithBody(new { foo = "< > & ' 😀 👍 ❤️", n = 42 }, new NewtonsoftJsonConverter()); // Act - var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, _settings).ConfigureAwait(false); + var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, _settings); // Assert response.Message.BodyData!.BodyAsString.Should().Be("""{"foo":"< > & ' 😀 👍 ❤️","n":42}"""); diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs index a612d973..8aa7768a 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithCallbackTests.cs @@ -123,7 +123,7 @@ public class ResponseWithCallbackTests .WithHeader("H2", "X2") .WithBody(async req => { - await Task.Delay(1).ConfigureAwait(false); + await Task.Delay(1); return $"path: {req.Path}"; }); diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsLinqTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsLinqTests.cs index 211058e4..8391e4d3 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsLinqTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsLinqTests.cs @@ -200,7 +200,7 @@ public class ResponseWithHandlebarsLinqTests .WithTransformer(); // Act - Func a = async () => await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, _settings).ConfigureAwait(false); + Func a = async () => await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, _settings); // Assert a.Should().ThrowAsync(); @@ -227,7 +227,7 @@ public class ResponseWithHandlebarsLinqTests .WithTransformer(); // Act - Func a = async () => await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, _settings).ConfigureAwait(false); + Func a = async () => await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, _settings); // Assert a.Should().ThrowAsync(); diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithTransformerTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithTransformerTests.cs index 7e56ef79..70e6445a 100644 --- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithTransformerTests.cs +++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithTransformerTests.cs @@ -469,7 +469,7 @@ public class ResponseWithTransformerTests .WithTransformer(transformerType); // Act - var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, settings).ConfigureAwait(false); + var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, settings); // Assert JsonConvert.SerializeObject(response.Message.BodyData!.BodyAsJson).Should().Be(expected); diff --git a/test/WireMock.Net.Tests/Settings/HandlebarsSettingsTests.cs b/test/WireMock.Net.Tests/Settings/HandlebarsSettingsTests.cs index e4f85dc2..10161564 100644 --- a/test/WireMock.Net.Tests/Settings/HandlebarsSettingsTests.cs +++ b/test/WireMock.Net.Tests/Settings/HandlebarsSettingsTests.cs @@ -71,7 +71,7 @@ public class HandlebarsSettingsTests .WithTransformer(); // Act - var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, settingsWithEnv).ConfigureAwait(false); + var response = await responseBuilder.ProvideResponseAsync(_mappingMock.Object, Mock.Of(), request, settingsWithEnv); // Assert response.Message?.BodyData?.BodyAsString.Should().NotContain("{{Environment.GetEnvironmentVariable"); diff --git a/test/WireMock.Net.Tests/Testcontainers/TestcontainersTests.cs b/test/WireMock.Net.Tests/Testcontainers/TestcontainersTests.cs index 8bcc79e5..2de6a1a1 100644 --- a/test/WireMock.Net.Tests/Testcontainers/TestcontainersTests.cs +++ b/test/WireMock.Net.Tests/Testcontainers/TestcontainersTests.cs @@ -112,7 +112,7 @@ public class TestcontainersTests(ITestOutputHelper testOutputHelper) private static async Task StartTestAsync(WireMockContainer wireMockContainer) { // Start - await wireMockContainer.StartAsync().ConfigureAwait(false); + await wireMockContainer.StartAsync(); // Assert using (new AssertionScope()) diff --git a/test/WireMock.Net.Tests/WebSockets/README.md b/test/WireMock.Net.Tests/WebSockets/README.md deleted file mode 100644 index 88c49942..00000000 --- a/test/WireMock.Net.Tests/WebSockets/README.md +++ /dev/null @@ -1,248 +0,0 @@ -# WebSocket Integration Tests - Summary - -## Overview -Comprehensive integration tests for the WebSockets implementation in WireMock.Net. These tests are based on Examples 1, 2, and 3 from `WireMock.Net.WebSocketExamples` and use `ClientWebSocket` to perform real WebSocket connections. - -## Test File -- **Location**: `test\WireMock.Net.Tests\WebSockets\WebSocketIntegrationTests.cs` -- **Test Count**: 21 integration tests -- **Test Framework**: xUnit with FluentAssertions - -## Test Coverage Summary - -| Category | Tests | Description | -|----------|-------|-------------| -| **Example 1: Echo Server** | 4 | Basic echo functionality with text/binary messages | -| **Example 2: Custom Handlers** | 8 | Command processing and custom message handlers | -| **Example 3: JSON (SendJsonAsync)** | 3 | JSON serialization and complex object handling | -| **Broadcast** | 6 | Multi-client broadcasting functionality | -| **Total** | **21** | | - -## Detailed Test Descriptions - -### Example 1: Echo Server Tests (4 tests) -Tests the basic WebSocket echo functionality where messages are echoed back to the sender. - -1. **Example1_EchoServer_Should_Echo_Text_Messages** - - ✅ Single text message echo - - ✅ Verifies message type and content - -2. **Example1_EchoServer_Should_Echo_Multiple_Messages** - - ✅ Multiple sequential messages - - ✅ Each message echoed correctly - -3. **Example1_EchoServer_Should_Echo_Binary_Messages** - - ✅ Binary data echo - - ✅ Byte array verification - -4. **Example1_EchoServer_Should_Handle_Empty_Messages** - - ✅ Edge case: empty messages - - ✅ Graceful handling - -### Example 2: Custom Message Handler Tests (8 tests) -Tests custom message processing with various commands. - -1. **Example2_CustomHandler_Should_Handle_Help_Command** - - ✅ `/help` → Returns list of available commands - -2. **Example2_CustomHandler_Should_Handle_Time_Command** - - ✅ `/time` → Returns current server time - -3. **Example2_CustomHandler_Should_Handle_Echo_Command** - - ✅ `/echo ` → Echoes the text - -4. **Example2_CustomHandler_Should_Handle_Upper_Command** - - ✅ `/upper ` → Converts to uppercase - -5. **Example2_CustomHandler_Should_Handle_Reverse_Command** - - ✅ `/reverse ` → Reverses the text - -6. **Example2_CustomHandler_Should_Handle_Quit_Command** - - ✅ `/quit` → Sends goodbye and closes connection - -7. **Example2_CustomHandler_Should_Handle_Unknown_Command** - - ✅ Invalid commands → Error message - -8. **Example2_CustomHandler_Should_Handle_Multiple_Commands_In_Sequence** - - ✅ All commands in sequence - - ✅ State consistency verification - -### Example 3: SendJsonAsync Tests (3 tests) -Tests JSON serialization and the `SendJsonAsync` functionality. - -1. **Example3_JsonEndpoint_Should_Send_Json_Response** - - ✅ Basic JSON response - - ✅ Structure: `{ timestamp, message, length, type }` - - ✅ Proper serialization - -2. **Example3_JsonEndpoint_Should_Handle_Multiple_Json_Messages** - - ✅ Sequential JSON messages - - ✅ Each properly serialized - -3. **Example3_JsonEndpoint_Should_Serialize_Complex_Objects** - - ✅ Nested objects - - ✅ Arrays within objects - - ✅ Complex structures - -### Broadcast Tests (6 tests) -Tests the broadcast functionality with multiple simultaneous clients. - -1. **Broadcast_Should_Send_Message_To_All_Connected_Clients** - - ✅ 3 connected clients - - ✅ All receive same broadcast - - ✅ Timestamp in messages - -2. **Broadcast_Should_Only_Send_To_Open_Connections** - - ✅ Closed connections skipped - - ✅ Only active clients receive - -3. **BroadcastJson_Should_Send_Json_To_All_Clients** - - ✅ JSON broadcasting - - ✅ Multiple clients receive - - ✅ Sender identification - -4. **Broadcast_Should_Handle_Multiple_Sequential_Messages** - - ✅ Sequential broadcasts - - ✅ Message ordering - - ✅ All clients receive all messages - -5. **Broadcast_Should_Work_With_Many_Clients** - - ✅ 5 simultaneous clients - - ✅ Scalability test - - ✅ Parallel message reception - -6. **Broadcast Integration** - - ✅ Complete flow testing - -## Key Testing Features - -### 🔌 Real WebSocket Connections -- Uses `System.Net.WebSockets.ClientWebSocket` -- Actual network communication -- Protocol compliance verification - -### 📤 SendJsonAsync Coverage -```csharp -await ctx.SendJsonAsync(new { - timestamp = DateTime.UtcNow, - message = msg.Text, - data = complexObject -}); -``` -- Simple objects -- Complex nested structures -- Arrays and collections - -### 📡 Broadcast Coverage -```csharp -await ctx.BroadcastTextAsync("Message to all"); -await ctx.BroadcastJsonAsync(jsonObject); -``` -- Multiple simultaneous clients -- Text and JSON broadcasts -- Connection state handling -- Scalability testing - -### ✨ Best Practices -- ✅ Test isolation (each test has own server) -- ✅ Random ports (Port = 0) -- ✅ Proper cleanup (`IDisposable`) -- ✅ FluentAssertions for readability -- ✅ Async/await throughout -- ✅ No test interdependencies - -## Running the Tests - -### All WebSocket Tests -```bash -dotnet test --filter "FullyQualifiedName~WebSocketIntegrationTests" -``` - -### By Example -```bash -# Example 1: Echo -dotnet test --filter "FullyQualifiedName~Example1" - -# Example 2: Custom Handlers -dotnet test --filter "FullyQualifiedName~Example2" - -# Example 3: JSON -dotnet test --filter "FullyQualifiedName~Example3" -``` - -### By Feature -```bash -# Broadcast tests -dotnet test --filter "FullyQualifiedName~Broadcast" - -# JSON tests -dotnet test --filter "FullyQualifiedName~Json" -``` - -### Run Specific Test -```bash -dotnet test --filter "FullyQualifiedName~Example1_EchoServer_Should_Echo_Text_Messages" -``` - -## Dependencies - -| Package | Purpose | -|---------|---------| -| `System.Net.WebSockets.ClientWebSocket` | Real WebSocket client | -| `WireMock.Server` | WireMock server instance | -| `FluentAssertions` | Readable assertions | -| `xUnit` | Test framework | -| `Newtonsoft.Json` | JSON parsing in assertions | - -All dependencies are included in `WireMock.Net.Tests.csproj`. - -## Implementation Details - -### JSON Testing Pattern -```csharp -// Send text message -await client.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None); - -// Receive JSON response -var result = await client.ReceiveAsync(buffer, CancellationToken.None); -var json = JObject.Parse(received); - -// Assert structure -json["message"].ToString().Should().Be(expectedMessage); -json["timestamp"].Should().NotBeNull(); -``` - -### Broadcast Testing Pattern -```csharp -// Connect multiple clients -var clients = new[] { new ClientWebSocket(), new ClientWebSocket() }; -foreach (var c in clients) - await c.ConnectAsync(uri, CancellationToken.None); - -// Send from one client -await clients[0].SendAsync(message, ...); - -// All clients receive -foreach (var c in clients) { - var result = await c.ReceiveAsync(buffer, ...); - // Assert all received the same message -} -``` - -## Test Timing Notes -- Connection registration delays: 100-200ms -- Ensures all clients are registered before broadcasting -- Prevents race conditions in multi-client tests -- Production code does not require delays - -## Coverage Metrics -- ✅ Text messages -- ✅ Binary messages -- ✅ Empty messages -- ✅ JSON serialization (simple & complex) -- ✅ Multiple sequential messages -- ✅ Multiple simultaneous clients -- ✅ Connection state transitions -- ✅ Broadcast to all clients -- ✅ Closed connection handling -- ✅ Error scenarios diff --git a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs index 5e2ba2bf..a5a27580 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.WithBody.cs @@ -215,9 +215,8 @@ public partial class WireMockServerTests // Act var content = """{"id":"ec475f56d4694b48bc737500ba575b35-1"}"""; - var response = await new HttpClient() - .PostAsync($"{server.Url}/system-text-json", new StringContent(content, Encoding.UTF8, "application/json")) - .ConfigureAwait(false); + using var httpClient = new HttpClient(); + var response = await httpClient.PostAsync($"{server.Url}/system-text-json", new StringContent(content, Encoding.UTF8, "application/json")); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); diff --git a/test/WireMock.Net.Tests/WireMockServerTests.cs b/test/WireMock.Net.Tests/WireMockServerTests.cs index 77231ff2..6be8bfd2 100644 --- a/test/WireMock.Net.Tests/WireMockServerTests.cs +++ b/test/WireMock.Net.Tests/WireMockServerTests.cs @@ -220,7 +220,7 @@ public partial class WireMockServerTests foreach (var address in IPv4) { // Act - var response = await new HttpClient().GetStringAsync("http://" + address + ":" + server.Ports[0] + "/foo").ConfigureAwait(false); + var response = await new HttpClient().GetStringAsync("http://" + address + ":" + server.Ports[0] + "/foo"); // Assert response.Should().Be("x"); @@ -245,7 +245,7 @@ public partial class WireMockServerTests foreach (var address in IPv6) { // Act - var response = await new HttpClient().GetStringAsync("http://[" + address + "]:" + server.Ports[0] + "/foo").ConfigureAwait(false); + var response = await new HttpClient().GetStringAsync("http://[" + address + "]:" + server.Ports[0] + "/foo"); // Assert response.Should().Be("x"); @@ -358,7 +358,7 @@ public partial class WireMockServerTests async Task ExecuteTimedRequestAsync() { watch.Reset(); - await httClient.GetStringAsync("http://localhost:" + server.Ports[0] + "/foo").ConfigureAwait(false); + await httClient.GetStringAsync("http://localhost:" + server.Ports[0] + "/foo"); return watch.ElapsedMilliseconds; }