mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-11 13:00:33 +01:00
Fix readyness-check for Testcontainers (#1408)
* Add XUnit Logging to TestcontainersTests * .
This commit is contained in:
@@ -24,28 +24,21 @@ namespace WireMock.Net.Testcontainers;
|
||||
/// <summary>
|
||||
/// A container for running WireMock in a docker environment.
|
||||
/// </summary>
|
||||
public sealed class WireMockContainer : DockerContainer
|
||||
/// <remarks>
|
||||
/// Initializes a new instance of the <see cref="WireMockContainer" /> class.
|
||||
/// </remarks>
|
||||
/// <param name="configuration">The container configuration.</param>
|
||||
public sealed class WireMockContainer(WireMockConfiguration configuration) : DockerContainer(configuration)
|
||||
{
|
||||
private const int EnhancedFileSystemWatcherTimeoutMs = 2000;
|
||||
internal const int ContainerPort = 80;
|
||||
|
||||
private readonly WireMockConfiguration _configuration;
|
||||
private readonly WireMockConfiguration _configuration = Guard.NotNull(configuration);
|
||||
|
||||
private IWireMockAdminApi? _adminApi;
|
||||
private EnhancedFileSystemWatcher? _enhancedFileSystemWatcher;
|
||||
private IDictionary<int, Uri>? _publicUris;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockContainer" /> class.
|
||||
/// </summary>
|
||||
/// <param name="configuration">The container configuration.</param>
|
||||
public WireMockContainer(WireMockConfiguration configuration) : base(configuration)
|
||||
{
|
||||
_configuration = Guard.NotNull(configuration);
|
||||
|
||||
Started += async (sender, eventArgs) => await WireMockContainerStartedAsync(sender, eventArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the public Url.
|
||||
/// </summary>
|
||||
@@ -157,14 +150,28 @@ public sealed class WireMockContainer : DockerContainer
|
||||
try
|
||||
{
|
||||
var result = await _adminApi.ReloadStaticMappingsAsync(cancellationToken);
|
||||
Logger.LogInformation("ReloadStaticMappings result: {Result}", result);
|
||||
Logger.LogInformation("WireMock.Net -> ReloadStaticMappings result: {Result}", result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Error calling /__admin/mappings/reloadStaticMappings");
|
||||
Logger.LogWarning(ex, "WireMock.Net -> Error calling /__admin/mappings/reloadStaticMappings");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs additional actions after the container is ready.
|
||||
/// </summary>
|
||||
public Task CallAdditionalActionsAfterReadyAsync()
|
||||
{
|
||||
Logger.LogInformation("WireMock.Net -> Calling additional actions.");
|
||||
|
||||
_adminApi = CreateWireMockAdminClient();
|
||||
|
||||
RegisterEnhancedFileSystemWatcher();
|
||||
|
||||
return AddProtoDefinitionsAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override ValueTask DisposeAsyncCore()
|
||||
{
|
||||
@@ -197,15 +204,6 @@ public sealed class WireMockContainer : DockerContainer
|
||||
}
|
||||
}
|
||||
|
||||
private async Task WireMockContainerStartedAsync(object sender, EventArgs e)
|
||||
{
|
||||
_adminApi = CreateWireMockAdminClient();
|
||||
|
||||
RegisterEnhancedFileSystemWatcher();
|
||||
|
||||
await CallAdditionalActionsAfterStartedAsync();
|
||||
}
|
||||
|
||||
private void RegisterEnhancedFileSystemWatcher()
|
||||
{
|
||||
if (!_configuration.WatchStaticMappings || string.IsNullOrEmpty(_configuration.StaticMappingsPath))
|
||||
@@ -223,22 +221,22 @@ public sealed class WireMockContainer : DockerContainer
|
||||
_enhancedFileSystemWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
private async Task CallAdditionalActionsAfterStartedAsync()
|
||||
private async Task AddProtoDefinitionsAsync()
|
||||
{
|
||||
foreach (var kvp in _configuration.ProtoDefinitions)
|
||||
{
|
||||
Logger.LogInformation("Adding ProtoDefinition {Id}", kvp.Key);
|
||||
Logger.LogInformation("WireMock.Net -> Adding ProtoDefinition '{Id}'", kvp.Key);
|
||||
|
||||
foreach (var protoDefinition in kvp.Value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _adminApi!.AddProtoDefinitionAsync(kvp.Key, protoDefinition);
|
||||
Logger.LogInformation("AddProtoDefinition '{Id}' result: {Result}", kvp.Key, result);
|
||||
Logger.LogInformation("WireMock.Net -> AddProtoDefinition '{Id}' result: {Result}", kvp.Key, result);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Error adding ProtoDefinition '{Id}'.", kvp.Key);
|
||||
Logger.LogWarning(ex, "WireMock.Net -> Error adding ProtoDefinition '{Id}'.", kvp.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,17 +253,17 @@ public sealed class WireMockContainer : DockerContainer
|
||||
try
|
||||
{
|
||||
await ReloadStaticMappingsAsync(args.FullPath);
|
||||
Logger.LogInformation("ReloadStaticMappings triggered from file change: '{FullPath}'.", args.FullPath);
|
||||
Logger.LogInformation("WireMock.Net -> ReloadStaticMappings triggered from file change: '{FullPath}'.", args.FullPath);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.LogWarning(ex, "Error reloading static mappings from '{FullPath}'.", args.FullPath);
|
||||
Logger.LogWarning(ex, "WireMock.Net -> Error reloading static mappings from '{FullPath}'.", args.FullPath);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ReloadStaticMappingsAsync(string path, CancellationToken cancellationToken = default)
|
||||
{
|
||||
Logger.LogInformation("MappingFile created, changed or deleted: '{Path}'. Triggering ReloadStaticMappings.", path);
|
||||
Logger.LogInformation("WireMock.Net -> MappingFile created, changed or deleted: '{Path}'. Triggering ReloadStaticMappings.", path);
|
||||
await ReloadStaticMappingsAsync(cancellationToken);
|
||||
}
|
||||
|
||||
|
||||
@@ -253,8 +253,9 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
||||
builder.Validate();
|
||||
|
||||
var waitForContainerOS = _imageOS == OSPlatform.Windows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
||||
builder
|
||||
builder = builder
|
||||
.WithWaitStrategy(waitForContainerOS
|
||||
.UntilMessageIsLogged("WireMock.Net server running", waitStrategy => waitStrategy.WithTimeout(TimeSpan.FromSeconds(30)))
|
||||
.UntilHttpRequestIsSucceeded(httpWaitStrategy => httpWaitStrategy
|
||||
.ForPort(WireMockContainer.ContainerPort)
|
||||
.WithMethod(HttpMethod.Get)
|
||||
@@ -267,6 +268,7 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
||||
return content?.Contains("Healthy") == true;
|
||||
})
|
||||
)
|
||||
.AddCustomWaitStrategy(new WireMockWaitStrategy())
|
||||
);
|
||||
|
||||
return new WireMockContainer(builder.DockerResourceConfiguration);
|
||||
@@ -277,13 +279,9 @@ public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContaine
|
||||
{
|
||||
var builder = base.Init();
|
||||
|
||||
var waitForContainerOS = _imageOS == OSPlatform.Windows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
||||
return builder
|
||||
.WithPortBinding(WireMockContainer.ContainerPort, true)
|
||||
.WithCommand($"--WireMockLogger {DefaultLogger}")
|
||||
.WithWaitStrategy(waitForContainerOS
|
||||
.UntilMessageIsLogged("WireMock.Net server running", waitStrategy => waitStrategy.WithTimeout(TimeSpan.FromSeconds(30)))
|
||||
);
|
||||
.WithCommand($"--WireMockLogger {DefaultLogger}");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
||||
23
src/WireMock.Net.Testcontainers/WireMockWaitStrategy.cs
Normal file
23
src/WireMock.Net.Testcontainers/WireMockWaitStrategy.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using DotNet.Testcontainers.Configurations;
|
||||
using DotNet.Testcontainers.Containers;
|
||||
|
||||
namespace WireMock.Net.Testcontainers;
|
||||
|
||||
internal class WireMockWaitStrategy : IWaitUntil
|
||||
{
|
||||
public async Task<bool> UntilAsync(IContainer container)
|
||||
{
|
||||
if (container is not WireMockContainer wireMockContainer)
|
||||
{
|
||||
throw new InvalidOperationException("The passed container is not a WireMockContainer.");
|
||||
|
||||
}
|
||||
|
||||
await wireMockContainer.CallAdditionalActionsAfterReadyAsync();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,8 @@ using System.Threading.Tasks;
|
||||
using DotNet.Testcontainers.Builders;
|
||||
using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Meziantou.Extensions.Logging.Xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WireMock.Net.Testcontainers;
|
||||
using WireMock.Net.Testcontainers.Utils;
|
||||
using WireMock.Net.Tests.Facts;
|
||||
@@ -17,6 +19,12 @@ namespace WireMock.Net.Tests.Testcontainers;
|
||||
|
||||
public class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly ILogger _logger = new XUnitLogger(testOutputHelper, new LoggerExternalScopeProvider(), nameof(TestcontainersTests), new XUnitLoggerOptions
|
||||
{
|
||||
IncludeCategory = true,
|
||||
TimestampFormat = "yyy-MM-dd HH:mm:ss.fff"
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockContainer_Build_And_StartAsync_and_StopAsync()
|
||||
{
|
||||
@@ -24,6 +32,7 @@ public class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
||||
.WithAutoRemove(true)
|
||||
.WithCleanUp(true)
|
||||
@@ -43,6 +52,7 @@ public class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
||||
.Build();
|
||||
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.WithNetwork(dummyNetwork)
|
||||
.WithWatchStaticMappings(true)
|
||||
.Build();
|
||||
@@ -58,6 +68,7 @@ public class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||
var wireMockContainerBuilder = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword);
|
||||
|
||||
var imageOS = await TestcontainersUtils.GetImageOSAsync.Value;
|
||||
@@ -83,6 +94,7 @@ public class TestcontainersTests(ITestOutputHelper testOutputHelper)
|
||||
var adminUsername = $"username_{Guid.NewGuid()}";
|
||||
var adminPassword = $"password_{Guid.NewGuid()}";
|
||||
var wireMockContainerBuilder = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword);
|
||||
|
||||
var imageOS = await TestcontainersUtils.GetImageOSAsync.Value;
|
||||
|
||||
@@ -11,6 +11,8 @@ using FluentAssertions;
|
||||
using FluentAssertions.Execution;
|
||||
using Greet;
|
||||
using Grpc.Net.Client;
|
||||
using Meziantou.Extensions.Logging.Xunit;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using WireMock.Constants;
|
||||
using WireMock.Net.Testcontainers;
|
||||
using WireMock.Util;
|
||||
@@ -22,6 +24,12 @@ namespace WireMock.Net.Tests.Testcontainers;
|
||||
[Collection("Grpc")]
|
||||
public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||
{
|
||||
private readonly ILogger _logger = new XUnitLogger(testOutputHelper, new LoggerExternalScopeProvider(), nameof(TestcontainersTestsGrpc), new XUnitLoggerOptions
|
||||
{
|
||||
IncludeCategory = true,
|
||||
TimestampFormat = "yyy-MM-dd HH:mm:ss.fff"
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public async Task WireMockContainer_Build_Grpc_TestPortsAndUrls1()
|
||||
{
|
||||
@@ -32,6 +40,7 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||
|
||||
// Act
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
||||
.WithCommand("--UseHttp2")
|
||||
.WithCommand("--Urls", $"http://*:80 grpc://*:{port}")
|
||||
@@ -88,6 +97,7 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||
|
||||
// Act
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.WithAdminUserNameAndPassword(adminUsername, adminPassword)
|
||||
.AddUrl($"http://*:{ports[0]}")
|
||||
.AddUrl($"grpc://*:{ports[1]}")
|
||||
@@ -222,10 +232,11 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<WireMockContainer> Given_WireMockContainerIsStartedForHttpAndGrpcAsync()
|
||||
private async Task<WireMockContainer> Given_WireMockContainerIsStartedForHttpAndGrpcAsync()
|
||||
{
|
||||
var port = PortUtils.FindFreeTcpPort();
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.AddUrl($"grpc://*:{port}")
|
||||
.Build();
|
||||
|
||||
@@ -234,10 +245,11 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||
return wireMockContainer;
|
||||
}
|
||||
|
||||
private static async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelIsStartedForHttpAndGrpcAsync()
|
||||
private async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelIsStartedForHttpAndGrpcAsync()
|
||||
{
|
||||
var port = PortUtils.FindFreeTcpPort();
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.AddUrl($"grpc://*:{port}")
|
||||
.AddProtoDefinition("my-greeter", ReadFile("greet.proto"))
|
||||
.Build();
|
||||
@@ -247,10 +259,11 @@ public class TestcontainersTestsGrpc(ITestOutputHelper testOutputHelper)
|
||||
return wireMockContainer;
|
||||
}
|
||||
|
||||
private static async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelWithWatchStaticMappingsIsStartedForHttpAndGrpcAsync()
|
||||
private async Task<WireMockContainer> Given_WireMockContainerWithProtoDefinitionAtServerLevelWithWatchStaticMappingsIsStartedForHttpAndGrpcAsync()
|
||||
{
|
||||
var port = PortUtils.FindFreeTcpPort();
|
||||
var wireMockContainer = new WireMockContainerBuilder()
|
||||
.WithLogger(_logger)
|
||||
.AddUrl($"grpc://*:{port}")
|
||||
.AddProtoDefinition("my-greeter", ReadFile("greet.proto"))
|
||||
.WithMappings(Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings"))
|
||||
|
||||
@@ -121,6 +121,7 @@
|
||||
|
||||
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'net7.0' or '$(TargetFramework)' == 'net8.0'">
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj" />
|
||||
<PackageReference Include="Meziantou.Extensions.Logging.Xunit" Version="1.0.21" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user