diff --git a/CHANGELOG.md b/CHANGELOG.md index ec8a8515..6c4afbe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 1.20.0 (24 December 2025) +- [#1399](https://github.com/wiremock/WireMock.Net/pull/1399) - Upgrade RamlToOpenApiConverter and YamlDotNet [feature] contributed by [StefH](https://github.com/StefH) +- [#1400](https://github.com/wiremock/WireMock.Net/pull/1400) - Add WireMock.Net.NUnit project [feature] contributed by [StefH](https://github.com/StefH) +- [#1405](https://github.com/wiremock/WireMock.Net/pull/1405) - Fix Testcontainers AddProtoDefinition [bug] contributed by [StefH](https://github.com/StefH) +- [#1398](https://github.com/wiremock/WireMock.Net/issues/1398) - Upgrade YamlDotNet dependency [feature] +- [#1404](https://github.com/wiremock/WireMock.Net/issues/1404) - An exception occurs when adding multiple proto definitions in the TestContainer. [bug] + # 1.19.0 (12 December 2025) - [#1391](https://github.com/wiremock/WireMock.Net/pull/1391) - Update WireMockContainerBuilder (WithImage and WithCustomImage) [feature] contributed by [StefH](https://github.com/StefH) - [#1392](https://github.com/wiremock/WireMock.Net/pull/1392) - WireMockContainerBuilder: allow all docker images named wiremock [feature] contributed by [StefH](https://github.com/StefH) diff --git a/Directory.Build.props b/Directory.Build.props index 6d59e440..daf48040 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -4,7 +4,7 @@ - 1.19.0 + 1.20.0 WireMock.Net-Logo.png https://github.com/wiremock/WireMock.Net Apache-2.0 diff --git a/Generate-ReleaseNotes.cmd b/Generate-ReleaseNotes.cmd index dee29e49..5a5e664b 100644 --- a/Generate-ReleaseNotes.cmd +++ b/Generate-ReleaseNotes.cmd @@ -1,6 +1,6 @@ rem https://github.com/StefH/GitHubReleaseNotes -SET version=1.19.0 +SET version=1.20.0 GitHubReleaseNotes --output CHANGELOG.md --skip-empty-releases --exclude-labels wontfix test question invalid doc duplicate example environment --version %version% --token %GH_TOKEN% diff --git a/PackageReleaseNotes.txt b/PackageReleaseNotes.txt index 1cc929a6..d0d374b0 100644 --- a/PackageReleaseNotes.txt +++ b/PackageReleaseNotes.txt @@ -1,6 +1,8 @@ -# 1.19.0 (12 December 2025) -- #1391 Update WireMockContainerBuilder (WithImage and WithCustomImage) [feature] -- #1392 WireMockContainerBuilder: allow all docker images named wiremock [feature] -- #1390 Unable to build WireMockContainerBuilder with custom image [feature] +# 1.20.0 (24 December 2025) +- #1399 Upgrade RamlToOpenApiConverter and YamlDotNet [feature] +- #1400 Add WireMock.Net.NUnit project [feature] +- #1405 Fix Testcontainers AddProtoDefinition [bug] +- #1398 Upgrade YamlDotNet dependency [feature] +- #1404 An exception occurs when adding multiple proto definitions in the TestContainer. [bug] The full release notes can be found here: https://github.com/wiremock/WireMock.Net/blob/master/CHANGELOG.md \ No newline at end of file diff --git a/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs b/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs index bbba63f3..7f79d896 100644 --- a/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs +++ b/src/WireMock.Net.ProtoBuf/Matchers/ProtoBufMatcher.cs @@ -96,14 +96,14 @@ public class ProtoBufMatcher : IProtoBufMatcher } var protoDefinitions = ProtoDefinition().Texts; - + var resolver = new WireMockProtoFileResolver(protoDefinitions); var request = new ConvertToObjectRequest(protoDefinitions[0], MessageType, input) .WithProtoFileResolver(resolver); try { - return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken).ConfigureAwait(false); + return await ProtoBufToJsonConverter.ConvertAsync(request, cancellationToken); } catch { diff --git a/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj b/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj index 47710e3d..52d1e3b0 100644 --- a/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj +++ b/src/WireMock.Net.ProtoBuf/WireMock.Net.ProtoBuf.csproj @@ -27,7 +27,7 @@ - + diff --git a/src/WireMock.Net.Testcontainers/Extensions/HttpWaitStrategyExtensions.cs b/src/WireMock.Net.Testcontainers/Extensions/HttpWaitStrategyExtensions.cs new file mode 100644 index 00000000..33aab29a --- /dev/null +++ b/src/WireMock.Net.Testcontainers/Extensions/HttpWaitStrategyExtensions.cs @@ -0,0 +1,18 @@ +// Copyright © WireMock.Net + +using WireMock.Net.Testcontainers; + +namespace DotNet.Testcontainers.Configurations; + +internal static class HttpWaitStrategyExtensions +{ + internal static HttpWaitStrategy WithBasicAuthentication(this HttpWaitStrategy strategy, WireMockConfiguration configuration) + { + if (configuration.HasBasicAuthentication) + { + return strategy.WithBasicAuthentication(configuration.Username, configuration.Password); + } + + return strategy; + } +} \ No newline at end of file diff --git a/src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs b/src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs new file mode 100644 index 00000000..caab4c75 --- /dev/null +++ b/src/WireMock.Net.Testcontainers/Utils/CombineUtils.cs @@ -0,0 +1,20 @@ +// Copyright © WireMock.Net + +using System.Collections.Generic; +using System.Linq; + +namespace WireMock.Net.Testcontainers.Utils; + +internal static class CombineUtils +{ + internal static List Combine(List oldValue, List newValue) + { + return oldValue.Union(newValue).ToList(); + } + + internal static Dictionary Combine(Dictionary oldValue, Dictionary newValue) + where TKey : notnull + { + return oldValue.Union(newValue).ToDictionary(item => item.Key, item => item.Value); + } +} \ No newline at end of file diff --git a/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs b/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs index f7260670..dded4876 100644 --- a/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs +++ b/src/WireMock.Net.Testcontainers/WireMockConfiguration.cs @@ -1,12 +1,12 @@ // Copyright © WireMock.Net using System.Collections.Generic; -using System.Linq; using Docker.DotNet.Models; using DotNet.Testcontainers.Builders; using DotNet.Testcontainers.Configurations; using JetBrains.Annotations; using Stef.Validation; +using WireMock.Net.Testcontainers.Utils; namespace WireMock.Net.Testcontainers; @@ -77,8 +77,8 @@ public sealed class WireMockConfiguration : ContainerConfiguration StaticMappingsPath = BuildConfiguration.Combine(oldValue.StaticMappingsPath, newValue.StaticMappingsPath); WatchStaticMappings = BuildConfiguration.Combine(oldValue.WatchStaticMappings, newValue.WatchStaticMappings); WatchStaticMappingsInSubdirectories = BuildConfiguration.Combine(oldValue.WatchStaticMappingsInSubdirectories, newValue.WatchStaticMappingsInSubdirectories); - AdditionalUrls = Combine(oldValue.AdditionalUrls, newValue.AdditionalUrls); - ProtoDefinitions = Combine(oldValue.ProtoDefinitions, newValue.ProtoDefinitions); + AdditionalUrls = CombineUtils.Combine(oldValue.AdditionalUrls, newValue.AdditionalUrls); + ProtoDefinitions = CombineUtils.Combine(oldValue.ProtoDefinitions, newValue.ProtoDefinitions); } /// @@ -130,16 +130,4 @@ public sealed class WireMockConfiguration : ContainerConfiguration return this; } - - private static List Combine(List oldValue, List newValue) - { - return oldValue.Concat(newValue).ToList(); - } - - private static Dictionary Combine(Dictionary oldValue, Dictionary newValue) - { - return newValue - .Concat(oldValue.Where(item => !newValue.Keys.Contains(item.Key))) - .ToDictionary(item => item.Key, item => item.Value); - } } \ No newline at end of file diff --git a/src/WireMock.Net.Testcontainers/WireMockContainer.cs b/src/WireMock.Net.Testcontainers/WireMockContainer.cs index 002a0469..f34cc1b3 100644 --- a/src/WireMock.Net.Testcontainers/WireMockContainer.cs +++ b/src/WireMock.Net.Testcontainers/WireMockContainer.cs @@ -228,6 +228,7 @@ public sealed class WireMockContainer : DockerContainer foreach (var kvp in _configuration.ProtoDefinitions) { Logger.LogInformation("Adding ProtoDefinition {Id}", kvp.Key); + foreach (var protoDefinition in kvp.Value) { try diff --git a/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs b/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs index 71c0ad69..37241598 100644 --- a/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs +++ b/src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs @@ -2,6 +2,8 @@ using System; using System.Linq; +using System.Net; +using System.Net.Http; using System.Runtime.InteropServices; using Docker.DotNet.Models; using DotNet.Testcontainers.Builders; @@ -250,6 +252,23 @@ public sealed class WireMockContainerBuilder : ContainerBuilder httpWaitStrategy + .ForPort(WireMockContainer.ContainerPort) + .WithMethod(HttpMethod.Get) + .WithBasicAuthentication(DockerResourceConfiguration) + .ForPath("/__admin/health") + .ForStatusCode(HttpStatusCode.OK) + .ForResponseMessageMatching(async httpResponseMessage => + { + var content = await httpResponseMessage.Content.ReadAsStringAsync(); + return content?.Contains("Healthy") == true; + }) + ) + ); + return new WireMockContainer(builder.DockerResourceConfiguration); } @@ -262,7 +281,9 @@ public sealed class WireMockContainerBuilder : ContainerBuilder waitStrategy.WithTimeout(TimeSpan.FromSeconds(30))) + ); } /// diff --git a/test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs b/test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs new file mode 100644 index 00000000..95f55774 --- /dev/null +++ b/test/WireMock.Net.Tests/Testcontainers/CombineUtilsTests.cs @@ -0,0 +1,161 @@ +// Copyright © WireMock.Net + +using System.Collections.Generic; +using FluentAssertions; +using WireMock.Net.Testcontainers.Utils; +using Xunit; + +namespace WireMock.Net.Tests.Testcontainers; + +public class CombineUtilsTests +{ + [Fact] + public void Combine_Lists_WithBothEmpty_ReturnsEmptyList() + { + // Arrange + var oldValue = new List(); + var newValue = new List(); + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().BeEmpty(); + } + + [Fact] + public void Combine_Lists_WithEmptyOldValue_ReturnsNewValue() + { + // Arrange + var oldValue = new List(); + var newValue = new List { "item1", "item2" }; + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().Equal("item1", "item2"); + } + + [Fact] + public void Combine_Lists_WithEmptyNewValue_ReturnsOldValue() + { + // Arrange + var oldValue = new List { "item1", "item2" }; + var newValue = new List(); + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().Equal("item1", "item2"); + } + + [Fact] + public void Combine_Lists_WithBothPopulated_ReturnsConcatenatedList() + { + // Arrange + var oldValue = new List { 1, 2, 3 }; + var newValue = new List { 4, 5, 6 }; + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().Equal(1, 2, 3, 4, 5, 6); + } + + [Fact] + public void Combine_Lists_WithDuplicates_RemovesDuplicates() + { + // Arrange + var oldValue = new List { "a", "b", "c" }; + var newValue = new List { "b", "c", "d" }; + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().Equal("a", "b", "c", "d"); + } + + [Fact] + public void Combine_Dictionaries_WithBothEmpty_ReturnsEmptyDictionary() + { + // Arrange + var oldValue = new Dictionary(); + var newValue = new Dictionary(); + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().BeEmpty(); + } + + [Fact] + public void Combine_Dictionaries_WithEmptyOldValue_ReturnsNewValue() + { + // Arrange + var oldValue = new Dictionary(); + var newValue = new Dictionary + { + { "key1", 1 }, + { "key2", 2 } + }; + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().HaveCount(2); + result["key1"].Should().Be(1); + result["key2"].Should().Be(2); + } + + [Fact] + public void Combine_Dictionaries_WithEmptyNewValue_ReturnsOldValue() + { + // Arrange + var oldValue = new Dictionary + { + { "key1", 1 }, + { "key2", 2 } + }; + var newValue = new Dictionary(); + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().HaveCount(2); + result["key1"].Should().Be(1); + result["key2"].Should().Be(2); + } + + [Fact] + public void Combine_Dictionaries_WithNoOverlappingKeys_ReturnsMergedDictionary() + { + // Arrange + var oldValue = new Dictionary + { + { "key1", "value1" }, + { "key2", "value2" } + }; + var newValue = new Dictionary + { + { "key3", "value3" }, + { "key4", "value4" } + }; + + // Act + var result = CombineUtils.Combine(oldValue, newValue); + + // Assert + result.Should().HaveCount(4); + result["key1"].Should().Be("value1"); + result["key2"].Should().Be("value2"); + result["key3"].Should().Be("value3"); + result["key4"].Should().Be("value4"); + } +} \ No newline at end of file diff --git a/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs b/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs index c05125e0..1c578f78 100644 --- a/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs +++ b/test/WireMock.Net.Tests/Testcontainers/TestcontainersTestsGrpc.cs @@ -19,7 +19,7 @@ using Xunit.Abstractions; namespace WireMock.Net.Tests.Testcontainers; -[Collection("Grpc")] +//[Collection("Grpc")] public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper) { [Fact] @@ -37,7 +37,7 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper) .WithCommand("--Urls", $"http://*:80 grpc://*:{port}") .WithPortBinding(port, true) .Build(); - + try { await wireMockContainer.StartAsync(); @@ -171,6 +171,35 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper) await StopAsync(wireMockContainer); } + private async Task When_GrpcClient_Calls_SayHelloAsync(WireMockContainer wireMockContainer) + { + var address = wireMockContainer.GetPublicUrls().First(x => x.Key != 80).Value; + var channel = GrpcChannel.ForAddress(address); + + var client = new Greeter.GreeterClient(channel); + + try + { + return await client.SayHelloAsync(new HelloRequest { Name = "stef" }); + } + catch (Exception ex) + { + testOutputHelper.WriteLine("Exception during GrpcClient Call to {0}. Exception = {1}.", address, ex); + + testOutputHelper.WriteLine("Dumping WireMock.Net logs:"); + var (stdOut, stdError) = await wireMockContainer.GetLogsAsync(DateTime.MinValue); + testOutputHelper.WriteLine("Out :\r\n{0}", stdOut); + testOutputHelper.WriteLine("Error:\r\n{0}", stdError); + + testOutputHelper.WriteLine("Dumping WireMock.Net mappings:"); + using var httpClient = wireMockContainer.CreateClient(); + using var response = await httpClient.GetAsync("/__admin/mappings"); + var mappings = await response.Content.ReadAsStringAsync(); + testOutputHelper.WriteLine("Mappings:\r\n{0}", mappings); + throw; + } + } + private async Task StopAsync(WireMockContainer wireMockContainer) { try @@ -240,18 +269,6 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper) var result = await httpClient.PostAsync("/__admin/mappings", new StringContent(mappingsJson, Encoding.UTF8, WireMockConstants.ContentTypeJson)); result.EnsureSuccessStatusCode(); - - await Task.Delay(5000); - } - - private static async Task When_GrpcClient_Calls_SayHelloAsync(WireMockContainer wireMockContainer) - { - var address = wireMockContainer.GetPublicUrls().First(x => x.Key != 80).Value; - var channel = GrpcChannel.ForAddress(address); - - var client = new Greeter.GreeterClient(channel); - - return await client.SayHelloAsync(new HelloRequest { Name = "stef" }); } private static void Then_ReplyMessage_Should_BeCorrect(HelloReply reply) diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj index de6ca818..b53868b9 100644 --- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj +++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj @@ -30,6 +30,10 @@ + + + +