mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-02-20 07:48:00 +01:00
310 lines
12 KiB
C#
310 lines
12 KiB
C#
// Copyright © WireMock.Net
|
|
|
|
using System;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Net.Http;
|
|
using System.Runtime.InteropServices;
|
|
using Docker.DotNet.Models;
|
|
using DotNet.Testcontainers.Builders;
|
|
using DotNet.Testcontainers.Configurations;
|
|
using JetBrains.Annotations;
|
|
using Stef.Validation;
|
|
using WireMock.Net.Testcontainers.Utils;
|
|
using WireMock.Util;
|
|
|
|
namespace WireMock.Net.Testcontainers;
|
|
|
|
/// <summary>
|
|
/// A specific fluent Docker container builder for WireMock.Net
|
|
/// </summary>
|
|
public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContainerBuilder, WireMockContainer, WireMockConfiguration>
|
|
{
|
|
private const string DefaultLogger = "WireMockConsoleLogger";
|
|
private OSPlatform? _imageOS;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="ContainerBuilder" /> class.
|
|
/// </summary>
|
|
public WireMockContainerBuilder() : this(new WireMockConfiguration())
|
|
{
|
|
DockerResourceConfiguration = Init().DockerResourceConfiguration;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically use the correct image for WireMock.
|
|
/// For Linux this is "sheyenrath/wiremock.net-alpine:latest"
|
|
/// For Windows this is "sheyenrath/wiremock.net-windows:latest"
|
|
/// </summary>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithImage()
|
|
{
|
|
_imageOS ??= TestcontainersUtils.GetImageOSAsync.Value.GetAwaiter().GetResult();
|
|
return WithImage(_imageOS.Value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically use a Linux image for WireMock. This is "sheyenrath/wiremock.net-alpine:latest"
|
|
/// </summary>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithLinuxImage()
|
|
{
|
|
return WithImage(OSPlatform.Linux);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically use a Windows image for WireMock. This is "sheyenrath/wiremock.net-windows:latest"
|
|
/// </summary>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithWindowsImage()
|
|
{
|
|
return WithImage(OSPlatform.Windows);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a custom WireMock.Net image for which to create the container.
|
|
/// </summary>
|
|
/// <param name="image">The image name.</param>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public new WireMockContainerBuilder WithImage(string image)
|
|
{
|
|
return WithCustomImage(image);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a custom WireMock.Net image for which to create the container.
|
|
/// </summary>
|
|
/// <param name="image">The image name.</param>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithCustomImage(string image)
|
|
{
|
|
_imageOS ??= TestcontainersUtils.GetImageOSAsync.Value.GetAwaiter().GetResult();
|
|
return base.WithImage(image);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the admin username and password for the container (basic authentication).
|
|
/// </summary>
|
|
/// <param name="username">The admin username.</param>
|
|
/// <param name="password">The admin password.</param>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
public WireMockContainerBuilder WithAdminUserNameAndPassword(string username, string password)
|
|
{
|
|
Guard.NotNull(username);
|
|
Guard.NotNull(password);
|
|
|
|
if (string.IsNullOrEmpty(username) && string.IsNullOrEmpty(password))
|
|
{
|
|
return this;
|
|
}
|
|
|
|
return Merge(DockerResourceConfiguration, new WireMockConfiguration(username, password))
|
|
.WithCommand($"--AdminUserName {username}", $"--AdminPassword {password}");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use the WireMockNullLogger.
|
|
/// </summary>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithNullLogger()
|
|
{
|
|
return WithCommand("--WireMockLogger WireMockNullLogger");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Defines if the static mappings should be read at startup (default set to false).
|
|
/// </summary>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithReadStaticMappings()
|
|
{
|
|
return WithCommand("--ReadStaticMappings true");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Watch the static mapping files + folder for changes when running.
|
|
/// </summary>
|
|
/// <param name="includeSubDirectories">Also look in SubDirectories.</param>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithWatchStaticMappings(bool includeSubDirectories)
|
|
{
|
|
DockerResourceConfiguration.WithWatchStaticMappings(includeSubDirectories);
|
|
return
|
|
WithCommand("--ReadStaticMappings true").
|
|
WithCommand("--WatchStaticMappings true").
|
|
WithCommand("--WatchStaticMappingsInSubdirectories", includeSubDirectories);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies the path for the (static) mapping json files.
|
|
/// </summary>
|
|
/// <param name="path">The path</param>
|
|
/// <param name="includeSubDirectories">Also look in SubDirectories.</param>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithMappings(string path, bool includeSubDirectories = false)
|
|
{
|
|
Guard.NotNullOrEmpty(path);
|
|
|
|
DockerResourceConfiguration.WithStaticMappingsPath(path);
|
|
|
|
return WithWatchStaticMappings(includeSubDirectories);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use Http version 2.
|
|
/// </summary>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder WithHttp2()
|
|
{
|
|
return WithCommand("--UseHttp2 true");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds another URL to the WireMock container. By default, the WireMock container will listen on <c>http://*:80</c>.
|
|
///
|
|
/// This method can be used to also host the WireMock container on another port or protocol (like grpc).
|
|
/// </summary>
|
|
/// <example>grpc://*:9090</example>
|
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder AddUrl(string url)
|
|
{
|
|
if (!PortUtils.TryExtract(Guard.NotNullOrEmpty(url), out _, out _, out _, out _, out var port))
|
|
{
|
|
throw new ArgumentException("The URL is not valid.", nameof(url));
|
|
}
|
|
|
|
DockerResourceConfiguration.WithAdditionalUrl(url);
|
|
|
|
return WithPortBinding(port, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a Grpc ProtoDefinition at server-level.
|
|
/// </summary>
|
|
/// <param name="id">Unique identifier for the ProtoDefinition.</param>
|
|
/// <param name="protoDefinition">The ProtoDefinition as text.</param>
|
|
/// <returns><see cref="WireMockContainerBuilder"/></returns>
|
|
[PublicAPI]
|
|
public WireMockContainerBuilder AddProtoDefinition(string id, params string[] protoDefinition)
|
|
{
|
|
Guard.NotNullOrWhiteSpace(id);
|
|
Guard.NotNullOrEmpty(protoDefinition);
|
|
|
|
DockerResourceConfiguration.AddProtoDefinition(id, protoDefinition);
|
|
|
|
return this;
|
|
}
|
|
|
|
private WireMockContainerBuilder WithCommand(string param, bool value)
|
|
{
|
|
return !value ? this : WithCommand($"{param} true");
|
|
}
|
|
|
|
private WireMockContainerBuilder(WireMockConfiguration dockerResourceConfiguration) : base(dockerResourceConfiguration)
|
|
{
|
|
DockerResourceConfiguration = dockerResourceConfiguration;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override WireMockConfiguration DockerResourceConfiguration { get; }
|
|
|
|
/// <inheritdoc />
|
|
public override WireMockContainer Build()
|
|
{
|
|
var builder = this;
|
|
|
|
// In case no image has been set, set the image using internal logic.
|
|
if (DockerResourceConfiguration.Image == null)
|
|
{
|
|
builder = WithImage();
|
|
}
|
|
|
|
// In case the _imageOS is not set, determine it from the Image FullName.
|
|
if (_imageOS == null)
|
|
{
|
|
if (builder.DockerResourceConfiguration.Image.FullName.IndexOf("wiremock", StringComparison.OrdinalIgnoreCase) < 0)
|
|
{
|
|
throw new InvalidOperationException("It's only possible to use a wiremock docker image.");
|
|
}
|
|
|
|
_imageOS = builder.DockerResourceConfiguration.Image.FullName.IndexOf("windows", StringComparison.OrdinalIgnoreCase) >= 0 ? OSPlatform.Windows : OSPlatform.Linux;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(builder.DockerResourceConfiguration.StaticMappingsPath))
|
|
{
|
|
builder = builder.WithBindMount(builder.DockerResourceConfiguration.StaticMappingsPath, ContainerInfoProvider.Info[_imageOS.Value].MappingsPath);
|
|
}
|
|
|
|
if (builder.DockerResourceConfiguration.AdditionalUrls.Any())
|
|
{
|
|
builder = builder.WithCommand($"--Urls http://*:80 {string.Join(" ", builder.DockerResourceConfiguration.AdditionalUrls)}");
|
|
}
|
|
|
|
builder.Validate();
|
|
|
|
var waitForContainerOS = _imageOS == OSPlatform.Windows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
|
builder = builder
|
|
.WithWaitStrategy(waitForContainerOS
|
|
.UntilMessageIsLogged("WireMock.Net server running", waitStrategy => waitStrategy.WithTimeout(TimeSpan.FromSeconds(30)))
|
|
.UntilHttpRequestIsSucceeded(httpWaitStrategy => 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;
|
|
})
|
|
)
|
|
.AddCustomWaitStrategy(new WireMockWaitStrategy())
|
|
);
|
|
|
|
return new WireMockContainer(builder.DockerResourceConfiguration);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override WireMockContainerBuilder Init()
|
|
{
|
|
var builder = base.Init();
|
|
|
|
return builder
|
|
.WithPortBinding(WireMockContainer.ContainerPort, true)
|
|
.WithCommand($"--WireMockLogger {DefaultLogger}");
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override WireMockContainerBuilder Clone(IContainerConfiguration resourceConfiguration)
|
|
{
|
|
return Merge(DockerResourceConfiguration, new WireMockConfiguration(resourceConfiguration));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override WireMockContainerBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
|
|
{
|
|
return Merge(DockerResourceConfiguration, new WireMockConfiguration(resourceConfiguration));
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
protected override WireMockContainerBuilder Merge(WireMockConfiguration oldValue, WireMockConfiguration newValue)
|
|
{
|
|
return new WireMockContainerBuilder(new WireMockConfiguration(oldValue, newValue));
|
|
}
|
|
|
|
private WireMockContainerBuilder WithImage(OSPlatform os)
|
|
{
|
|
_imageOS = os;
|
|
return WithImage(ContainerInfoProvider.Info[os].Image);
|
|
}
|
|
} |