Add Aspire Extension (#1109)

* WireMock.Net.Aspire

* .

* xxx

* nuget

* [CodeFactor] Apply fixes

* ut

* t

* **WireMock.Net.Aspire**

* .

* t

* .

* .

* .

* TESTS

* docker utils

* Install .NET Aspire workload

* 4

* 4!

* projects: '**/test/**/*.csproj'

* script: 'dotnet workload install aspire'

* projects: '**/test/**/*.csproj'

* coverage

* WithWatchStaticMappings

* Admin

* typo

* port

* fix

* .

* x

* ...

* wait

* readme

* x

* 2

* async

* <Version>0.0.1-preview-03</Version>

* ...

* fix aspire

* admin/pwd

* Install .NET Aspire workload

* 0.0.1-preview-04

* WaitForHealthAsync

* ...

* IsHealthyAsync

* .

* add eps

* name: 'Execute Aspire Tests'

* name: Install .NET Aspire workload

* .

* dotnet test

* remove duplicate

* .

* cc

* dotnet tool install --global coverlet.console

* -*

* merge

* /d:sonar.pullrequest.provider=github

* <Version>0.0.1-preview-05</Version>

* // Copyright © WireMock.Net

* .

---------

Co-authored-by: codefactor-io <support@codefactor.io>
This commit is contained in:
Stef Heyenrath
2024-07-27 18:53:59 +02:00
committed by GitHub
parent 69c829fae0
commit 4b12f3419f
70 changed files with 2849 additions and 31 deletions

View File

@@ -0,0 +1,62 @@
// Copyright © WireMock.Net
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
namespace WireMock.Net.Aspire.Tests;
[ExcludeFromCodeCoverage]
internal static class DockerUtils
{
public static bool IsDockerRunningLinuxContainerMode()
{
return IsDockerRunning() && IsLinuxContainerMode();
}
private static bool IsDockerRunning()
{
try
{
var processInfo = new ProcessStartInfo("docker", "info")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};
var process = Process.Start(processInfo);
process?.WaitForExit();
return process?.ExitCode == 0;
}
catch (Exception ex)
{
Console.WriteLine($"Error checking Docker status: {ex.Message}");
return false;
}
}
private static bool IsLinuxContainerMode()
{
try
{
var processInfo = new ProcessStartInfo("docker", "version --format '{{.Server.Os}}'")
{
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
var process = Process.Start(processInfo);
var output = process?.StandardOutput.ReadToEnd();
process?.WaitForExit();
return output?.Contains("linux", StringComparison.OrdinalIgnoreCase) == true;
}
catch (Exception ex)
{
Console.WriteLine($"Error checking Docker container mode: {ex.Message}");
return false;
}
}
}

View File

@@ -0,0 +1,75 @@
// Copyright © WireMock.Net
using System.Net.Http.Json;
using FluentAssertions;
using Projects;
using Xunit.Abstractions;
namespace WireMock.Net.Aspire.Tests;
public class IntegrationTests(ITestOutputHelper output)
{
private record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);
[Fact]
public async Task StartAppHostWithWireMockAndCreateHttpClientToCallTheMockedWeatherForecastEndpoint()
{
if (!DockerUtils.IsDockerRunningLinuxContainerMode())
{
output.WriteLine("Docker is not running in Linux container mode. Skipping test.");
return;
}
// Arrange
var appHostBuilder = await DistributedApplicationTestingBuilder.CreateAsync<WireMock_Net_Aspire_TestAppHost>();
await using var app = await appHostBuilder.BuildAsync();
await app.StartAsync();
using var httpClient = app.CreateHttpClient("wiremock-service");
// Act 1
var weatherForecasts1 = await httpClient.GetFromJsonAsync<WeatherForecast[]>("/weatherforecast");
// Assert 1
weatherForecasts1.Should().BeEquivalentTo(new[]
{
new WeatherForecast(new DateOnly(2024, 5, 24), -10, "Freezing"),
new WeatherForecast(new DateOnly(2024, 5, 25), +33, "Hot")
});
// Act 2
var weatherForecasts2 = await httpClient.GetFromJsonAsync<WeatherForecast[]>("/weatherforecast2");
// Assert 2
weatherForecasts2.Should().HaveCount(5);
}
[Fact]
public async Task StartAppHostWithWireMockAndCreateWireMockAdminClientToCallTheAdminEndpoint()
{
if (!DockerUtils.IsDockerRunningLinuxContainerMode())
{
output.WriteLine("Docker is not running in Linux container mode. Skipping test.");
return;
}
// Arrange
var appHostBuilder = await DistributedApplicationTestingBuilder.CreateAsync<WireMock_Net_Aspire_TestAppHost>();
await using var app = await appHostBuilder.BuildAsync();
await app.StartAsync();
var adminClient = app.CreateWireMockAdminClient("wiremock-service");
// Act 1
var settings = await adminClient.GetSettingsAsync();
// Assert 1
settings.Should().NotBeNull();
// Act 2
var mappings = await adminClient.GetMappingsAsync();
// Assert 2
mappings.Should().HaveCount(2);
}
}

View File

@@ -0,0 +1,45 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<SignAssembly>true</SignAssembly>
<AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Aspire.Hosting.Testing" Version="8.0.0" />
<PackageReference Include="Codecov" Version="1.13.0" />
<PackageReference Include="coverlet.msbuild" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
<PackageReference Include="Moq" Version="4.20.70" />
<PackageReference Include="xunit" Version="2.8.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\WireMock.Net.Aspire\WireMock.Net.Aspire.csproj" IsAspireProjectResource="false" />
<ProjectReference Include="..\WireMock.Net.Aspire.TestAppHost\WireMock.Net.Aspire.TestAppHost.csproj" />
</ItemGroup>
<ItemGroup>
<Using Include="Aspire.Hosting.Testing" />
<Using Include="Xunit" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,134 @@
// Copyright © WireMock.Net
using FluentAssertions;
namespace WireMock.Net.Aspire.Tests;
public class WireMockServerArgumentsTests
{
[Fact]
public void DefaultValues_ShouldBeSetCorrectly()
{
// Arrange & Act
var args = new WireMockServerArguments();
// Assert
args.HttpPort.Should().BeNull();
args.AdminUsername.Should().BeNull();
args.AdminPassword.Should().BeNull();
args.ReadStaticMappings.Should().BeFalse();
args.WithWatchStaticMappings.Should().BeFalse();
args.MappingsPath.Should().BeNull();
}
[Fact]
public void HasBasicAuthentication_ShouldReturnTrue_WhenUsernameAndPasswordAreProvided()
{
// Arrange
var args = new WireMockServerArguments
{
AdminUsername = "admin",
AdminPassword = "password"
};
// Act & Assert
args.HasBasicAuthentication.Should().BeTrue();
}
[Fact]
public void HasBasicAuthentication_ShouldReturnFalse_WhenEitherUsernameOrPasswordIsNotProvided()
{
// Arrange
var argsWithUsernameOnly = new WireMockServerArguments { AdminUsername = "admin" };
var argsWithPasswordOnly = new WireMockServerArguments { AdminPassword = "password" };
// Act & Assert
argsWithUsernameOnly.HasBasicAuthentication.Should().BeFalse();
argsWithPasswordOnly.HasBasicAuthentication.Should().BeFalse();
}
[Fact]
public void GetArgs_WhenReadStaticMappingsIsTrue_ShouldContainReadStaticMappingsTrue()
{
// Arrange
var args = new WireMockServerArguments
{
ReadStaticMappings = true
};
// Act
var commandLineArgs = args.GetArgs();
// Assert
commandLineArgs.Should().ContainInOrder("--ReadStaticMappings", "true");
}
[Fact]
public void GetArgs_WhenReadStaticMappingsIsFalse_ShouldNotContainReadStaticMappingsTrue()
{
// Arrange
var args = new WireMockServerArguments
{
ReadStaticMappings = false
};
// Act
var commandLineArgs = args.GetArgs();
// Assert
commandLineArgs.Should().NotContain("--ReadStaticMappings", "true");
}
[Theory]
[InlineData(false)]
[InlineData(true)]
public void GetArgs_WhenWithWatchStaticMappingsIsTrue_ShouldContainWatchStaticMappingsTrue(bool readStaticMappings)
{
// Arrange
var args = new WireMockServerArguments
{
WithWatchStaticMappings = true,
ReadStaticMappings = readStaticMappings
};
// Act
var commandLineArgs = args.GetArgs();
// Assert
commandLineArgs.Should().ContainInOrder("--ReadStaticMappings", "true", "--WatchStaticMappings", "true", "--WatchStaticMappingsInSubdirectories", "true");
}
[Fact]
public void GetArgs_WhenWithWatchStaticMappingsIsFalse_ShouldNotContainWatchStaticMappingsTrue()
{
// Arrange
var args = new WireMockServerArguments
{
WithWatchStaticMappings = false
};
// Act
var commandLineArgs = args.GetArgs();
// Assert
commandLineArgs.Should().NotContain("--WatchStaticMappings", "true").And.NotContain("--WatchStaticMappingsInSubdirectories", "true");
}
[Fact]
public void GetArgs_ShouldIncludeAuthenticationDetails_WhenAuthenticationIsRequired()
{
// Arrange
var args = new WireMockServerArguments
{
AdminUsername = "admin",
AdminPassword = "password"
};
// Act
var commandLineArgs = args.GetArgs();
// Assert
commandLineArgs.Should().Contain("--AdminUserName", "admin");
commandLineArgs.Should().Contain("--AdminPassword", "password");
}
}

View File

@@ -0,0 +1,96 @@
// Copyright © WireMock.Net
using System.Net.Sockets;
using FluentAssertions;
using Moq;
namespace WireMock.Net.Aspire.Tests;
public class WireMockServerBuilderExtensionsTests
{
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData(" ")]
[InlineData("\t")]
public void AddWireMock_WithNullOrWhiteSpaceName_ShouldThrowException(string? name)
{
// Arrange
var builder = Mock.Of<IDistributedApplicationBuilder>();
// Act
Action act = () => builder.AddWireMock(name!, 12345);
// Assert
act.Should().Throw<Exception>();
}
[Fact]
public void AddWireMock_WithInvalidPort_ShouldThrowArgumentOutOfRangeException()
{
// Arrange
const int invalidPort = -1;
var builder = Mock.Of<IDistributedApplicationBuilder>();
// Act
Action act = () => builder.AddWireMock("ValidName", invalidPort);
// Assert
act.Should().Throw<ArgumentOutOfRangeException>().WithMessage("Specified argument was out of the range of valid values. (Parameter 'port')");
}
[Fact]
public void AddWireMock()
{
// Arrange
var name = $"apiservice{Guid.NewGuid()}";
const int port = 12345;
const string username = "admin";
const string password = "test";
var builder = DistributedApplication.CreateBuilder();
// Act
var wiremock = builder
.AddWireMock(name, port)
.WithAdminUserNameAndPassword(username, password)
.WithReadStaticMappings();
// Assert
wiremock.Resource.Should().NotBeNull();
wiremock.Resource.Name.Should().Be(name);
wiremock.Resource.Arguments.Should().BeEquivalentTo(new WireMockServerArguments
{
AdminPassword = password,
AdminUsername = username,
ReadStaticMappings = true,
WithWatchStaticMappings = false,
MappingsPath = null,
HttpPort = port
});
wiremock.Resource.Annotations.Should().HaveCount(4);
var containerImageAnnotation = wiremock.Resource.Annotations.OfType<ContainerImageAnnotation>().FirstOrDefault();
containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
{
Image = "sheyenrath/wiremock.net-alpine",
Registry = null,
Tag = "latest"
});
var endpointAnnotation = wiremock.Resource.Annotations.OfType<EndpointAnnotation>().FirstOrDefault();
endpointAnnotation.Should().BeEquivalentTo(new EndpointAnnotation(
protocol: ProtocolType.Tcp,
uriScheme: "http",
transport: null,
name: null,
port: port,
targetPort: 80,
isExternal: null,
isProxied: true
));
wiremock.Resource.Annotations.OfType<EnvironmentCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
wiremock.Resource.Annotations.OfType<CommandLineArgsCallbackAnnotation>().FirstOrDefault().Should().NotBeNull();
}
}