mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-21 08:21:53 +02:00
WireMock.Net.Testcontainers (#948)
* WireMock.Net.Testcontainers * . * logger? * . * . * WatchStaticMappings * linux * . * -- * ContainerInfo * . * 02 * . * fix * .
This commit is contained in:
@@ -68,13 +68,15 @@ WireMock.Net can be used in several ways:
|
|||||||
You can use your favorite test framework and use WireMock within your tests, see
|
You can use your favorite test framework and use WireMock within your tests, see
|
||||||
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
[Wiki : UnitTesting](https://github.com/StefH/WireMock.Net/wiki/Using-WireMock-in-UnitTests).
|
||||||
|
|
||||||
|
### Unit/Integration Testing using Testcontainers.DotNet
|
||||||
|
You can use [Wiki : WireMock.Net.Testcontainers](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||||
|
|
||||||
### As a dotnet tool
|
### As a dotnet tool
|
||||||
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
|
It's simple to install WireMock.Net as (global) dotnet tool, see [Wiki : dotnet tool](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-dotnet-tool).
|
||||||
|
|
||||||
### As standalone process / console application
|
### As standalone process / console application
|
||||||
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
This is quite straight forward to launch a mock server within a console application, see [Wiki : Standalone Process](https://github.com/StefH/WireMock.Net/wiki/WireMock-as-a-standalone-process).
|
||||||
|
|
||||||
|
|
||||||
### As a Windows Service
|
### As a Windows Service
|
||||||
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
|
You can also run WireMock.Net as a Windows Service, follow this [WireMock-as-a-Windows-Service](https://github.com/WireMock-Net/WireMock.Net/wiki/WireMock-as-a-Windows-Service).
|
||||||
|
|
||||||
|
|||||||
@@ -111,6 +111,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueExample",
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "examples\WireMockAzureQueueProxy\WireMockAzureQueueProxy.csproj", "{ADB557D8-D66B-4387-912B-3F73E290B478}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.Testcontainers", "src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj", "{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WireMock.Net.TestcontainersExample", "examples\WireMock.Net.TestcontainersExample\WireMock.Net.TestcontainersExample.csproj", "{56A38798-C48B-4A4A-B805-071E05C02CE1}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -265,6 +269,14 @@ Global
|
|||||||
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{ADB557D8-D66B-4387-912B-3F73E290B478}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU
|
{ADB557D8-D66B-4387-912B-3F73E290B478}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{56A38798-C48B-4A4A-B805-071E05C02CE1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@@ -309,6 +321,8 @@ Global
|
|||||||
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
{7C2A9DE8-C89F-4841-9058-6B9BF81E5E34} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
{BAA9EC2A-874B-45CE-8E51-A73622DC7F3D} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
{ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
{ADB557D8-D66B-4387-912B-3F73E290B478} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
|
{12B016A5-9D8B-4EFE-96C2-CA51BE43367D} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||||
|
{56A38798-C48B-4A4A-B805-071E05C02CE1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=IP/@EntryIndexedValue">IP</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=MD/@EntryIndexedValue">MD5</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OPTIONS/@EntryIndexedValue">OPTIONS</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OPTIONS/@EntryIndexedValue">OPTIONS</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=OS/@EntryIndexedValue">OS</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PATCH/@EntryIndexedValue">PATCH</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PATCH/@EntryIndexedValue">PATCH</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=POST/@EntryIndexedValue">POST</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=POST/@EntryIndexedValue">POST</s:String>
|
||||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PUT/@EntryIndexedValue">PUT</s:String>
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=PUT/@EntryIndexedValue">PUT</s:String>
|
||||||
@@ -32,6 +33,7 @@
|
|||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Raml/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Raml/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=randomizer/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scriban/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Scriban/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=sheyenrath/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sigil/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Sigil/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=Stef/@EntryIndexedValue">True</s:Boolean>
|
||||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=templated/@EntryIndexedValue">True</s:Boolean>
|
<s:Boolean x:Key="/Default/UserDictionary/Words/=templated/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
|||||||
43
examples/WireMock.Net.TestcontainersExample/Program.cs
Normal file
43
examples/WireMock.Net.TestcontainersExample/Program.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
using Testcontainers.MsSql;
|
||||||
|
using WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
|
namespace WireMock.Net.TestcontainersExample;
|
||||||
|
|
||||||
|
internal class Program
|
||||||
|
{
|
||||||
|
private static async Task Main(string[] args)
|
||||||
|
{
|
||||||
|
var container = new WireMockContainerBuilder()
|
||||||
|
.WithAdminUserNameAndPassword("x", "y")
|
||||||
|
.WithMappings(@"C:\Dev\GitHub\WireMock.Net\examples\WireMock.Net.Console.NET6\__admin\mappings")
|
||||||
|
.WithWatchStaticMappings(true)
|
||||||
|
.WithAutoRemove(true)
|
||||||
|
.WithCleanUp(true)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
await container.StartAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
var logs = await container.GetLogsAsync(DateTime.Now.AddDays(-1)).ConfigureAwait(false);
|
||||||
|
Console.WriteLine("logs = " + logs.Stdout);
|
||||||
|
|
||||||
|
var restEaseApiClient = container.CreateWireMockAdminClient();
|
||||||
|
|
||||||
|
var settings = await restEaseApiClient.GetSettingsAsync();
|
||||||
|
Console.WriteLine("settings = " + JsonConvert.SerializeObject(settings, Formatting.Indented));
|
||||||
|
|
||||||
|
var mappings = await restEaseApiClient.GetMappingsAsync();
|
||||||
|
Console.WriteLine("mappings = " + JsonConvert.SerializeObject(mappings, Formatting.Indented));
|
||||||
|
|
||||||
|
var client = container.CreateClient();
|
||||||
|
var result = await client.GetStringAsync("/static/mapping");
|
||||||
|
Console.WriteLine("result = " + result);
|
||||||
|
|
||||||
|
await container.StopAsync();
|
||||||
|
|
||||||
|
var sql = new MsSqlBuilder()
|
||||||
|
.WithAutoRemove(true)
|
||||||
|
.WithCleanUp(true)
|
||||||
|
.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Testcontainers.MsSql" Version="3.2.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\WireMock.Net.Testcontainers\WireMock.Net.Testcontainers.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="873d495f-940e-4b86-a1f4-4f0fc7be8b8b.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
7
src/WireMock.Net.Testcontainers/Models/ContainerInfo.cs
Normal file
7
src/WireMock.Net.Testcontainers/Models/ContainerInfo.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace WireMock.Net.Testcontainers.Models;
|
||||||
|
|
||||||
|
internal record ContainerInfo
|
||||||
|
(
|
||||||
|
string Image,
|
||||||
|
string MappingsPath
|
||||||
|
);
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Description>A fluent testcontainer builder for the Docker version of WireMock.Net</Description>
|
||||||
|
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<PackageTags>wiremock;docker;testcontainer;testcontainers</PackageTags>
|
||||||
|
<ProjectGuid>{12B016A5-9D8B-4EFE-96C2-CA51BE43367D}</ProjectGuid>
|
||||||
|
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||||
|
<CodeAnalysisRuleSet>../WireMock.Net/WireMock.Net.ruleset</CodeAnalysisRuleSet>
|
||||||
|
<SignAssembly>true</SignAssembly>
|
||||||
|
<AssemblyOriginatorKeyFile>../WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||||
|
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<LangVersion>10</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
|
||||||
|
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IsExternalInit" Version="1.0.3">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
|
||||||
|
<PackageReference Include="Stef.Validation" Version="0.1.1" />
|
||||||
|
<PackageReference Include="Testcontainers" Version="3.2.0" />
|
||||||
|
<PackageReference Include="JetBrains.Annotations" VersionOverride="2022.3.1" PrivateAssets="All" Version="2022.3.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
66
src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
Normal file
66
src/WireMock.Net.Testcontainers/WireMockConfiguration.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using Docker.DotNet.Models;
|
||||||
|
using DotNet.Testcontainers.Builders;
|
||||||
|
using DotNet.Testcontainers.Configurations;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ContainerConfiguration" />
|
||||||
|
[PublicAPI]
|
||||||
|
public sealed class WireMockConfiguration : ContainerConfiguration
|
||||||
|
{
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
public string? Username { get; }
|
||||||
|
|
||||||
|
public string? Password { get; }
|
||||||
|
|
||||||
|
public bool HasBasicAuthentication => !string.IsNullOrEmpty(Username) && !string.IsNullOrEmpty(Password);
|
||||||
|
|
||||||
|
public WireMockConfiguration(
|
||||||
|
string? username = null,
|
||||||
|
string? password = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
Username = username;
|
||||||
|
Password = password;
|
||||||
|
}
|
||||||
|
#pragma warning restore CS1591
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
|
||||||
|
public WireMockConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration) : base(resourceConfiguration)
|
||||||
|
{
|
||||||
|
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
|
||||||
|
public WireMockConfiguration(IContainerConfiguration resourceConfiguration) : base(resourceConfiguration)
|
||||||
|
{
|
||||||
|
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
|
||||||
|
public WireMockConfiguration(WireMockConfiguration resourceConfiguration) : this(new WireMockConfiguration(), resourceConfiguration)
|
||||||
|
{
|
||||||
|
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockConfiguration" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="oldValue">The old Docker resource configuration.</param>
|
||||||
|
/// <param name="newValue">The new Docker resource configuration.</param>
|
||||||
|
public WireMockConfiguration(WireMockConfiguration oldValue, WireMockConfiguration newValue) : base(oldValue, newValue)
|
||||||
|
{
|
||||||
|
Username = BuildConfiguration.Combine(oldValue.Username, newValue.Username);
|
||||||
|
Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
|
||||||
|
}
|
||||||
|
}
|
||||||
110
src/WireMock.Net.Testcontainers/WireMockContainer.cs
Normal file
110
src/WireMock.Net.Testcontainers/WireMockContainer.cs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Text;
|
||||||
|
using DotNet.Testcontainers.Containers;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using RestEase;
|
||||||
|
using Stef.Validation;
|
||||||
|
using WireMock.Client;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A container for running WireMock in a docker environment.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WireMockContainer : DockerContainer
|
||||||
|
{
|
||||||
|
internal const int ContainerPort = 80;
|
||||||
|
|
||||||
|
private readonly WireMockConfiguration _configuration;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockContainer" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration">The container configuration.</param>
|
||||||
|
/// <param name="logger">The logger.</param>
|
||||||
|
public WireMockContainer(WireMockConfiguration configuration, ILogger logger) : base(configuration, logger)
|
||||||
|
{
|
||||||
|
_configuration = Guard.NotNull(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the public Url.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public string GetPublicUrl() => GetPublicUri().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a RestEase Admin client which can be used to call the admin REST endpoint.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="IWireMockAdminApi"/></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public IWireMockAdminApi CreateWireMockAdminClient()
|
||||||
|
{
|
||||||
|
ValidateIfRunning();
|
||||||
|
|
||||||
|
var api = RestClient.For<IWireMockAdminApi>(GetPublicUri());
|
||||||
|
|
||||||
|
if (_configuration.HasBasicAuthentication)
|
||||||
|
{
|
||||||
|
api.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{_configuration.Username}:{_configuration.Password}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
return api;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="HttpClient"/> which can be used to call this instance.
|
||||||
|
/// <param name="handlers">
|
||||||
|
/// An ordered list of System.Net.Http.DelegatingHandler instances to be invoked
|
||||||
|
/// as an System.Net.Http.HttpRequestMessage travels from the System.Net.Http.HttpClient
|
||||||
|
/// to the network and an System.Net.Http.HttpResponseMessage travels from the network
|
||||||
|
/// back to System.Net.Http.HttpClient. The handlers are invoked in a top-down fashion.
|
||||||
|
/// That is, the first entry is invoked first for an outbound request message but
|
||||||
|
/// last for an inbound response message.
|
||||||
|
/// </param>
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public HttpClient CreateClient(params DelegatingHandler[] handlers)
|
||||||
|
{
|
||||||
|
ValidateIfRunning();
|
||||||
|
|
||||||
|
var client = HttpClientFactory.Create(handlers);
|
||||||
|
client.BaseAddress = GetPublicUri();
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a <see cref="HttpClient"/> (one for each URL) which can be used to call this instance.
|
||||||
|
/// <param name="innerHandler">The inner handler represents the destination of the HTTP message channel.</param>
|
||||||
|
/// <param name="handlers">
|
||||||
|
/// An ordered list of System.Net.Http.DelegatingHandler instances to be invoked
|
||||||
|
/// as an System.Net.Http.HttpRequestMessage travels from the System.Net.Http.HttpClient
|
||||||
|
/// to the network and an System.Net.Http.HttpResponseMessage travels from the network
|
||||||
|
/// back to System.Net.Http.HttpClient. The handlers are invoked in a top-down fashion.
|
||||||
|
/// That is, the first entry is invoked first for an outbound request message but
|
||||||
|
/// last for an inbound response message.
|
||||||
|
/// </param>
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public HttpClient CreateClient(HttpMessageHandler innerHandler, params DelegatingHandler[] handlers)
|
||||||
|
{
|
||||||
|
ValidateIfRunning();
|
||||||
|
|
||||||
|
var client = HttpClientFactory.Create(innerHandler, handlers);
|
||||||
|
client.BaseAddress = GetPublicUri();
|
||||||
|
return client;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ValidateIfRunning()
|
||||||
|
{
|
||||||
|
if (State != TestcontainersStates.Running)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Unable to create HttpClient because the WireMock.Net is not yet running.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uri GetPublicUri() => new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(ContainerPort)).Uri;
|
||||||
|
}
|
||||||
175
src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
Normal file
175
src/WireMock.Net.Testcontainers/WireMockContainerBuilder.cs
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Docker.DotNet.Models;
|
||||||
|
using DotNet.Testcontainers.Builders;
|
||||||
|
using DotNet.Testcontainers.Configurations;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Stef.Validation;
|
||||||
|
using WireMock.Net.Testcontainers.Models;
|
||||||
|
|
||||||
|
namespace WireMock.Net.Testcontainers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An specific fluent Docker container builder for WireMock.Net
|
||||||
|
/// </summary>
|
||||||
|
public sealed class WireMockContainerBuilder : ContainerBuilder<WireMockContainerBuilder, WireMockContainer, WireMockConfiguration>
|
||||||
|
{
|
||||||
|
private readonly Dictionary<bool, ContainerInfo> _info = new()
|
||||||
|
{
|
||||||
|
{ false, new ContainerInfo("sheyenrath/wiremock.net:latest", "/app/__admin/mappings") },
|
||||||
|
{ true, new ContainerInfo("sheyenrath/wiremock.net-windows:latest", @"c:\app\__admin\mappings") }
|
||||||
|
};
|
||||||
|
|
||||||
|
private const string DefaultLogger = "WireMockConsoleLogger";
|
||||||
|
|
||||||
|
private readonly Lazy<Task<bool>> _isWindowsAsLazy = new(async () =>
|
||||||
|
{
|
||||||
|
using var dockerClientConfig = TestcontainersSettings.OS.DockerEndpointAuthConfig.GetDockerClientConfiguration();
|
||||||
|
using var dockerClient = dockerClientConfig.CreateClient();
|
||||||
|
|
||||||
|
var version = await dockerClient.System.GetVersionAsync();
|
||||||
|
return version.Os.IndexOf("Windows", StringComparison.OrdinalIgnoreCase) > -1;
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ContainerBuilder" /> class.
|
||||||
|
/// </summary>
|
||||||
|
public WireMockContainerBuilder() : this(new WireMockConfiguration())
|
||||||
|
{
|
||||||
|
DockerResourceConfiguration = Init().DockerResourceConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Automatically set the correct image (Linux or Windows) for WireMock which to create the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public WireMockContainerBuilder WithImage()
|
||||||
|
{
|
||||||
|
var isWindows = _isWindowsAsLazy.Value.GetAwaiter().GetResult();
|
||||||
|
return WithImage(_info[isWindows].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>
|
||||||
|
/// <returns>A configured instance of <see cref="WireMockContainerBuilder"/></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public WireMockContainerBuilder WithWatchStaticMappings(bool includeSubDirectories)
|
||||||
|
{
|
||||||
|
return WithCommand("--WatchStaticMappings true").WithCommand($"--WatchStaticMappingsInSubdirectories {includeSubDirectories}");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specifies the path for the (static) mapping json files.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public WireMockContainerBuilder WithMappings(string path)
|
||||||
|
{
|
||||||
|
Guard.NotNullOrEmpty(path);
|
||||||
|
|
||||||
|
var isWindows = _isWindowsAsLazy.Value.GetAwaiter().GetResult();
|
||||||
|
|
||||||
|
return WithReadStaticMappings().WithBindMount(path, _info[isWindows].MappingsPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="WireMockContainerBuilder" /> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dockerResourceConfiguration">The Docker resource configuration.</param>
|
||||||
|
private WireMockContainerBuilder(WireMockConfiguration dockerResourceConfiguration) : base(dockerResourceConfiguration)
|
||||||
|
{
|
||||||
|
DockerResourceConfiguration = dockerResourceConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override WireMockConfiguration DockerResourceConfiguration { get; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override WireMockContainer Build()
|
||||||
|
{
|
||||||
|
Validate();
|
||||||
|
|
||||||
|
return new WireMockContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override WireMockContainerBuilder Init()
|
||||||
|
{
|
||||||
|
var builder = base.Init();
|
||||||
|
|
||||||
|
// In case no image has been set, set the image using internal logic.
|
||||||
|
if (builder.DockerResourceConfiguration.Image == null)
|
||||||
|
{
|
||||||
|
builder = builder.WithImage();
|
||||||
|
}
|
||||||
|
|
||||||
|
var isWindows = _isWindowsAsLazy.Value.GetAwaiter().GetResult();
|
||||||
|
var waitForContainerOS = isWindows ? Wait.ForWindowsContainer() : Wait.ForUnixContainer();
|
||||||
|
return builder
|
||||||
|
.WithPortBinding(WireMockContainer.ContainerPort, true)
|
||||||
|
.WithCommand($"--WireMockLogger {DefaultLogger}")
|
||||||
|
.WithWaitStrategy(waitForContainerOS.UntilMessageIsLogged("By Stef Heyenrath"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user