diff --git a/azure-pipelines-ci.yml b/azure-pipelines-ci.yml
index e98c695a..a20a72d0 100644
--- a/azure-pipelines-ci.yml
+++ b/azure-pipelines-ci.yml
@@ -1,185 +1,176 @@
-variables:
- Prerelease: 'ci'
- buildId: "1$(Build.BuildId)"
- buildProjects: '**/src/**/*.csproj'
-
-jobs:
-- job: Linux_Build_Test_SonarCloud
-
- pool:
- vmImage: 'ubuntu-22.04'
-
- steps:
- - script: |
- echo "BuildId = $(buildId)"
- displayName: 'Print buildId'
-
- - task: CmdLine@2
- displayName: 'Install .NET Aspire workload'
- inputs:
- script: 'dotnet workload install aspire'
-
- - script: |
- dotnet tool install --global dotnet-sonarscanner
- dotnet tool install --global dotnet-coverage
- displayName: 'Install dotnet tools'
-
- - script: |
- dotnet workload install aspire
- displayName: 'Install aspire'
-
- - task: PowerShell@2
- displayName: "Use JDK17 by default"
- inputs:
- targetType: 'inline'
- script: |
- $jdkPath = $env:JAVA_HOME_17_X64
- Write-Host "##vso[task.setvariable variable=JAVA_HOME]$jdkPath"
-
- - script: |
- dotnet dev-certs https --trust || true
- displayName: 'dotnet dev-certs https'
-
- # See: https://docs.sonarsource.com/sonarcloud/enriching/test-coverage/dotnet-test-coverage
- - script: |
- dotnet sonarscanner begin /k:"WireMock-Net_WireMock.Net" /o:"wiremock-net" /d:sonar.branch.name=$(Build.SourceBranchName) /d:sonar.host.url="https://sonarcloud.io" /d:sonar.token="$(SONAR_TOKEN)" /d:sonar.pullrequest.provider=github /d:sonar.cs.vscoveragexml.reportsPaths=**/wiremock-coverage-*.xml /d:sonar.verbose=true
- displayName: 'Begin analysis on SonarCloud'
- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
-
- - task: DotNetCoreCLI@2
- displayName: 'Build Unit tests'
- inputs:
- command: 'build'
- projects: '**/test/**/*.csproj'
- arguments: '--configuration Debug --framework net8.0'
-
- - task: CmdLine@2
- inputs:
- script: |
- dotnet-coverage collect "dotnet test ./test/WireMock.Net.Tests/WireMock.Net.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-xunit.xml"
- displayName: 'WireMock.Net.Tests with Coverage'
-
- - task: CmdLine@2
- inputs:
- script: |
- dotnet-coverage collect "dotnet test ./test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-tunit.xml"
- displayName: 'WireMock.Net.TUnitTests with Coverage'
-
- - task: CmdLine@2
- inputs:
- script: |
- dotnet-coverage collect "dotnet test ./test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-middleware.xml"
- displayName: 'WireMock.Net.Middleware.Tests with Coverage'
-
- - task: CmdLine@2
- inputs:
- script: |
- dotnet-coverage collect "dotnet test ./test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj --configuration Debug --no-build" -f xml -o "wiremock-coverage-aspire.xml"
- displayName: 'WireMock.Net.Aspire.Tests with Coverage'
-
- - task: CmdLine@2
- displayName: 'Merge coverage files'
- inputs:
- script: 'dotnet coverage merge **/wiremock-coverage-*.xml --output ./test/wiremock-coverage.xml --output-format xml'
-
- - script: |
- dotnet sonarscanner end /d:sonar.token="$(SONAR_TOKEN)"
- displayName: 'End analysis on SonarCloud'
- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
-
- - task: whitesource.ws-bolt.bolt.wss.WhiteSource Bolt@19
- displayName: 'WhiteSource Bolt'
- condition: and(succeeded(), eq(variables['RUN_WHITESOURCE'], 'yes'))
-
- - script: |
- bash <(curl https://codecov.io/bash) -t $(CODECOV_TOKEN) -f ./test/wiremock-coverage.xml
- displayName: 'Upload coverage results to codecov'
-
- - task: PublishTestResults@2
- condition: and(succeeded(), eq(variables['PUBLISH_TESTRESULTS'], 'yes'))
- inputs:
- testRunner: VSTest
- testResultsFiles: '**/*.trx'
-
- - task: PublishBuildArtifacts@1
- displayName: Publish coverage files
- inputs:
- PathtoPublish: './test/WireMock.Net.Tests/coverage.net8.0.opencover.xml'
-
-- job: Windows_Build_Test
-
- pool:
- vmImage: 'windows-2022'
-
- steps:
- - task: UseDotNet@2
- displayName: Use .NET 8.0
- inputs:
- packageType: 'sdk'
- version: '8.0.x'
-
- - task: DotNetCoreCLI@2
- displayName: 'WireMock.Net.Tests with Coverage'
- inputs:
- command: 'test'
- projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj'
- arguments: '--configuration Debug --framework net8.0 --collect:"XPlat Code Coverage" --logger trx'
-
- - task: DotNetCoreCLI@2
- displayName: 'WireMock.Net.TUnitTests with Coverage'
- inputs:
- command: 'test'
- projects: './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj'
- arguments: '--configuration Debug --framework net8.0 --collect:"XPlat Code Coverage" --logger trx'
-
- - task: DotNetCoreCLI@2
- displayName: 'WireMock.Net.Middleware.Tests with Coverage'
- inputs:
- command: 'test'
- projects: './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj'
- arguments: '--configuration Debug --framework net8.0 --collect:"XPlat Code Coverage" --logger trx'
-
-- job: Windows_Release_to_MyGet
- dependsOn: Windows_Build_Test
-
- pool:
- vmImage: 'windows-2022'
-
- steps:
- - task: UseDotNet@2
- displayName: Use .NET 8.0
- inputs:
- packageType: 'sdk'
- version: '8.0.x'
-
- - task: DotNetCoreCLI@2
- displayName: Build Release
- inputs:
- command: 'build'
- arguments: /p:Configuration=Release
- projects: $(buildProjects)
-
- - task: DotNetCoreCLI@2
- displayName: Pack
- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
- inputs:
- command: pack
- configuration: 'Release'
- packagesToPack: $(buildProjects)
- nobuild: true
- packDirectory: '$(Build.ArtifactStagingDirectory)/packages'
- verbosityPack: 'normal'
-
- - task: PublishBuildArtifacts@1
- displayName: Publish Artifacts
- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
- inputs:
- PathtoPublish: '$(Build.ArtifactStagingDirectory)'
-
- - task: DotNetCoreCLI@2
- displayName: Push to MyGet
- condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
- inputs:
- command: custom
- custom: nuget
+variables:
+ Prerelease: 'ci'
+ buildId: "1$(Build.BuildId)"
+ buildProjects: '**/src/**/*.csproj'
+
+jobs:
+- job: Linux_Build_Test_SonarCloud
+
+ pool:
+ vmImage: 'ubuntu-22.04'
+
+ steps:
+ - script: |
+ echo "BuildId = $(buildId)"
+ displayName: 'Print buildId'
+
+ - script: |
+ dotnet tool install --global dotnet-sonarscanner
+ dotnet tool install --global dotnet-coverage
+ displayName: 'Install dotnet tools'
+
+ - task: PowerShell@2
+ displayName: "Use JDK17 by default"
+ inputs:
+ targetType: 'inline'
+ script: |
+ $jdkPath = $env:JAVA_HOME_17_X64
+ Write-Host "##vso[task.setvariable variable=JAVA_HOME]$jdkPath"
+
+ - script: |
+ dotnet dev-certs https --trust || true
+ displayName: 'dotnet dev-certs https'
+
+ # See: https://docs.sonarsource.com/sonarcloud/enriching/test-coverage/dotnet-test-coverage
+ - script: |
+ dotnet sonarscanner begin /k:"WireMock-Net_WireMock.Net" /o:"wiremock-net" /d:sonar.branch.name=$(Build.SourceBranchName) /d:sonar.host.url="https://sonarcloud.io" /d:sonar.token="$(SONAR_TOKEN)" /d:sonar.pullrequest.provider=github /d:sonar.cs.vscoveragexml.reportsPaths=**/wiremock-coverage-*.xml /d:sonar.verbose=true
+ displayName: 'Begin analysis on SonarCloud'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
+
+ - task: DotNetCoreCLI@2
+ displayName: 'Build Unit tests'
+ inputs:
+ command: 'build'
+ projects: '**/test/**/*.csproj'
+ arguments: '--configuration Debug --framework net8.0'
+
+ - task: CmdLine@2
+ inputs:
+ script: |
+ dotnet-coverage collect "dotnet test ./test/WireMock.Net.Tests/WireMock.Net.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-xunit.xml"
+ displayName: 'WireMock.Net.Tests with Coverage'
+
+ - task: CmdLine@2
+ inputs:
+ script: |
+ dotnet-coverage collect "dotnet test ./test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-tunit.xml"
+ displayName: 'WireMock.Net.TUnitTests with Coverage'
+
+ - task: CmdLine@2
+ inputs:
+ script: |
+ dotnet-coverage collect "dotnet test ./test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj --configuration Debug --no-build --framework net8.0" -f xml -o "wiremock-coverage-middleware.xml"
+ displayName: 'WireMock.Net.Middleware.Tests with Coverage'
+
+ - task: CmdLine@2
+ inputs:
+ script: |
+ dotnet-coverage collect "dotnet test ./test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj --configuration Debug --no-build" -f xml -o "wiremock-coverage-aspire.xml"
+ displayName: 'WireMock.Net.Aspire.Tests with Coverage'
+
+ - task: CmdLine@2
+ displayName: 'Merge coverage files'
+ inputs:
+ script: 'dotnet coverage merge **/wiremock-coverage-*.xml --output ./test/wiremock-coverage.xml --output-format xml'
+
+ - script: |
+ dotnet sonarscanner end /d:sonar.token="$(SONAR_TOKEN)"
+ displayName: 'End analysis on SonarCloud'
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
+
+ - task: whitesource.ws-bolt.bolt.wss.WhiteSource Bolt@19
+ displayName: 'WhiteSource Bolt'
+ condition: and(succeeded(), eq(variables['RUN_WHITESOURCE'], 'yes'))
+
+ - script: |
+ bash <(curl https://codecov.io/bash) -t $(CODECOV_TOKEN) -f ./test/wiremock-coverage.xml
+ displayName: 'Upload coverage results to codecov'
+
+ - task: PublishTestResults@2
+ condition: and(succeeded(), eq(variables['PUBLISH_TESTRESULTS'], 'yes'))
+ inputs:
+ testRunner: VSTest
+ testResultsFiles: '**/*.trx'
+
+ - task: PublishBuildArtifacts@1
+ displayName: Publish coverage files
+ inputs:
+ PathtoPublish: './test/WireMock.Net.Tests/coverage.net8.0.opencover.xml'
+
+- job: Windows_Build_Test
+
+ pool:
+ vmImage: 'windows-2022'
+
+ steps:
+ - task: UseDotNet@2
+ displayName: Use .NET 8.0
+ inputs:
+ packageType: 'sdk'
+ version: '8.0.x'
+
+ - task: DotNetCoreCLI@2
+ displayName: 'WireMock.Net.Tests with Coverage'
+ inputs:
+ command: 'test'
+ projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj'
+ arguments: '--configuration Debug --framework net8.0 --collect:"XPlat Code Coverage" --logger trx'
+
+ - task: DotNetCoreCLI@2
+ displayName: 'WireMock.Net.TUnitTests with Coverage'
+ inputs:
+ command: 'test'
+ projects: './test/WireMock.Net.TUnitTests/WireMock.Net.TUnitTests.csproj'
+ arguments: '--configuration Debug --framework net8.0 --collect:"XPlat Code Coverage" --logger trx'
+
+ - task: DotNetCoreCLI@2
+ displayName: 'WireMock.Net.Middleware.Tests with Coverage'
+ inputs:
+ command: 'test'
+ projects: './test/WireMock.Net.Middleware.Tests/WireMock.Net.Middleware.Tests.csproj'
+ arguments: '--configuration Debug --framework net8.0 --collect:"XPlat Code Coverage" --logger trx'
+
+- job: Windows_Release_to_MyGet
+ dependsOn: Windows_Build_Test
+
+ pool:
+ vmImage: 'windows-2022'
+
+ steps:
+ - task: UseDotNet@2
+ displayName: Use .NET 8.0
+ inputs:
+ packageType: 'sdk'
+ version: '8.0.x'
+
+ - task: DotNetCoreCLI@2
+ displayName: Build Release
+ inputs:
+ command: 'build'
+ arguments: /p:Configuration=Release
+ projects: $(buildProjects)
+
+ - task: DotNetCoreCLI@2
+ displayName: Pack
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
+ inputs:
+ command: pack
+ configuration: 'Release'
+ packagesToPack: $(buildProjects)
+ nobuild: true
+ packDirectory: '$(Build.ArtifactStagingDirectory)/packages'
+ verbosityPack: 'normal'
+
+ - task: PublishBuildArtifacts@1
+ displayName: Publish Artifacts
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
+ inputs:
+ PathtoPublish: '$(Build.ArtifactStagingDirectory)'
+
+ - task: DotNetCoreCLI@2
+ displayName: Push to MyGet
+ condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) # Do not run for PullRequests
+ inputs:
+ command: custom
+ custom: nuget
arguments: push $(Build.ArtifactStagingDirectory)\packages\*.nupkg -n -s https://www.myget.org/F/wiremock-net/api/v3/index.json -k $(MyGetKey)
\ No newline at end of file
diff --git a/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj b/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj
index 086e1522..f0554470 100644
--- a/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj
+++ b/examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj
@@ -1,23 +1,24 @@
-
-
-
- Exe
- net8.0
- enable
- enable
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj b/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj
index d31ac8fd..f556db85 100644
--- a/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj
+++ b/examples-Aspire/AspireApp1.AppHostOriginal/AspireApp1.AppHostOriginal.csproj
@@ -1,20 +1,21 @@
-
-
-
- Exe
- net8.0
- enable
- enable
- true
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj b/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj
index 853ab0a5..7fd91ef2 100644
--- a/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj
+++ b/src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj
@@ -1,49 +1,49 @@
-
-
-
- enable
- Aspire extension to start a WireMock.Net server to stub an api.
- WireMock.Net.Aspire
- Stef Heyenrath
- net8.0
- true
- WireMock.Net.Aspire
- WireMock.Net.Aspire
- dotnet;aspire;wiremock;extension
- {B6269AAC-170A-4346-8B9A-579DED3D9A12}
- true
- $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
- true
- true
- true
- ../WireMock.Net/WireMock.Net.ruleset
- true
- ../WireMock.Net/WireMock.Net.snk
- true
- MIT
- WireMock.Net-LogoAspire.png
- ../../resources/WireMock.Net-LogoAspire.ico
-
-
-
-
-
-
-
-
-
-
-
-
- true
-
-
-
-
-
-
-
-
-
-
+
+
+
+ enable
+ Aspire extension to start a WireMock.Net server to stub an api.
+ WireMock.Net.Aspire
+ Stef Heyenrath
+ net8.0
+ true
+ WireMock.Net.Aspire
+ WireMock.Net.Aspire
+ dotnet;aspire;wiremock;extension
+ {B6269AAC-170A-4346-8B9A-579DED3D9A12}
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+ true
+ true
+ true
+ ../WireMock.Net/WireMock.Net.ruleset
+ true
+ ../WireMock.Net/WireMock.Net.snk
+ true
+ MIT
+ WireMock.Net-LogoAspire.png
+ ../../resources/WireMock.Net-LogoAspire.ico
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/WireMock.Net.Aspire/WireMockInspector.cs b/src/WireMock.Net.Aspire/WireMockInspector.cs
new file mode 100644
index 00000000..7ea033ef
--- /dev/null
+++ b/src/WireMock.Net.Aspire/WireMockInspector.cs
@@ -0,0 +1,43 @@
+using System.Diagnostics;
+using System.Runtime.CompilerServices;
+
+namespace Aspire.Hosting.WireMock;
+
+internal static class WireMockInspector
+{
+ ///
+ /// Opens the WireMockInspector tool to inspect the WireMock server.
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// Copy of
+ /// without requestFilters and no call to WaitForExit() method in the process so it doesn't block the caller.
+ ///
+ public static void Inspect(string wireMockUrl, [CallerMemberName] string title = "")
+ {
+ try
+ {
+ var arguments = $"attach --adminUrl {wireMockUrl} --autoLoad --instanceName \"{title}\"";
+
+ Process.Start(new ProcessStartInfo
+ {
+ FileName = "wiremockinspector",
+ Arguments = arguments,
+ UseShellExecute = false
+ });
+ }
+ catch (Exception e)
+ {
+ throw new InvalidOperationException
+ (
+ message: @"Cannot find installation of WireMockInspector.
+Execute the following command to install WireMockInspector dotnet tool:
+> dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
+To get more info please visit https://github.com/WireMock-Net/WireMockInspector",
+ innerException: e
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs b/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs
index c5e85b32..bd5df90b 100644
--- a/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs
+++ b/src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs
@@ -1,175 +1,222 @@
-// Copyright © WireMock.Net
-
-using Aspire.Hosting.ApplicationModel;
-using Aspire.Hosting.Lifecycle;
-using Stef.Validation;
-using WireMock.Client.Builders;
-using WireMock.Net.Aspire;
-
-// ReSharper disable once CheckNamespace
-namespace Aspire.Hosting;
-
-///
-/// Provides extension methods for adding WireMock.Net Server resources to the application model.
-///
-public static class WireMockServerBuilderExtensions
-{
- // Linux only (https://github.com/dotnet/aspire/issues/854)
- private const string DefaultLinuxImage = "sheyenrath/wiremock.net-alpine";
- private const string DefaultLinuxMappingsPath = "/app/__admin/mappings";
-
- ///
- /// Adds a WireMock.Net Server resource to the application model.
- ///
- /// The .
- /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
- /// The HTTP port for the WireMock Server.
- /// A reference to the .
- public static IResourceBuilder AddWireMock(this IDistributedApplicationBuilder builder, string name, int? port = null)
- {
- Guard.NotNull(builder);
- Guard.NotNullOrWhiteSpace(name);
- Guard.Condition(port, p => p is null or > 0 and <= ushort.MaxValue);
-
- return builder.AddWireMock(name, callback =>
- {
- callback.HttpPort = port;
- });
- }
-
- ///
- /// Adds a WireMock.Net Server resource to the application model.
- ///
- /// The .
- /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
- /// The arguments to start the WireMock.Net Server.
- /// A reference to the .
- public static IResourceBuilder AddWireMock(this IDistributedApplicationBuilder builder, string name, WireMockServerArguments arguments)
- {
- Guard.NotNull(builder);
- Guard.NotNullOrWhiteSpace(name);
- Guard.NotNull(arguments);
-
- var wireMockContainerResource = new WireMockServerResource(name, arguments);
- var resourceBuilder = builder
- .AddResource(wireMockContainerResource)
- .WithImage(DefaultLinuxImage)
- .WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher
- .WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort);
-
- if (!string.IsNullOrEmpty(arguments.MappingsPath))
- {
- resourceBuilder = resourceBuilder.WithBindMount(arguments.MappingsPath, DefaultLinuxMappingsPath);
- }
-
- resourceBuilder = resourceBuilder.WithArgs(ctx =>
- {
- foreach (var arg in arguments.GetArgs())
- {
- ctx.Args.Add(arg);
- }
- });
-
- return resourceBuilder;
- }
-
- ///
- /// Adds a WireMock.Net Server resource to the application model.
- ///
- /// The .
- /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
- /// A callback that allows for setting the .
- /// A reference to the .
- public static IResourceBuilder AddWireMock(this IDistributedApplicationBuilder builder, string name, Action callback)
- {
- Guard.NotNull(builder);
- Guard.NotNullOrWhiteSpace(name);
- Guard.NotNull(callback);
-
- var arguments = new WireMockServerArguments();
- callback(arguments);
-
- return builder.AddWireMock(name, arguments);
- }
-
- ///
- /// Defines if the static mappings should be read at startup.
- ///
- /// Default set to false.
- ///
- /// A reference to the .
- public static IResourceBuilder WithReadStaticMappings(this IResourceBuilder wiremock)
- {
- Guard.NotNull(wiremock).Resource.Arguments.ReadStaticMappings = true;
- return wiremock;
- }
-
- ///
- /// Watch the static mapping files + folder for changes when running.
- ///
- /// Default set to false.
- ///
- /// A reference to the .
- public static IResourceBuilder WithWatchStaticMappings(this IResourceBuilder wiremock)
- {
- Guard.NotNull(wiremock).Resource.Arguments.WatchStaticMappings = true;
- return wiremock;
- }
-
- ///
- /// Specifies the path for the (static) mapping json files.
- ///
- /// The .
- /// The local path.
- /// A reference to the .
- public static IResourceBuilder WithMappingsPath(this IResourceBuilder wiremock, string mappingsPath)
- {
- Guard.NotNullOrWhiteSpace(mappingsPath);
- Guard.NotNull(wiremock).Resource.Arguments.MappingsPath = mappingsPath;
-
- return wiremock.WithBindMount(mappingsPath, DefaultLinuxMappingsPath);
- }
-
- ///
- /// Set the admin username and password for accessing the admin interface from WireMock.Net via HTTP.
- ///
- /// The .
- /// The admin username.
- /// The admin password.
- /// A reference to the .
- public static IResourceBuilder WithAdminUserNameAndPassword(this IResourceBuilder wiremock, string username, string password)
- {
- Guard.NotNull(wiremock);
-
- wiremock.Resource.Arguments.AdminUsername = Guard.NotNull(username);
- wiremock.Resource.Arguments.AdminPassword = Guard.NotNull(password);
- return wiremock;
- }
-
- ///
- /// Use WireMock Client's AdminApiMappingBuilder to configure the WireMock.Net resource.
- ///
- /// The .
- /// Delegate that will be invoked to configure the WireMock.Net resource.
- ///
- public static IResourceBuilder WithApiMappingBuilder(this IResourceBuilder wiremock, Func configure)
- {
- return wiremock.WithApiMappingBuilder((adminApiMappingBuilder, _) => configure.Invoke(adminApiMappingBuilder));
- }
-
- ///
- /// Use WireMock Client's AdminApiMappingBuilder to configure the WireMock.Net resource.
- ///
- /// The .
- /// Delegate that will be invoked to configure the WireMock.Net resource.
- ///
- public static IResourceBuilder WithApiMappingBuilder(this IResourceBuilder wiremock, Func configure)
- {
- Guard.NotNull(wiremock);
-
- wiremock.ApplicationBuilder.Services.TryAddLifecycleHook();
- wiremock.Resource.Arguments.ApiMappingBuilder = configure;
-
- return wiremock;
- }
+// Copyright © WireMock.Net
+
+using Aspire.Hosting.ApplicationModel;
+using Aspire.Hosting.Lifecycle;
+using Aspire.Hosting.WireMock;
+using Microsoft.Extensions.Diagnostics.HealthChecks;
+using Stef.Validation;
+using WireMock.Client.Builders;
+using WireMock.Net.Aspire;
+
+// ReSharper disable once CheckNamespace
+namespace Aspire.Hosting;
+
+///
+/// Provides extension methods for adding WireMock.Net Server resources to the application model.
+///
+public static class WireMockServerBuilderExtensions
+{
+ // Linux only (https://github.com/dotnet/aspire/issues/854)
+ private const string DefaultLinuxImage = "sheyenrath/wiremock.net-alpine";
+ private const string DefaultLinuxMappingsPath = "/app/__admin/mappings";
+
+ ///
+ /// Adds a WireMock.Net Server resource to the application model.
+ ///
+ /// The .
+ /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
+ /// The HTTP port for the WireMock Server.
+ /// A reference to the .
+ public static IResourceBuilder AddWireMock(this IDistributedApplicationBuilder builder, string name, int? port = null)
+ {
+ Guard.NotNull(builder);
+ Guard.NotNullOrWhiteSpace(name);
+ Guard.Condition(port, p => p is null or > 0 and <= ushort.MaxValue);
+
+ return builder.AddWireMock(name, callback =>
+ {
+ callback.HttpPort = port;
+ });
+ }
+
+ ///
+ /// Adds a WireMock.Net Server resource to the application model.
+ ///
+ /// The .
+ /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
+ /// The arguments to start the WireMock.Net Server.
+ /// A reference to the .
+ public static IResourceBuilder AddWireMock(this IDistributedApplicationBuilder builder, string name, WireMockServerArguments arguments)
+ {
+ Guard.NotNull(builder);
+ Guard.NotNullOrWhiteSpace(name);
+ Guard.NotNull(arguments);
+
+ var wireMockContainerResource = new WireMockServerResource(name, arguments);
+ var resourceBuilder = builder
+ .AddResource(wireMockContainerResource)
+ .WithImage(DefaultLinuxImage)
+ .WithEnvironment(ctx => ctx.EnvironmentVariables.Add("DOTNET_USE_POLLING_FILE_WATCHER", "1")) // https://khalidabuhakmeh.com/aspnet-docker-gotchas-and-workarounds#configuration-reloads-and-filesystemwatcher
+ .WithHttpEndpoint(port: arguments.HttpPort, targetPort: WireMockServerArguments.HttpContainerPort)
+ .WithWireMockInspectorCommand();
+
+ if (!string.IsNullOrEmpty(arguments.MappingsPath))
+ {
+ resourceBuilder = resourceBuilder.WithBindMount(arguments.MappingsPath, DefaultLinuxMappingsPath);
+ }
+
+ resourceBuilder = resourceBuilder.WithArgs(ctx =>
+ {
+ foreach (var arg in arguments.GetArgs())
+ {
+ ctx.Args.Add(arg);
+ }
+ });
+
+ return resourceBuilder;
+ }
+
+ ///
+ /// Adds a WireMock.Net Server resource to the application model.
+ ///
+ /// The .
+ /// The name of the resource. This name will be used as the connection string name when referenced in a dependency.
+ /// A callback that allows for setting the .
+ /// A reference to the .
+ public static IResourceBuilder AddWireMock(this IDistributedApplicationBuilder builder, string name, Action callback)
+ {
+ Guard.NotNull(builder);
+ Guard.NotNullOrWhiteSpace(name);
+ Guard.NotNull(callback);
+
+ var arguments = new WireMockServerArguments();
+ callback(arguments);
+
+ return builder.AddWireMock(name, arguments);
+ }
+
+ ///
+ /// Defines if the static mappings should be read at startup.
+ ///
+ /// Default set to false.
+ ///
+ /// A reference to the .
+ public static IResourceBuilder WithReadStaticMappings(this IResourceBuilder wiremock)
+ {
+ Guard.NotNull(wiremock).Resource.Arguments.ReadStaticMappings = true;
+ return wiremock;
+ }
+
+ ///
+ /// Watch the static mapping files + folder for changes when running.
+ ///
+ /// Default set to false.
+ ///
+ /// A reference to the .
+ public static IResourceBuilder WithWatchStaticMappings(this IResourceBuilder wiremock)
+ {
+ Guard.NotNull(wiremock).Resource.Arguments.WatchStaticMappings = true;
+ return wiremock;
+ }
+
+ ///
+ /// Specifies the path for the (static) mapping json files.
+ ///
+ /// The .
+ /// The local path.
+ /// A reference to the .
+ public static IResourceBuilder WithMappingsPath(this IResourceBuilder wiremock, string mappingsPath)
+ {
+ Guard.NotNullOrWhiteSpace(mappingsPath);
+ Guard.NotNull(wiremock).Resource.Arguments.MappingsPath = mappingsPath;
+
+ return wiremock.WithBindMount(mappingsPath, DefaultLinuxMappingsPath);
+ }
+
+ ///
+ /// Set the admin username and password for accessing the admin interface from WireMock.Net via HTTP.
+ ///
+ /// The .
+ /// The admin username.
+ /// The admin password.
+ /// A reference to the .
+ public static IResourceBuilder WithAdminUserNameAndPassword(this IResourceBuilder wiremock, string username, string password)
+ {
+ Guard.NotNull(wiremock);
+
+ wiremock.Resource.Arguments.AdminUsername = Guard.NotNull(username);
+ wiremock.Resource.Arguments.AdminPassword = Guard.NotNull(password);
+ return wiremock;
+ }
+
+ ///
+ /// Use WireMock Client's AdminApiMappingBuilder to configure the WireMock.Net resource.
+ ///
+ /// The .
+ /// Delegate that will be invoked to configure the WireMock.Net resource.
+ ///
+ public static IResourceBuilder WithApiMappingBuilder(this IResourceBuilder wiremock, Func configure)
+ {
+ return wiremock.WithApiMappingBuilder((adminApiMappingBuilder, _) => configure.Invoke(adminApiMappingBuilder));
+ }
+
+ ///
+ /// Use WireMock Client's AdminApiMappingBuilder to configure the WireMock.Net resource.
+ ///
+ /// The .
+ /// Delegate that will be invoked to configure the WireMock.Net resource.
+ ///
+ public static IResourceBuilder WithApiMappingBuilder(this IResourceBuilder wiremock, Func configure)
+ {
+ Guard.NotNull(wiremock);
+
+ wiremock.ApplicationBuilder.Services.TryAddLifecycleHook();
+ wiremock.Resource.Arguments.ApiMappingBuilder = configure;
+
+ return wiremock;
+ }
+
+ ///
+ /// Enables the WireMockInspect, a cross-platform UI app that facilitates WireMock troubleshooting.
+ /// This requires installation of the WireMockInspector tool.
+ ///
+ /// dotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources
+ ///
+ ///
+ /// The .
+ ///
+ public static IResourceBuilder WithWireMockInspectorCommand(this IResourceBuilder builder)
+ {
+ Guard.NotNull(builder);
+
+ CommandOptions commandOptions = new()
+ {
+ Description = "Requires installation of the WireMockInspector (https://github.com/WireMock-Net/WireMockInspector) tool:\ndotnet tool install WireMockInspector --global --no-cache --ignore-failed-sources",
+ UpdateState = OnUpdateResourceState,
+ IconName = "BoxSearch",
+ IconVariant = IconVariant.Filled
+ };
+
+ builder.WithCommand(
+ name: "wiremock-inspector",
+ displayName: "WireMock Inspector",
+ executeCommand: context => OnRunOpenInspectorCommandAsync(builder),
+ commandOptions: commandOptions);
+
+ return builder;
+ }
+
+ private static Task OnRunOpenInspectorCommandAsync(IResourceBuilder builder)
+ {
+ WireMockInspector.Inspect(builder.Resource.GetEndpoint().Url);
+
+ return Task.FromResult(CommandResults.Success());
+ }
+
+ private static ResourceCommandState OnUpdateResourceState(UpdateCommandStateContext context)
+ {
+ return context.ResourceSnapshot.HealthStatus is HealthStatus.Healthy
+ ? ResourceCommandState.Enabled
+ : ResourceCommandState.Disabled;
+ }
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj b/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj
index bd2cf52a..16d5e97c 100644
--- a/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj
+++ b/test/WireMock.Net.Aspire.TestAppHost/WireMock.Net.Aspire.TestAppHost.csproj
@@ -1,30 +1,31 @@
-
-
-
- Exe
- net8.0
- enable
- enable
- true
-
- true
- ../../src/WireMock.Net/WireMock.Net.snk
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
- PreserveNewest
-
-
-
+
+
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+ true
+ ../../src/WireMock.Net/WireMock.Net.snk
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+
+
+
\ No newline at end of file
diff --git a/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj b/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj
index 246c1c61..51a115cf 100644
--- a/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj
+++ b/test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj
@@ -1,46 +1,48 @@
-
-
-
- net8.0
- enable
- enable
- false
- true
- true
- true
- ../../src/WireMock.Net/WireMock.Net.snk
- true
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+ net8.0
+ enable
+ enable
+ false
+ true
+ true
+ true
+ ../../src/WireMock.Net/WireMock.Net.snk
+ true
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/WireMock.Net.Aspire.Tests/WireMockServerBuilderExtensionsTests.cs b/test/WireMock.Net.Aspire.Tests/WireMockServerBuilderExtensionsTests.cs
index cad18dea..a2c1f15d 100644
--- a/test/WireMock.Net.Aspire.Tests/WireMockServerBuilderExtensionsTests.cs
+++ b/test/WireMock.Net.Aspire.Tests/WireMockServerBuilderExtensionsTests.cs
@@ -1,96 +1,98 @@
-// 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();
-
- // Act
- Action act = () => builder.AddWireMock(name!, 12345);
-
- // Assert
- act.Should().Throw();
- }
-
- [Fact]
- public void AddWireMock_WithInvalidPort_ShouldThrowArgumentOutOfRangeException()
- {
- // Arrange
- const int invalidPort = -1;
- var builder = Mock.Of();
-
- // Act
- Action act = () => builder.AddWireMock("ValidName", invalidPort);
-
- // Assert
- act.Should().Throw().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,
- WatchStaticMappings = false,
- MappingsPath = null,
- HttpPort = port
- });
- wiremock.Resource.Annotations.Should().HaveCount(4);
-
- var containerImageAnnotation = wiremock.Resource.Annotations.OfType().FirstOrDefault();
- containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
- {
- Image = "sheyenrath/wiremock.net-alpine",
- Registry = null,
- Tag = "latest"
- });
-
- var endpointAnnotation = wiremock.Resource.Annotations.OfType().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().FirstOrDefault().Should().NotBeNull();
-
- wiremock.Resource.Annotations.OfType().FirstOrDefault().Should().NotBeNull();
- }
+// 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();
+
+ // Act
+ Action act = () => builder.AddWireMock(name!, 12345);
+
+ // Assert
+ act.Should().Throw();
+ }
+
+ [Fact]
+ public void AddWireMock_WithInvalidPort_ShouldThrowArgumentOutOfRangeException()
+ {
+ // Arrange
+ const int invalidPort = -1;
+ var builder = Mock.Of();
+
+ // Act
+ Action act = () => builder.AddWireMock("ValidName", invalidPort);
+
+ // Assert
+ act.Should().Throw().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,
+ WatchStaticMappings = false,
+ MappingsPath = null,
+ HttpPort = port
+ });
+ wiremock.Resource.Annotations.Should().HaveCount(5);
+
+ var containerImageAnnotation = wiremock.Resource.Annotations.OfType().FirstOrDefault();
+ containerImageAnnotation.Should().BeEquivalentTo(new ContainerImageAnnotation
+ {
+ Image = "sheyenrath/wiremock.net-alpine",
+ Registry = null,
+ Tag = "latest"
+ });
+
+ var endpointAnnotation = wiremock.Resource.Annotations.OfType().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().FirstOrDefault().Should().NotBeNull();
+
+ wiremock.Resource.Annotations.OfType().FirstOrDefault().Should().NotBeNull();
+
+ wiremock.Resource.Annotations.OfType().FirstOrDefault().Should().NotBeNull();
+ }
}
\ No newline at end of file