mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-01-11 14:20:29 +01:00
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:
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -19,9 +19,8 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 'Run Unit Tests'
|
||||
run: |
|
||||
dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
|
||||
- name: 'Execute Tests'
|
||||
run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
|
||||
|
||||
linux-build-and-run:
|
||||
name: Run Tests on Linux
|
||||
@@ -33,6 +32,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 'Run Unit Tests'
|
||||
run: |
|
||||
dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
|
||||
- name: 'Execute Tests'
|
||||
run: dotnet test './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj' -c Release --framework net8.0
|
||||
|
||||
- name: Install .NET Aspire workload
|
||||
run: dotnet workload install aspire
|
||||
|
||||
- name: 'Execute .NET Aspire Tests'
|
||||
run: dotnet test './test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj' -c Release
|
||||
@@ -1,5 +1,5 @@
|
||||
## WireMock.Net
|
||||
Lightweight Http Mocking Server for .NET, inspired by [WireMock(http://WireMock.org) from the Java landscape.
|
||||
Lightweight Http Mocking Server for .NET, inspired by WireMock.org (from the Java landscape).
|
||||
|
||||
### :star: Key Features
|
||||
* HTTP response stubbing, matchable on URL/Path, headers, cookies and body content patterns
|
||||
@@ -11,6 +11,7 @@ Lightweight Http Mocking Server for .NET, inspired by [WireMock(http://WireMock.
|
||||
* Stateful behaviour simulation
|
||||
* Response templating / transformation using Handlebars and extensions
|
||||
* Can be used locally or in CI/CD scenarios
|
||||
* Can be used for Aspire Distributed Application testing
|
||||
|
||||
### :star: Stubbing
|
||||
A core feature of WireMock.Net is the ability to return predefined HTTP responses for requests matching criteria.
|
||||
@@ -32,6 +33,12 @@ WireMock.Net can be used in several ways:
|
||||
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).
|
||||
|
||||
### Unit/Integration Testing using Testcontainers.DotNet
|
||||
See [Wiki : WireMock.Net.Testcontainers](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
|
||||
### Unit/Integration Testing using an an Aspire Distributed Application
|
||||
See [Wiki : WireMock.Net.Aspire](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock.Net.Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
|
||||
|
||||
#### 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).
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
||||
* Stateful behaviour simulation
|
||||
* Response templating / transformation using Handlebars and extensions
|
||||
* Can be used locally or in CI/CD scenarios
|
||||
* Can be used for Aspire Distributed Application testing
|
||||
|
||||
## :memo: Blogs
|
||||
- [mstack.nl : Generate C# Code from Mapping(s)](https://mstack.nl/blog/20230201-wiremock.net-tocode/)
|
||||
@@ -46,6 +47,7 @@ For more info, see also this WIKI page: [What is WireMock.Net](https://github.co
|
||||
| **WireMock.Net.RestClient** | [](https://www.nuget.org/packages/WireMock.Net.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.RestClient)
|
||||
| **WireMock.Net.xUnit** | [](https://www.nuget.org/packages/WireMock.Net.xUnit) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.xUnit)
|
||||
| **WireMock.Net.Testcontainers** | [](https://www.nuget.org/packages/WireMock.Net.Testcontainers) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Testcontainers)
|
||||
| **WireMock.Net.Aspire** | [](https://www.nuget.org/packages/WireMock.Net.Aspire) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Net.Aspire)
|
||||
| **WireMock.Org.RestClient** | [](https://www.nuget.org/packages/WireMock.Org.RestClient) | [](https://www.myget.org/feed/wiremock-net/package/nuget/WireMock.Org.RestClient)
|
||||
|
||||
|
||||
@@ -73,7 +75,10 @@ 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).
|
||||
|
||||
### 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.
|
||||
See [Wiki : WireMock.Net.Testcontainers](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock.Net.Testcontainers) on how to build a WireMock.Net Docker container which can be used in Unit/Integration testing.
|
||||
|
||||
### Unit/Integration Testing using an an Aspire Distributed Application
|
||||
See [Wiki : WireMock.Net.Aspire](https://github.com/WireMock-Net/WireMock.Net/wiki/Using-WireMock.Net.Aspire) on how to use WireMock.Net as an Aspire Hosted application to do Unit/Integration testing.
|
||||
|
||||
### 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).
|
||||
|
||||
@@ -113,6 +113,24 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMockAzureQueueProxy", "
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Console.GrpcClient", "examples\WireMock.Net.Console.GrpcClient\WireMock.Net.Console.GrpcClient.csproj", "{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples - Aspire", "examples - Aspire", "{AD474543-0715-49F2-A284-936B060BF736}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Aspire", "src\WireMock.Net.Aspire\WireMock.Net.Aspire.csproj", "{CAB42D88-B4E4-4887-B684-9F3E09D085A1}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.ServiceDefaults", "examples-Aspire\AspireApp1.ServiceDefaults\AspireApp1.ServiceDefaults.csproj", "{42113E6B-DC43-4E80-9967-1E4233568E87}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.ApiService", "examples-Aspire\AspireApp1.ApiService\AspireApp1.ApiService.csproj", "{84624E1F-DF07-4315-89B0-51776BE99E13}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.Web", "examples-Aspire\AspireApp1.Web\AspireApp1.Web.csproj", "{A34F1575-7C33-4548-8CEF-8D8D8B84153C}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.AppHost", "examples-Aspire\AspireApp1.AppHost\AspireApp1.AppHost.csproj", "{7373B7DC-47ED-45A5-969D-D7DDBA529B53}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Aspire.Tests", "test\WireMock.Net.Aspire.Tests\WireMock.Net.Aspire.Tests.csproj", "{CE602F57-FEF8-4559-A9E0-6200BE1BF398}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WireMock.Net.Aspire.TestAppHost", "test\WireMock.Net.Aspire.TestAppHost\WireMock.Net.Aspire.TestAppHost.csproj", "{F1B5999D-D22E-48A6-AB86-18A7876BD32E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspireApp1.AppHostOriginal", "examples-Aspire\AspireApp1.AppHostOriginal\AspireApp1.AppHostOriginal.csproj", "{C9210DA3-F390-4598-8512-349A473FE9C9}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
@@ -267,6 +285,38 @@ Global
|
||||
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CAB42D88-B4E4-4887-B684-9F3E09D085A1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{42113E6B-DC43-4E80-9967-1E4233568E87}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{42113E6B-DC43-4E80-9967-1E4233568E87}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{42113E6B-DC43-4E80-9967-1E4233568E87}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{84624E1F-DF07-4315-89B0-51776BE99E13}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{84624E1F-DF07-4315-89B0-51776BE99E13}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{84624E1F-DF07-4315-89B0-51776BE99E13}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A34F1575-7C33-4548-8CEF-8D8D8B84153C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{7373B7DC-47ED-45A5-969D-D7DDBA529B53}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{CE602F57-FEF8-4559-A9E0-6200BE1BF398}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F1B5999D-D22E-48A6-AB86-18A7876BD32E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C9210DA3-F390-4598-8512-349A473FE9C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C9210DA3-F390-4598-8512-349A473FE9C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C9210DA3-F390-4598-8512-349A473FE9C9}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
@@ -311,6 +361,14 @@ Global
|
||||
{1EA72C0F-92E9-486B-8FFE-53F992BFC4AA} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{7FC0B409-2682-40EE-B3B9-3930D6769D01} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{B1580A38-84E7-44BE-8FE7-3EE5031D74A1} = {985E0ADB-D4B4-473A-AA40-567E279B7946}
|
||||
{CAB42D88-B4E4-4887-B684-9F3E09D085A1} = {8F890C6F-9ACC-438D-928A-AD61CDA862F2}
|
||||
{42113E6B-DC43-4E80-9967-1E4233568E87} = {AD474543-0715-49F2-A284-936B060BF736}
|
||||
{84624E1F-DF07-4315-89B0-51776BE99E13} = {AD474543-0715-49F2-A284-936B060BF736}
|
||||
{A34F1575-7C33-4548-8CEF-8D8D8B84153C} = {AD474543-0715-49F2-A284-936B060BF736}
|
||||
{7373B7DC-47ED-45A5-969D-D7DDBA529B53} = {AD474543-0715-49F2-A284-936B060BF736}
|
||||
{CE602F57-FEF8-4559-A9E0-6200BE1BF398} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{F1B5999D-D22E-48A6-AB86-18A7876BD32E} = {0BB8B634-407A-4610-A91F-11586990767A}
|
||||
{C9210DA3-F390-4598-8512-349A473FE9C9} = {AD474543-0715-49F2-A284-936B060BF736}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {DC539027-9852-430C-B19F-FD035D018458}
|
||||
|
||||
@@ -14,10 +14,14 @@ jobs:
|
||||
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
|
||||
dotnet tool install --global coverlet.console
|
||||
displayName: 'Install dotnet tools'
|
||||
|
||||
- task: PowerShell@2
|
||||
@@ -31,10 +35,10 @@ jobs:
|
||||
- script: |
|
||||
dotnet dev-certs https --trust || true
|
||||
displayName: 'dotnet dev-certs https'
|
||||
|
||||
# See: https://docs.sonarsource.com/sonarcloud/enriching/test-coverage/dotnet-test-coverage
|
||||
|
||||
# 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.dotnet.excludeTestProjects=true /d:sonar.cs.vscoveragexml.reportsPaths=**/wiremock-coverage.xml /d:sonar.verbose=true
|
||||
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.dotnet.excludeTestProjects=true /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
|
||||
|
||||
@@ -42,14 +46,21 @@ jobs:
|
||||
displayName: 'Build Unit tests'
|
||||
inputs:
|
||||
command: 'build'
|
||||
projects: './test/WireMock.Net.Tests/WireMock.Net.Tests.csproj'
|
||||
arguments: '--configuration Debug --framework net8.0 --no-incremental'
|
||||
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.xml"'
|
||||
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-1.xml"
|
||||
dotnet-coverage collect "dotnet test ./test/WireMock.Net.Aspire.Tests/WireMock.Net.Aspire.Tests.csproj --configuration Debug --no-build" -f xml -o "wiremock-coverage-2.xml"
|
||||
displayName: 'Execute Unit 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'
|
||||
@@ -60,8 +71,8 @@ jobs:
|
||||
condition: and(succeeded(), eq(variables['RUN_WHITESOURCE'], 'yes'))
|
||||
|
||||
- script: |
|
||||
bash <(curl https://codecov.io/bash) -t $(CODECOV_TOKEN) -f ./test/WireMock.Net.Tests/coverage.8.0.opencover.xml
|
||||
displayName: 'codecov'
|
||||
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'))
|
||||
@@ -70,7 +81,7 @@ jobs:
|
||||
testResultsFiles: '**/*.trx'
|
||||
|
||||
- task: PublishBuildArtifacts@1
|
||||
displayName: Publish coverage file
|
||||
displayName: Publish coverage files
|
||||
inputs:
|
||||
PathtoPublish: './test/WireMock.Net.Tests/coverage.net8.0.opencover.xml'
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AspireApp1.ServiceDefaults\AspireApp1.ServiceDefaults.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
52
examples-Aspire/AspireApp1.ApiService/Program.cs
Normal file
52
examples-Aspire/AspireApp1.ApiService/Program.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add service defaults & Aspire components.
|
||||
builder.AddServiceDefaults();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddProblemDetails();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
app.UseExceptionHandler();
|
||||
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
app.MapGet("/weatherforecast", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
});
|
||||
|
||||
app.MapGet("/weatherforecast2", () =>
|
||||
{
|
||||
var forecast = Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray();
|
||||
return forecast;
|
||||
});
|
||||
|
||||
app.MapDefaultEndpoints();
|
||||
|
||||
app.Run();
|
||||
|
||||
record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "weatherforecast",
|
||||
"applicationUrl": "http://localhost:5433",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "weatherforecast",
|
||||
"applicationUrl": "https://localhost:7365;http://localhost:5433",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
examples-Aspire/AspireApp1.ApiService/appsettings.json
Normal file
9
examples-Aspire/AspireApp1.ApiService/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
23
examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj
Normal file
23
examples-Aspire/AspireApp1.AppHost/AspireApp1.AppHost.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAspireHost>true</IsAspireHost>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AspireApp1.ApiService\AspireApp1.ApiService.csproj" />
|
||||
<ProjectReference Include="..\AspireApp1.Web\AspireApp1.Web.csproj" />
|
||||
|
||||
<!-- https://learn.microsoft.com/en-us/dotnet/aspire/extensibility/custom-resources?tabs=windows#create-library-for-resource-extension -->
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Aspire\WireMock.Net.Aspire.csproj" IsAspireProjectResource="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
21
examples-Aspire/AspireApp1.AppHost/Program.cs
Normal file
21
examples-Aspire/AspireApp1.AppHost/Program.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using AspireApp1.AppHost;
|
||||
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
//IResourceBuilder<ProjectResource> apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
|
||||
|
||||
var mappingsPath = Path.Combine(Directory.GetCurrentDirectory(), "WireMockMappings");
|
||||
|
||||
Console.WriteLine($"MappingsPath: {mappingsPath}");
|
||||
|
||||
var wiremock = builder
|
||||
.AddWireMock("apiservice", WireMockServerArguments.DefaultPort)
|
||||
.WithMappingsPath(mappingsPath)
|
||||
.WithReadStaticMappings()
|
||||
.WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
|
||||
|
||||
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
|
||||
.WithExternalHttpEndpoints()
|
||||
.WithReference(wiremock);
|
||||
|
||||
builder.Build().Run();
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:17194;http://localhost:15256",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21232",
|
||||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22019"
|
||||
}
|
||||
},
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:15256",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19163",
|
||||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20086"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
examples-Aspire/AspireApp1.AppHost/WeatherForecastApiMock.cs
Normal file
36
examples-Aspire/AspireApp1.AppHost/WeatherForecastApiMock.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using WireMock.Client.Builders;
|
||||
|
||||
namespace AspireApp1.AppHost;
|
||||
|
||||
internal class WeatherForecastApiMock
|
||||
{
|
||||
public static async Task BuildAsync(AdminApiMappingBuilder builder)
|
||||
{
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
builder.Given(b => b
|
||||
.WithRequest(request => request
|
||||
.UsingGet()
|
||||
.WithPath("/weatherforecast2")
|
||||
)
|
||||
.WithResponse(response => response
|
||||
.WithHeaders(h => h.Add("Content-Type", "application/json"))
|
||||
.WithBodyAsJson(() => Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray())
|
||||
)
|
||||
);
|
||||
|
||||
await builder.BuildAndPostAsync();
|
||||
}
|
||||
}
|
||||
|
||||
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"Guid": "873d495f-940e-4b86-a1f4-4f0fc7be8b8b",
|
||||
"Request": {
|
||||
"Path": "/weatherforecast",
|
||||
"Methods": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"Response": {
|
||||
"BodyAsJson": [
|
||||
{
|
||||
"date": "2024-05-24",
|
||||
"temperatureC": -17,
|
||||
"summary": "Balmy"
|
||||
},
|
||||
{
|
||||
"date": "2024-05-25",
|
||||
"temperatureC": -13,
|
||||
"summary": "Mild"
|
||||
},
|
||||
{
|
||||
"date": "2024-05-26",
|
||||
"temperatureC": 31,
|
||||
"summary": "Bracing"
|
||||
},
|
||||
{
|
||||
"date": "2024-05-27",
|
||||
"temperatureC": 6,
|
||||
"summary": "Hot"
|
||||
},
|
||||
{
|
||||
"date": "2024-05-28",
|
||||
"temperatureC": -2,
|
||||
"summary": "Mild"
|
||||
}
|
||||
],
|
||||
"Headers": {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
examples-Aspire/AspireApp1.AppHost/appsettings.json
Normal file
9
examples-Aspire/AspireApp1.AppHost/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Aspire.Hosting.Dcp": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAspireHost>true</IsAspireHost>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AspireApp1.ApiService\AspireApp1.ApiService.csproj" />
|
||||
<ProjectReference Include="..\AspireApp1.Web\AspireApp1.Web.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
9
examples-Aspire/AspireApp1.AppHostOriginal/Program.cs
Normal file
9
examples-Aspire/AspireApp1.AppHostOriginal/Program.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
IResourceBuilder<ProjectResource> apiService = builder.AddProject<Projects.AspireApp1_ApiService>("apiservice");
|
||||
|
||||
builder.AddProject<Projects.AspireApp1_Web>("webfrontend")
|
||||
.WithExternalHttpEndpoints()
|
||||
.WithReference(apiService);
|
||||
|
||||
builder.Build().Run();
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:17194;http://localhost:15256",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "https://localhost:21232",
|
||||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "https://localhost:22019"
|
||||
}
|
||||
},
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:15256",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"DOTNET_ENVIRONMENT": "Development",
|
||||
"DOTNET_DASHBOARD_OTLP_ENDPOINT_URL": "http://localhost:19163",
|
||||
"DOTNET_RESOURCE_SERVICE_ENDPOINT_URL": "http://localhost:20086"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"Aspire.Hosting.Dcp": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAspireSharedProject>true</IsAspireSharedProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
|
||||
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.3.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="8.0.0" />
|
||||
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.8.1" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.8.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
112
examples-Aspire/AspireApp1.ServiceDefaults/Extensions.cs
Normal file
112
examples-Aspire/AspireApp1.ServiceDefaults/Extensions.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using OpenTelemetry;
|
||||
using OpenTelemetry.Metrics;
|
||||
using OpenTelemetry.Trace;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Microsoft.Extensions.Hosting;
|
||||
|
||||
// Adds common .NET Aspire services: service discovery, resilience, health checks, and OpenTelemetry.
|
||||
// This project should be referenced by each service project in your solution.
|
||||
// To learn more about using this project, see https://aka.ms/dotnet/aspire/service-defaults
|
||||
public static class Extensions
|
||||
{
|
||||
public static IHostApplicationBuilder AddServiceDefaults(this IHostApplicationBuilder builder)
|
||||
{
|
||||
builder.ConfigureOpenTelemetry();
|
||||
|
||||
builder.AddDefaultHealthChecks();
|
||||
|
||||
builder.Services.AddServiceDiscovery();
|
||||
|
||||
builder.Services.ConfigureHttpClientDefaults(http =>
|
||||
{
|
||||
// Turn on resilience by default
|
||||
http.AddStandardResilienceHandler();
|
||||
|
||||
// Turn on service discovery by default
|
||||
http.AddServiceDiscovery();
|
||||
});
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IHostApplicationBuilder ConfigureOpenTelemetry(this IHostApplicationBuilder builder)
|
||||
{
|
||||
builder.Logging.AddOpenTelemetry(logging =>
|
||||
{
|
||||
logging.IncludeFormattedMessage = true;
|
||||
logging.IncludeScopes = true;
|
||||
});
|
||||
|
||||
builder.Services.AddOpenTelemetry()
|
||||
.WithMetrics(metrics =>
|
||||
{
|
||||
metrics.AddAspNetCoreInstrumentation()
|
||||
.AddHttpClientInstrumentation()
|
||||
.AddRuntimeInstrumentation();
|
||||
})
|
||||
.WithTracing(tracing =>
|
||||
{
|
||||
tracing.AddAspNetCoreInstrumentation()
|
||||
// Uncomment the following line to enable gRPC instrumentation (requires the OpenTelemetry.Instrumentation.GrpcNetClient package)
|
||||
//.AddGrpcClientInstrumentation()
|
||||
.AddHttpClientInstrumentation();
|
||||
});
|
||||
|
||||
builder.AddOpenTelemetryExporters();
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private static IHostApplicationBuilder AddOpenTelemetryExporters(this IHostApplicationBuilder builder)
|
||||
{
|
||||
var useOtlpExporter = !string.IsNullOrWhiteSpace(builder.Configuration["OTEL_EXPORTER_OTLP_ENDPOINT"]);
|
||||
|
||||
if (useOtlpExporter)
|
||||
{
|
||||
builder.Services.AddOpenTelemetry().UseOtlpExporter();
|
||||
}
|
||||
|
||||
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
|
||||
//if (!string.IsNullOrEmpty(builder.Configuration["APPLICATIONINSIGHTS_CONNECTION_STRING"]))
|
||||
//{
|
||||
// builder.Services.AddOpenTelemetry()
|
||||
// .UseAzureMonitor();
|
||||
//}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static IHostApplicationBuilder AddDefaultHealthChecks(this IHostApplicationBuilder builder)
|
||||
{
|
||||
builder.Services.AddHealthChecks()
|
||||
// Add a default liveness check to ensure app is responsive
|
||||
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public static WebApplication MapDefaultEndpoints(this WebApplication app)
|
||||
{
|
||||
// Adding health checks endpoints to applications in non-development environments has security implications.
|
||||
// See https://aka.ms/dotnet/aspire/healthchecks for details before enabling these endpoints in non-development environments.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
// All health checks must pass for app to be considered ready to accept traffic after starting
|
||||
app.MapHealthChecks("/health");
|
||||
|
||||
// Only health checks tagged with the "live" tag must pass for app to be considered alive
|
||||
app.MapHealthChecks("/alive", new HealthCheckOptions
|
||||
{
|
||||
Predicate = r => r.Tags.Contains("live")
|
||||
});
|
||||
}
|
||||
|
||||
return app;
|
||||
}
|
||||
}
|
||||
28
examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj
Normal file
28
examples-Aspire/AspireApp1.Tests/AspireApp1.Tests.csproj
Normal file
@@ -0,0 +1,28 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsPackable>false</IsPackable>
|
||||
<IsTestProject>true</IsTestProject>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.Testing" Version="8.0.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
|
||||
<PackageReference Include="xunit" Version="2.5.3" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AspireApp1.AppHost\AspireApp1.AppHost.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Using Include="Aspire.Hosting.Testing" />
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
22
examples-Aspire/AspireApp1.Tests/WebTests.cs
Normal file
22
examples-Aspire/AspireApp1.Tests/WebTests.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using System.Net;
|
||||
|
||||
namespace AspireApp1.Tests;
|
||||
|
||||
public class WebTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task GetWebResourceRootReturnsOkStatusCode()
|
||||
{
|
||||
// Arrange
|
||||
var appHost = await DistributedApplicationTestingBuilder.CreateAsync<Projects.AspireApp1_AppHost>();
|
||||
await using var app = await appHost.BuildAsync();
|
||||
await app.StartAsync();
|
||||
|
||||
// Act
|
||||
var httpClient = app.CreateHttpClient("webfrontend");
|
||||
var response = await httpClient.GetAsync("/");
|
||||
|
||||
// Assert
|
||||
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
|
||||
}
|
||||
}
|
||||
13
examples-Aspire/AspireApp1.Web/AspireApp1.Web.csproj
Normal file
13
examples-Aspire/AspireApp1.Web/AspireApp1.Web.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\AspireApp1.ServiceDefaults\AspireApp1.ServiceDefaults.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
20
examples-Aspire/AspireApp1.Web/Components/App.razor
Normal file
20
examples-Aspire/AspireApp1.Web/Components/App.razor
Normal file
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<base href="/" />
|
||||
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
|
||||
<link rel="stylesheet" href="app.css" />
|
||||
<link rel="stylesheet" href="AspireApp1.Web.styles.css" />
|
||||
<link rel="icon" type="image/png" href="favicon.png" />
|
||||
<HeadOutlet />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<Routes />
|
||||
<script src="_framework/blazor.web.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,23 @@
|
||||
@inherits LayoutComponentBase
|
||||
|
||||
<div class="page">
|
||||
<div class="sidebar">
|
||||
<NavMenu />
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="top-row px-4">
|
||||
<a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
|
||||
</div>
|
||||
|
||||
<article class="content px-4">
|
||||
@Body
|
||||
</article>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<div id="blazor-error-ui">
|
||||
An unhandled error has occurred.
|
||||
<a href="" class="reload">Reload</a>
|
||||
<a class="dismiss">🗙</a>
|
||||
</div>
|
||||
@@ -0,0 +1,96 @@
|
||||
.page {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
main {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
background-color: #f7f7f7;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
justify-content: flex-end;
|
||||
height: 3.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
white-space: nowrap;
|
||||
margin-left: 1.5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.top-row ::deep a:first-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
@media (max-width: 640.98px) {
|
||||
.top-row {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.top-row ::deep a, .top-row ::deep .btn-link {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.page {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
width: 250px;
|
||||
height: 100vh;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.top-row.auth ::deep a:first-child {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.top-row, article {
|
||||
padding-left: 2rem !important;
|
||||
padding-right: 1.5rem !important;
|
||||
}
|
||||
}
|
||||
|
||||
#blazor-error-ui {
|
||||
background: lightyellow;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
|
||||
display: none;
|
||||
left: 0;
|
||||
padding: 0.6rem 1.25rem 0.7rem;
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#blazor-error-ui .dismiss {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 0.75rem;
|
||||
top: 0.5rem;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
<div class="top-row ps-3 navbar navbar-dark">
|
||||
<div class="container-fluid">
|
||||
<a class="navbar-brand" href="">AspireApp1</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="checkbox" title="Navigation menu" class="navbar-toggler" />
|
||||
|
||||
<div class="nav-scrollable" onclick="document.querySelector('.navbar-toggler').click()">
|
||||
<nav class="flex-column">
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="" Match="NavLinkMatch.All">
|
||||
<span class="bi bi-house-door-fill" aria-hidden="true"></span> Home
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
<div class="nav-item px-3">
|
||||
<NavLink class="nav-link" href="weather">
|
||||
<span class="bi bi-list-nested" aria-hidden="true"></span> Weather
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
@@ -0,0 +1,102 @@
|
||||
.navbar-toggler {
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
width: 3.5rem;
|
||||
height: 2.5rem;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0.5rem;
|
||||
right: 1rem;
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
background: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e") no-repeat center/1.75rem rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.navbar-toggler:checked {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.top-row {
|
||||
height: 3.5rem;
|
||||
background-color: rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.bi {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
margin-right: 0.75rem;
|
||||
top: -1px;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bi-house-door-fill {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-house-door-fill' viewBox='0 0 16 16'%3E%3Cpath d='M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-plus-square-fill {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-plus-square-fill' viewBox='0 0 16 16'%3E%3Cpath d='M2 0a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V2a2 2 0 0 0-2-2H2zm6.5 4.5v3h3a.5.5 0 0 1 0 1h-3v3a.5.5 0 0 1-1 0v-3h-3a.5.5 0 0 1 0-1h3v-3a.5.5 0 0 1 1 0z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.bi-list-nested {
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-list-nested' viewBox='0 0 16 16'%3E%3Cpath fill-rule='evenodd' d='M4.5 11.5A.5.5 0 0 1 5 11h10a.5.5 0 0 1 0 1H5a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 3 7h10a.5.5 0 0 1 0 1H3a.5.5 0 0 1-.5-.5zm-2-4A.5.5 0 0 1 1 3h10a.5.5 0 0 1 0 1H1a.5.5 0 0 1-.5-.5z'/%3E%3C/svg%3E");
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
font-size: 0.9rem;
|
||||
padding-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.nav-item:first-of-type {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nav-item:last-of-type {
|
||||
padding-bottom: 1rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a {
|
||||
color: #d7d7d7;
|
||||
border-radius: 4px;
|
||||
height: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 3rem;
|
||||
}
|
||||
|
||||
.nav-item ::deep a.active {
|
||||
background-color: rgba(255,255,255,0.37);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-item ::deep a:hover {
|
||||
background-color: rgba(255,255,255,0.1);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-toggler:checked ~ .nav-scrollable {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (min-width: 641px) {
|
||||
.navbar-toggler {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.nav-scrollable {
|
||||
/* Never collapse the sidebar for wide screens */
|
||||
display: block;
|
||||
|
||||
/* Allow sidebar to scroll for tall menus */
|
||||
height: calc(100vh - 3.5rem);
|
||||
overflow-y: auto;
|
||||
}
|
||||
}
|
||||
38
examples-Aspire/AspireApp1.Web/Components/Pages/Error.razor
Normal file
38
examples-Aspire/AspireApp1.Web/Components/Pages/Error.razor
Normal file
@@ -0,0 +1,38 @@
|
||||
@page "/Error"
|
||||
@using System.Diagnostics
|
||||
|
||||
<PageTitle>Error</PageTitle>
|
||||
|
||||
<h1 class="text-danger">Error.</h1>
|
||||
<h2 class="text-danger">An error occurred while processing your request.</h2>
|
||||
|
||||
@if (ShowRequestId)
|
||||
{
|
||||
<p>
|
||||
<strong>Request ID:</strong> <code>@requestId</code>
|
||||
</p>
|
||||
}
|
||||
|
||||
<h3>Development Mode</h3>
|
||||
<p>
|
||||
Swapping to <strong>Development</strong> environment will display more detailed information about the error that occurred.
|
||||
</p>
|
||||
<p>
|
||||
<strong>The Development environment shouldn't be enabled for deployed applications.</strong>
|
||||
It can result in displaying sensitive information from exceptions to end users.
|
||||
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong>
|
||||
and restarting the app.
|
||||
</p>
|
||||
|
||||
@code{
|
||||
[CascadingParameter]
|
||||
public HttpContext? HttpContext { get; set; }
|
||||
|
||||
private string? requestId;
|
||||
private bool ShowRequestId => !string.IsNullOrEmpty(requestId);
|
||||
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
requestId = Activity.Current?.Id ?? HttpContext?.TraceIdentifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
@page "/"
|
||||
|
||||
<PageTitle>Home</PageTitle>
|
||||
|
||||
<h1>Hello, world!</h1>
|
||||
|
||||
Welcome to your new app.
|
||||
@@ -0,0 +1,86 @@
|
||||
@page "/weather"
|
||||
@attribute [StreamRendering]
|
||||
@* @attribute [OutputCache(Duration = 5)] *@
|
||||
|
||||
@inject WeatherApiClient WeatherApi
|
||||
@inject WeatherApiClient2 WeatherApi2
|
||||
|
||||
<PageTitle>Weather</PageTitle>
|
||||
|
||||
<h1>Weather in Den Bosch</h1>
|
||||
|
||||
@if (forecasts1 == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts1)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
<h1>Weather in New York</h1>
|
||||
|
||||
@if (forecasts2 == null)
|
||||
{
|
||||
<p><em>Loading...</em></p>
|
||||
}
|
||||
else
|
||||
{
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Temp. (C)</th>
|
||||
<th>Temp. (F)</th>
|
||||
<th>Summary</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var forecast in forecasts2)
|
||||
{
|
||||
<tr>
|
||||
<td>@forecast.Date.ToShortDateString()</td>
|
||||
<td>@forecast.TemperatureC</td>
|
||||
<td>@forecast.TemperatureF</td>
|
||||
<td>@forecast.Summary</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
@code {
|
||||
private WeatherForecast[]? forecasts1;
|
||||
private WeatherForecast[]? forecasts2;
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
var forecastsTask1 = WeatherApi.GetWeatherAsync();
|
||||
var forecastsTask2 = WeatherApi2.GetWeatherAsync();
|
||||
|
||||
await Task.WhenAll(forecastsTask1, forecastsTask2);
|
||||
|
||||
forecasts1 = await forecastsTask1;
|
||||
forecasts2 = await forecastsTask2;
|
||||
}
|
||||
}
|
||||
6
examples-Aspire/AspireApp1.Web/Components/Routes.razor
Normal file
6
examples-Aspire/AspireApp1.Web/Components/Routes.razor
Normal file
@@ -0,0 +1,6 @@
|
||||
<Router AppAssembly="@typeof(Program).Assembly">
|
||||
<Found Context="routeData">
|
||||
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
|
||||
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
|
||||
</Found>
|
||||
</Router>
|
||||
11
examples-Aspire/AspireApp1.Web/Components/_Imports.razor
Normal file
11
examples-Aspire/AspireApp1.Web/Components/_Imports.razor
Normal file
@@ -0,0 +1,11 @@
|
||||
@using System.Net.Http
|
||||
@using System.Net.Http.Json
|
||||
@using Microsoft.AspNetCore.Components.Forms
|
||||
@using Microsoft.AspNetCore.Components.Routing
|
||||
@using Microsoft.AspNetCore.Components.Web
|
||||
@using static Microsoft.AspNetCore.Components.Web.RenderMode
|
||||
@using Microsoft.AspNetCore.Components.Web.Virtualization
|
||||
@using Microsoft.AspNetCore.OutputCaching
|
||||
@using Microsoft.JSInterop
|
||||
@using AspireApp1.Web
|
||||
@using AspireApp1.Web.Components
|
||||
49
examples-Aspire/AspireApp1.Web/Program.cs
Normal file
49
examples-Aspire/AspireApp1.Web/Program.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
using AspireApp1.Web;
|
||||
using AspireApp1.Web.Components;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
// Add service defaults & Aspire components.
|
||||
builder.AddServiceDefaults();
|
||||
|
||||
// Add services to the container.
|
||||
builder.Services.AddRazorComponents()
|
||||
.AddInteractiveServerComponents();
|
||||
|
||||
builder.Services.AddOutputCache();
|
||||
|
||||
builder.Services.AddHttpClient<WeatherApiClient>(client =>
|
||||
{
|
||||
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
|
||||
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
|
||||
client.BaseAddress = new("https+http://apiservice");
|
||||
});
|
||||
builder.Services.AddHttpClient<WeatherApiClient2>(client =>
|
||||
{
|
||||
// This URL uses "https+http://" to indicate HTTPS is preferred over HTTP.
|
||||
// Learn more about service discovery scheme resolution at https://aka.ms/dotnet/sdschemes.
|
||||
client.BaseAddress = new("https+http://apiservice");
|
||||
});
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
if (!app.Environment.IsDevelopment())
|
||||
{
|
||||
app.UseExceptionHandler("/Error", createScopeForErrors: true);
|
||||
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
|
||||
app.UseHsts();
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseStaticFiles();
|
||||
app.UseAntiforgery();
|
||||
|
||||
app.UseOutputCache();
|
||||
|
||||
app.MapRazorComponents<App>()
|
||||
.AddInteractiveServerRenderMode();
|
||||
|
||||
app.MapDefaultEndpoints();
|
||||
|
||||
app.Run();
|
||||
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"http": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "http://localhost:5124",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"https": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": true,
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:7263;http://localhost:5124",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
62
examples-Aspire/AspireApp1.Web/WeatherApiClient.cs
Normal file
62
examples-Aspire/AspireApp1.Web/WeatherApiClient.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
namespace AspireApp1.Web;
|
||||
|
||||
public class WeatherApiClient(HttpClient httpClient)
|
||||
{
|
||||
public string GetBaseAddress()
|
||||
{
|
||||
return httpClient.BaseAddress?.ToString() ?? "???";
|
||||
}
|
||||
|
||||
public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
List<WeatherForecast>? forecasts = null;
|
||||
|
||||
await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast", cancellationToken))
|
||||
{
|
||||
if (forecasts?.Count >= maxItems)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (forecast is not null)
|
||||
{
|
||||
forecasts ??= [];
|
||||
forecasts.Add(forecast);
|
||||
}
|
||||
}
|
||||
|
||||
return forecasts?.ToArray() ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
public class WeatherApiClient2(HttpClient httpClient)
|
||||
{
|
||||
public string GetBaseAddress()
|
||||
{
|
||||
return httpClient.BaseAddress?.ToString() ?? "???";
|
||||
}
|
||||
|
||||
public async Task<WeatherForecast[]> GetWeatherAsync(int maxItems = 10, CancellationToken cancellationToken = default)
|
||||
{
|
||||
List<WeatherForecast>? forecasts = null;
|
||||
|
||||
await foreach (var forecast in httpClient.GetFromJsonAsAsyncEnumerable<WeatherForecast>("/weatherforecast2", cancellationToken))
|
||||
{
|
||||
if (forecasts?.Count >= maxItems)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (forecast is not null)
|
||||
{
|
||||
forecasts ??= [];
|
||||
forecasts.Add(forecast);
|
||||
}
|
||||
}
|
||||
|
||||
return forecasts?.ToArray() ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
public record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
|
||||
{
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
9
examples-Aspire/AspireApp1.Web/appsettings.json
Normal file
9
examples-Aspire/AspireApp1.Web/appsettings.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
25
examples-Aspire/AspireApp1.Web/wwwroot/app.css
Normal file
25
examples-Aspire/AspireApp1.Web/wwwroot/app.css
Normal file
@@ -0,0 +1,25 @@
|
||||
h1:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.valid.modified:not([type=checkbox]) {
|
||||
outline: 1px solid #26b050;
|
||||
}
|
||||
|
||||
.invalid {
|
||||
outline: 1px solid #e51240;
|
||||
}
|
||||
|
||||
.validation-message {
|
||||
color: #e51240;
|
||||
}
|
||||
|
||||
.blazor-error-boundary {
|
||||
background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
|
||||
padding: 1rem 1rem 1rem 3.7rem;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.blazor-error-boundary::after {
|
||||
content: "An error has occurred."
|
||||
}
|
||||
7
examples-Aspire/AspireApp1.Web/wwwroot/bootstrap/bootstrap.min.css
vendored
Normal file
7
examples-Aspire/AspireApp1.Web/wwwroot/bootstrap/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
BIN
examples-Aspire/AspireApp1.Web/wwwroot/favicon.png
Normal file
BIN
examples-Aspire/AspireApp1.Web/wwwroot/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
@@ -9,7 +9,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Google.Protobuf" Version="3.25.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.59.0" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.60.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.60.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
250
resources/WireMock.Net-Logo.eps
Normal file
250
resources/WireMock.Net-Logo.eps
Normal file
@@ -0,0 +1,250 @@
|
||||
%!PS-Adobe-3.0 EPSF-3.0
|
||||
%%Creator: cairo 1.15.10 (http://cairographics.org)
|
||||
%%CreationDate: Sat Jun 8 11:27:10 2024
|
||||
%%Pages: 1
|
||||
%%DocumentData: Clean7Bit
|
||||
%%LanguageLevel: 2
|
||||
%%BoundingBox: 0 0 150 143
|
||||
%%EndComments
|
||||
%%BeginProlog
|
||||
50 dict begin
|
||||
/q { gsave } bind def
|
||||
/Q { grestore } bind def
|
||||
/cm { 6 array astore concat } bind def
|
||||
/w { setlinewidth } bind def
|
||||
/J { setlinecap } bind def
|
||||
/j { setlinejoin } bind def
|
||||
/M { setmiterlimit } bind def
|
||||
/d { setdash } bind def
|
||||
/m { moveto } bind def
|
||||
/l { lineto } bind def
|
||||
/c { curveto } bind def
|
||||
/h { closepath } bind def
|
||||
/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
|
||||
0 exch rlineto 0 rlineto closepath } bind def
|
||||
/S { stroke } bind def
|
||||
/f { fill } bind def
|
||||
/f* { eofill } bind def
|
||||
/n { newpath } bind def
|
||||
/W { clip } bind def
|
||||
/W* { eoclip } bind def
|
||||
/BT { } bind def
|
||||
/ET { } bind def
|
||||
/BDC { mark 3 1 roll /BDC pdfmark } bind def
|
||||
/EMC { mark /EMC pdfmark } bind def
|
||||
/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
|
||||
/Tj { show currentpoint cairo_store_point } bind def
|
||||
/TJ {
|
||||
{
|
||||
dup
|
||||
type /stringtype eq
|
||||
{ show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
|
||||
} forall
|
||||
currentpoint cairo_store_point
|
||||
} bind def
|
||||
/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
|
||||
cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
|
||||
/Tf { pop /cairo_font exch def /cairo_font_matrix where
|
||||
{ pop cairo_selectfont } if } bind def
|
||||
/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
|
||||
/cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
|
||||
/cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
|
||||
cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/g { setgray } bind def
|
||||
/rg { setrgbcolor } bind def
|
||||
/d1 { setcachedevice } bind def
|
||||
/cairo_data_source {
|
||||
CairoDataIndex CairoData length lt
|
||||
{ CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }
|
||||
{ () } ifelse
|
||||
} def
|
||||
/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
|
||||
/cairo_image { image cairo_flush_ascii85_file } def
|
||||
/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
|
||||
%%EndProlog
|
||||
%%BeginSetup
|
||||
%%EndSetup
|
||||
%%Page: 1 1
|
||||
%%BeginPageSetup
|
||||
%%PageBoundingBox: 0 0 150 143
|
||||
%%EndPageSetup
|
||||
q 0 0 150 143 rectclip
|
||||
1 0 0 -1 0 143 cm q
|
||||
0.403922 0.113725 0.47451 rg
|
||||
16.574 140.176 m 16.199 138.824 15.449 136.199 15 134.324 c 14.551 132.449
|
||||
14.023 130.426 13.801 129.75 c 13.426 128.477 l 16.125 128.023 l 18.75
|
||||
127.5 l 21.375 125.25 l 23.926 123 l 24.824 120.301 l 25.648 117.676 l 25.273
|
||||
114.676 l 24.898 111.75 l 22.949 109.273 l 21 106.727 l 17.477 105.375
|
||||
l 15.449 104.625 13.801 104.023 13.727 103.949 c 13.574 103.875 14.398 100.875
|
||||
15.449 97.199 c 17.324 90.602 l 20.25 84.824 l 23.102 79.125 l 26.25 75.074
|
||||
l 28.051 72.824 30.977 69.301 32.926 67.352 c 36.375 63.676 l 42.074 59.625
|
||||
l 45.227 57.449 48.676 55.273 49.801 54.824 c 51.824 54.074 l 50.926 52.574
|
||||
l 77.102 52.574 l 79.648 45.074 l 80.625 45.074 l 81.148 45.074 81.75 45.375
|
||||
81.898 45.75 c 82.125 46.426 l 87.676 46.949 l 93.227 47.477 l 92.625 46.574
|
||||
l 92.102 45.676 l 93.449 45.977 l 94.801 46.199 l 95.699 49.199 l 96.523
|
||||
52.199 l 105.301 52.426 l 114.074 52.648 l 113.176 54 l 116.773 55.875
|
||||
l 118.727 56.926 122.773 59.551 125.625 61.801 c 130.875 65.773 l 135.602
|
||||
71.398 l 140.25 76.949 l 143.102 82.199 l 144.602 85.051 146.625 89.324
|
||||
147.523 91.574 c 149.176 95.699 l 149.176 116.25 l 149.25 136.801 l 148.352
|
||||
139.648 l 147.523 142.574 l 17.398 142.574 l h
|
||||
36 112.199 m 36 101.324 l 38.25 101.324 l 39.523 101.324 40.5 101.176 40.5
|
||||
101.023 c 40.5 100.801 39.824 99.449 38.926 98.023 c 37.352 95.324 l 33.75
|
||||
95.324 l 33.75 123.074 l 36 123.074 l h
|
||||
57.75 109.199 m 57.75 95.324 l 55.5 95.324 l 55.5 115.727 l 52.648 115.426
|
||||
l 49.727 115.199 l 52.426 119.102 l 55.051 123.074 l 57.75 123.074 l h
|
||||
84 122.324 m 84 121.574 l 72.75 121.574 l 72.75 109.574 l 82.5 109.574
|
||||
l 82.5 108.074 l 72.75 108.074 l 72.75 96.824 l 83.25 96.824 l 83.25 95.324
|
||||
l 69 95.324 l 69 122.102 l 69.523 122.551 l 69.977 123.074 l 84 123.074
|
||||
l h
|
||||
102.602 109.727 m 102.75 96.824 l 111 96.824 l 111 95.324 l 90.75 95.324
|
||||
l 90.75 96.824 l 99 96.824 l 99 122.102 l 99.523 122.625 l 99.824 122.926
|
||||
100.574 123 101.25 122.926 c 102.375 122.699 l h
|
||||
102.602 109.727 m f
|
||||
0.0117647 0.6 0.8 rg
|
||||
14.102 44.551 m 10.648 34.199 l 16.273 28.5 l 21.824 22.875 l 21 20.773
|
||||
l 20.625 19.648 20.25 17.699 20.25 16.5 c 20.25 14.324 l 22.199 14.324
|
||||
l 24 19.949 l 25.727 25.5 l 26.25 25.199 l 26.551 24.977 27.375 22.727 28.051
|
||||
20.176 c 28.801 17.551 29.926 13.426 30.676 10.949 c 31.426 8.477 32.477
|
||||
5.023 32.926 3.301 c 33.824 0.074 l 36.449 0.074 l 40.727 13.125 l 45 26.25
|
||||
l 45 30.977 l 43.875 30.074 l 42.75 29.176 l 41.625 31.801 l 41.023 33.301
|
||||
40.5 34.801 40.5 35.102 c 40.5 35.398 39.301 39.602 37.801 44.324 c 35.023
|
||||
52.949 l 33.898 53.926 l 32.699 54.824 l 17.477 54.824 l h
|
||||
14.102 44.551 m f
|
||||
51.148 53.176 m 51.074 52.199 49.648 47.551 48.074 42.824 c 45.148 34.199
|
||||
l 51.227 28.051 l 57.375 21.824 l 59.25 21.824 l 59.25 24.074 l 60.602
|
||||
24.074 l 62.176 18.301 l 63.074 15.074 64.574 9.75 65.551 6.449 c 67.352
|
||||
0.449 l 73.801 0.227 l 80.25 0 l 80.25 0.977 l 80.25 1.5 77.926 9.227 75
|
||||
18.074 c 72.148 26.926 68.324 38.773 66.523 44.324 c 63.301 54.449 l 57.301
|
||||
54.676 l 51.301 54.898 l h
|
||||
51.148 53.176 m f
|
||||
8.102 26.324 m 6.898 22.574 l 10.352 22.574 l 10.801 24.301 l 11.023 25.199
|
||||
11.25 26.852 11.25 28.051 c 11.25 30.074 l 9.301 30.074 l h
|
||||
8.102 26.324 m f
|
||||
17.398 8.398 m 16.875 6.824 16.5 4.648 16.5 3.523 c 16.5 1.574 l 18.449
|
||||
1.574 l 20.023 6.449 l 21.602 11.324 l 18.227 11.324 l h
|
||||
17.398 8.398 m f
|
||||
0.952941 0.423529 0.133333 rg
|
||||
66.75 53.324 m 66.75 51.824 l 77.023 51.824 l 78.977 45.074 l 81.75 45.074
|
||||
l 81.75 46.5 l 83.102 46.801 l 84.375 47.176 l 83.102 47.25 l 81.898 47.324
|
||||
l 79.352 54.824 l 66.75 54.824 l h
|
||||
66.75 53.324 m f
|
||||
94.273 54.301 m 93.824 53.477 92.852 49.574 92.625 47.551 c 92.398 45.824
|
||||
l 95.023 45.824 l 95.852 48.75 l 96.375 50.324 96.75 52.352 96.75 53.25
|
||||
c 96.75 54.824 l 95.699 54.824 l 95.102 54.824 94.426 54.602 94.273 54.301
|
||||
c h
|
||||
94.273 54.301 m f
|
||||
111 53.324 m 111 51.824 l 113.926 51.824 l 113.773 53.324 l 113.699 54.824
|
||||
l 111 54.824 l h
|
||||
111 53.324 m f
|
||||
87.227 28.801 m 86.25 28.426 l 86.25 22.426 l 87.375 18.227 l 88.051 15.824
|
||||
89.477 10.801 90.602 7.051 c 92.551 0.074 l 95.852 0.074 l 96.676 3.676
|
||||
l 97.051 5.625 98.102 8.773 98.926 10.727 c 99.676 12.602 100.5 14.926
|
||||
100.574 15.977 c 100.875 17.773 l 96.074 23.551 l 91.273 29.324 l 89.699
|
||||
29.25 l 88.875 29.25 87.676 29.023 87.227 28.801 c h
|
||||
87.227 28.801 m f
|
||||
120 26.699 m 120 24.898 121.949 16.949 124.125 9.824 c 124.727 7.949 125.551
|
||||
5.023 126 3.301 c 126.824 0.074 l 129.676 0.074 l 130.199 2.699 l 130.426
|
||||
4.125 131.625 8.023 132.75 11.398 c 134.926 17.477 l 130.273 23.023 l 125.625
|
||||
28.574 l 120 28.574 l h
|
||||
120 26.699 m f
|
||||
0.968627 0.576471 0.117647 rg
|
||||
60.75 54 m 60.75 53.477 63.301 45.227 66.375 35.699 c 69.449 26.25 73.273
|
||||
14.398 74.852 9.449 c 77.773 0.449 l 86.551 0.227 l 95.324 0 l 94.801 2.102
|
||||
l 94.5 3.227 93.523 6.75 92.625 9.824 c 89.926 18.898 88.352 25.426 88.648
|
||||
26.324 c 88.875 27.148 l 94.727 20.25 l 100.5 13.426 l 100.574 15.148 l
|
||||
100.574 16.125 101.398 18.977 102.301 21.449 c 104.102 25.949 l 105.227
|
||||
23.324 l 105.824 21.898 107.773 16.051 109.574 10.426 c 112.875 0.074 l
|
||||
129.676 0.074 l 127.727 7.051 l 126.676 10.801 125.102 16.727 124.199 20.176
|
||||
c 123.227 23.551 122.699 26.324 122.926 26.324 c 123.148 26.324 125.699
|
||||
23.625 128.477 20.25 c 133.648 14.176 l 135.75 20.477 l 136.875 23.852
|
||||
138.523 28.875 139.426 31.574 c 140.398 34.273 141.449 37.648 141.824 39.148
|
||||
c 142.125 40.648 143.102 43.352 143.852 45.148 c 144.676 46.949 145.5 49.727
|
||||
145.801 51.301 c 146.324 54.074 l 128.398 54.074 l 126.824 48.301 l 125.926
|
||||
45.074 124.426 39.75 123.449 36.449 c 121.648 30.523 l 120.898 30.227 l
|
||||
120.074 30 l 119.551 32.852 l 119.324 34.426 118.273 38.102 117.301 41.102
|
||||
c 116.324 44.023 115.5 47.023 115.5 47.625 c 115.5 48.301 114.977 50.176
|
||||
114.301 51.824 c 113.102 54.824 l 94.727 54.824 l 93.824 51.676 l 93.301
|
||||
49.949 92.699 47.625 92.477 46.574 c 92.324 45.523 91.352 42.148 90.449
|
||||
39.074 c 89.477 36 88.426 32.324 88.125 31.051 c 87.602 28.574 l 86.398
|
||||
28.574 l 85.875 31.051 l 85.574 32.324 84.523 35.926 83.551 39 c 82.574
|
||||
42 81.75 44.926 81.75 45.449 c 81.75 46.5 l 83.102 46.801 l 84.375 47.176
|
||||
l 81.227 47.324 l 79.273 54.074 l 76.199 54.074 l 74.551 54.074 70.352
|
||||
54.301 66.977 54.602 c 60.75 55.051 l h
|
||||
60.75 54 m f
|
||||
0.160784 0.670588 0.886275 rg
|
||||
5.773 18.977 m 2.625 9.148 0 0.898 0 0.602 c 0 0.074 l 17.773 0.074 l 19.727
|
||||
6.301 l 20.773 9.676 22.426 15.074 23.324 18.301 c 24.977 24.148 l 18.676
|
||||
30.523 l 15.227 33.977 12.227 36.824 12 36.824 c 11.773 36.824 9 28.801
|
||||
5.773 18.977 c h
|
||||
5.773 18.977 m f
|
||||
45 33.676 m 44.398 31.875 43.727 30.148 43.426 29.699 c 42.898 28.949 33.75
|
||||
1.5 33.75 0.602 c 33.75 0.074 l 52.199 0.074 l 54 5.926 l 54.898 9.074
|
||||
56.551 14.477 57.602 17.926 c 59.551 24.074 l 53.25 30.449 l 49.727 33.977
|
||||
46.727 36.824 46.426 36.824 c 46.199 36.824 45.523 35.398 45 33.676 c h
|
||||
45 33.676 m f
|
||||
0.811765 0.0941176 0.992157 rg
|
||||
22.199 116.699 m 22.199 121.574 18.227 125.551 13.352 125.551 c 8.477 125.551
|
||||
4.5 121.574 4.5 116.699 c 4.5 111.824 8.477 107.852 13.352 107.852 c 18.227
|
||||
107.852 22.199 111.824 22.199 116.699 c h
|
||||
22.199 116.699 m f
|
||||
31.5 123.824 m 31.5 122.324 l 38.25 122.324 l 38.25 125.324 l 31.5 125.324
|
||||
l h
|
||||
31.5 123.824 m f
|
||||
46.727 116.176 m 43.426 111.074 40.273 106.125 39.676 105.074 c 38.699
|
||||
103.199 l 38.176 105.824 l 37.727 108.449 l 37.574 107.176 l 37.5 105.824
|
||||
l 36 105.824 l 36 100.574 l 39.75 100.574 l 39.75 99.75 l 39.75 99.301
|
||||
39.227 98.477 38.625 97.875 c 37.5 96.75 l 37.5 93.074 l 39.676 93.074 l
|
||||
40.574 94.801 l 41.102 95.699 44.023 100.352 47.176 105.074 c 52.875 113.625
|
||||
l 53.102 111.148 l 53.324 108.676 l 54.375 110.102 l 55.426 111.449 l 55.5
|
||||
113.926 l 55.5 116.324 l 52.727 116.398 l 49.875 116.477 l 50.852 116.852
|
||||
l 51.75 117.227 l 51.75 120.074 l 53.852 120.074 l 54.301 121.199 l 54.676
|
||||
122.324 l 60 122.324 l 60 125.324 l 52.801 125.324 l h
|
||||
46.727 116.176 m f
|
||||
67.273 124.801 m 66.75 124.352 l 66.75 93.074 l 69.75 93.074 l 69.75 122.324
|
||||
l 86.25 122.324 l 86.25 125.324 l 67.727 125.324 l h
|
||||
67.273 124.801 m f
|
||||
97.273 124.875 m 96.75 124.352 l 96.75 99.074 l 94.5 99.074 l 94.5 96.824
|
||||
l 99.75 96.824 l 99.75 122.324 l 102 122.324 l 102 96.824 l 107.25 96.824
|
||||
l 107.25 99.074 l 105 99.074 l 104.852 112.051 l 104.625 124.949 l 101.25
|
||||
125.176 l 97.801 125.398 l h
|
||||
97.273 124.875 m f
|
||||
72 115.574 m 72 109.574 l 77.25 109.574 l 77.25 111.824 l 75 111.824 l
|
||||
75 119.324 l 77.25 119.324 l 77.25 121.574 l 72 121.574 l h
|
||||
72 115.574 m f
|
||||
72 102.449 m 72 96.824 l 77.25 96.824 l 77.25 99.074 l 75 99.074 l 75 105.824
|
||||
l 77.25 105.824 l 77.25 108.074 l 72 108.074 l h
|
||||
72 102.449 m f
|
||||
88.051 98.023 m 87.824 97.426 87.75 96.148 87.898 95.176 c 88.125 93.449
|
||||
l 89.477 93.227 l 90.75 93 l 90.75 99.074 l 88.426 99.074 l h
|
||||
88.051 98.023 m f
|
||||
131.25 67.199 m 129.449 65.324 128.102 63.824 128.324 63.824 c 128.551
|
||||
63.824 130.199 65.324 132 67.199 c 133.801 69.074 135.148 70.574 134.926
|
||||
70.574 c 134.699 70.574 133.051 69.074 131.25 67.199 c h
|
||||
131.25 67.199 m f
|
||||
34.875 65.324 m 36.449 63.676 37.949 62.324 38.176 62.324 c 38.398 62.324
|
||||
37.199 63.676 35.625 65.324 c 34.051 66.977 32.551 68.324 32.324 68.324
|
||||
c 32.102 68.324 33.301 66.977 34.875 65.324 c h
|
||||
34.875 65.324 m f
|
||||
83.102 46.801 m 84 46.648 85.5 46.648 86.477 46.801 c 88.125 47.102 l 81.375
|
||||
47.102 l h
|
||||
83.102 46.801 m f
|
||||
1 g
|
||||
31.5 108.824 m 31.5 93.074 l 39.602 93.074 l 39.977 94.051 l 40.199 94.574
|
||||
43.125 99.375 46.574 104.625 c 52.875 114.227 l 53.102 103.648 l 53.324
|
||||
93.074 l 60 93.074 l 60 124.574 l 52.574 124.574 l 48.523 118.426 l 46.273
|
||||
114.977 43.051 110.023 41.324 107.324 c 38.25 102.449 l 38.25 124.574 l
|
||||
31.5 124.574 l h
|
||||
31.5 108.824 m f
|
||||
67.5 108.824 m 67.5 93.074 l 85.5 93.074 l 85.5 99.074 l 74.25 99.074 l
|
||||
74.25 105.824 l 84.75 105.824 l 84.75 111.824 l 74.25 111.824 l 74.25 119.324
|
||||
l 86.25 119.324 l 86.25 124.574 l 67.5 124.574 l h
|
||||
67.5 108.824 m f
|
||||
97.5 111.824 m 97.5 99.074 l 88.5 99.074 l 88.5 93.074 l 113.25 93.074
|
||||
l 113.25 99.074 l 104.25 99.074 l 104.25 124.574 l 97.5 124.574 l h
|
||||
97.5 111.824 m f
|
||||
Q Q
|
||||
showpage
|
||||
%%Trailer
|
||||
end
|
||||
%%EOF
|
||||
317
resources/WireMock.Net-LogoAspire.eps
Normal file
317
resources/WireMock.Net-LogoAspire.eps
Normal file
@@ -0,0 +1,317 @@
|
||||
%!PS-Adobe-3.0 EPSF-3.0
|
||||
%%Creator: cairo 1.15.10 (http://cairographics.org)
|
||||
%%CreationDate: Sat Jun 8 11:28:29 2024
|
||||
%%Pages: 1
|
||||
%%DocumentData: Clean7Bit
|
||||
%%LanguageLevel: 2
|
||||
%%BoundingBox: 0 0 149 134
|
||||
%%EndComments
|
||||
%%BeginProlog
|
||||
50 dict begin
|
||||
/q { gsave } bind def
|
||||
/Q { grestore } bind def
|
||||
/cm { 6 array astore concat } bind def
|
||||
/w { setlinewidth } bind def
|
||||
/J { setlinecap } bind def
|
||||
/j { setlinejoin } bind def
|
||||
/M { setmiterlimit } bind def
|
||||
/d { setdash } bind def
|
||||
/m { moveto } bind def
|
||||
/l { lineto } bind def
|
||||
/c { curveto } bind def
|
||||
/h { closepath } bind def
|
||||
/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto
|
||||
0 exch rlineto 0 rlineto closepath } bind def
|
||||
/S { stroke } bind def
|
||||
/f { fill } bind def
|
||||
/f* { eofill } bind def
|
||||
/n { newpath } bind def
|
||||
/W { clip } bind def
|
||||
/W* { eoclip } bind def
|
||||
/BT { } bind def
|
||||
/ET { } bind def
|
||||
/BDC { mark 3 1 roll /BDC pdfmark } bind def
|
||||
/EMC { mark /EMC pdfmark } bind def
|
||||
/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def
|
||||
/Tj { show currentpoint cairo_store_point } bind def
|
||||
/TJ {
|
||||
{
|
||||
dup
|
||||
type /stringtype eq
|
||||
{ show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse
|
||||
} forall
|
||||
currentpoint cairo_store_point
|
||||
} bind def
|
||||
/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore
|
||||
cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def
|
||||
/Tf { pop /cairo_font exch def /cairo_font_matrix where
|
||||
{ pop cairo_selectfont } if } bind def
|
||||
/Td { matrix translate cairo_font_matrix matrix concatmatrix dup
|
||||
/cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point
|
||||
/cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def
|
||||
cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def
|
||||
/g { setgray } bind def
|
||||
/rg { setrgbcolor } bind def
|
||||
/d1 { setcachedevice } bind def
|
||||
/cairo_data_source {
|
||||
CairoDataIndex CairoData length lt
|
||||
{ CairoData CairoDataIndex get /CairoDataIndex CairoDataIndex 1 add def }
|
||||
{ () } ifelse
|
||||
} def
|
||||
/cairo_flush_ascii85_file { cairo_ascii85_file status { cairo_ascii85_file flushfile } if } def
|
||||
/cairo_image { image cairo_flush_ascii85_file } def
|
||||
/cairo_imagemask { imagemask cairo_flush_ascii85_file } def
|
||||
%%EndProlog
|
||||
%%BeginSetup
|
||||
%%EndSetup
|
||||
%%Page: 1 1
|
||||
%%BeginPageSetup
|
||||
%%PageBoundingBox: 0 0 149 134
|
||||
%%EndPageSetup
|
||||
q 0 0 149 134 rectclip
|
||||
1 0 0 -1 0 134 cm q
|
||||
0.317647 0.168627 0.831373 rg
|
||||
10.82 132.262 m 3.246 129.863 -1.703 121.086 0.547 113.738 c 1.07 111.938
|
||||
5.496 103.613 10.371 95.137 c 32.047 57.711 33.098 55.762 32.047 55.539
|
||||
c 30.695 55.238 30.695 53.137 32.047 53.137 c 32.645 53.137 33.32 52.238
|
||||
33.621 51.113 c 34.223 48.562 36.02 45.637 36.996 45.637 c 37.371 45.637
|
||||
37.52 46.164 37.297 46.762 c 36.996 47.363 36.996 47.887 37.223 47.887
|
||||
c 38.047 47.887 45.32 34.387 44.871 33.637 c 44.645 33.262 45.02 32.887
|
||||
45.77 32.887 c 46.82 32.887 47.57 34.238 48.848 38.363 c 51.621 47.211 51.695
|
||||
46.988 40.297 66.637 c 16.145 108.336 11.496 116.586 11.496 117.789 c 11.496
|
||||
118.461 12.395 119.738 13.445 120.562 c 15.395 122.137 15.922 122.137 74.645
|
||||
122.137 c 133.82 122.137 l 135.621 120.262 l 136.672 119.289 137.496 118.086
|
||||
137.496 117.562 c 137.496 116.738 128.195 100.461 109.598 68.664 c 105.172
|
||||
61.164 103.746 58.086 103.746 55.988 c 103.746 53.137 l 107.871 53.137
|
||||
l 110.121 53.137 111.996 52.836 111.996 52.387 c 111.996 52.012 112.746
|
||||
51.637 113.645 51.637 c 114.547 51.637 115.07 51.863 114.77 52.086 c 114.098
|
||||
52.762 114.621 53.664 132.098 83.887 c 140.645 98.512 147.996 112.086 148.445
|
||||
113.961 c 150.547 121.914 145.223 130.539 137.047 132.562 c 131.871 133.914
|
||||
15.098 133.613 10.82 132.262 c h
|
||||
10.82 132.262 m f
|
||||
77.496 55.012 m 77.496 52.539 77.645 52.387 80.121 52.387 c 82.598 52.387
|
||||
82.746 52.539 82.746 55.012 c 82.746 57.488 82.598 57.637 80.121 57.637
|
||||
c 77.645 57.637 77.496 57.488 77.496 55.012 c h
|
||||
77.496 55.012 m f
|
||||
56.27 20.887 m 54.621 15.113 54.547 12.113 56.047 13.312 c 56.871 14.062
|
||||
57.547 13.461 59.496 10.086 c 61.598 6.336 67.145 0.637 68.645 0.637 c
|
||||
69.621 0.637 69.32 3.863 67.82 8.586 c 67.07 10.988 65.645 15.789 64.672
|
||||
19.164 c 62.871 25.387 l 60.172 25.387 l 57.547 25.387 57.473 25.312 56.27
|
||||
20.887 c h
|
||||
56.27 20.887 m f
|
||||
0.0117647 0.6 0.8 rg
|
||||
48.996 55.914 m 48.996 54.938 48.473 53.062 47.871 51.863 c 46.973 49.988
|
||||
46.895 49.164 47.57 47.664 c 48.246 46.086 48.172 45.113 47.195 42.562
|
||||
c 46.445 40.762 45.621 37.914 45.32 36.113 c 44.797 33.336 44.871 32.887
|
||||
45.922 32.887 c 46.598 32.887 49.297 30.789 51.848 28.164 c 56.57 23.438
|
||||
l 55.371 20.211 l 54.098 16.613 53.871 11.887 54.922 11.887 c 55.82 11.887
|
||||
58.445 17.512 59.121 20.738 c 59.422 22.012 59.871 23.137 60.098 23.137
|
||||
c 60.395 23.137 61.445 20.363 62.348 16.914 c 63.32 13.539 64.895 8.586
|
||||
65.723 5.887 c 67.371 1.086 67.445 1.012 70.672 0.414 c 74.645 -0.336 80.496
|
||||
0.336 80.496 1.613 c 80.496 2.137 79.145 6.711 77.496 11.887 c 75.77 17.062
|
||||
71.945 29.062 68.871 38.586 c 65.797 48.039 63.246 56.211 63.246 56.738
|
||||
c 63.246 57.336 61.145 57.637 56.121 57.637 c 49.145 57.637 48.996 57.637
|
||||
48.996 55.914 c h
|
||||
48.996 55.914 m f
|
||||
14.348 45.113 m 10.895 34.762 l 16.52 29.062 l 21.695 23.738 21.996 23.289
|
||||
21.246 21.336 c 20.195 18.414 20.27 14.887 21.473 14.887 c 22.07 14.887
|
||||
23.195 17.211 24.246 20.512 c 25.223 23.586 26.195 25.914 26.496 25.762
|
||||
c 26.797 25.539 27.621 23.289 28.297 20.738 c 29.047 18.113 30.172 13.988
|
||||
30.922 11.512 c 31.672 9.039 32.723 5.586 33.172 3.863 c 33.77 1.613 34.445
|
||||
0.637 35.348 0.637 c 36.395 0.637 37.598 3.336 40.973 13.688 c 45.32 27.039
|
||||
46.371 32.512 44.121 30.637 c 43.145 29.887 42.848 30.113 41.871 32.363
|
||||
c 41.27 33.863 40.746 35.512 40.746 36.039 c 40.746 36.562 39.996 38.887
|
||||
39.172 41.137 c 38.27 43.387 37.52 46.086 37.445 47.137 c 37.371 48.188
|
||||
36.848 50.438 36.246 52.238 c 35.27 55.387 l 17.723 55.387 l h
|
||||
14.348 45.113 m f
|
||||
8.348 26.887 m 7.145 23.137 l 8.871 23.137 l 10.672 23.137 11.496 24.789
|
||||
11.496 28.613 c 11.496 31.988 9.621 30.938 8.348 26.887 c h
|
||||
8.348 26.887 m f
|
||||
17.645 8.961 m 15.32 1.387 17.871 -0.488 20.27 7.012 c 21.848 11.887 l
|
||||
20.195 11.887 l 18.848 11.887 18.32 11.211 17.645 8.961 c h
|
||||
17.645 8.961 m f
|
||||
0.952941 0.423529 0.133333 rg
|
||||
66.996 55.012 m 66.996 52.387 l 77.195 52.387 l 78.77 47.363 l 79.672 44.512
|
||||
81.32 38.961 82.52 34.988 c 83.723 30.711 84.996 27.637 85.598 27.637 c
|
||||
86.121 27.637 86.496 26.664 86.496 25.312 c 86.496 22.988 87.172 20.062
|
||||
90.848 7.539 c 92.57 1.539 93.02 0.637 94.445 0.637 c 95.871 0.637 96.246
|
||||
1.238 96.922 4.238 c 97.297 6.188 98.348 9.336 99.172 11.289 c 101.723
|
||||
17.289 101.57 17.812 95.945 24.562 c 90.77 30.711 l 92.797 38.211 l 93.922
|
||||
42.262 95.27 47.062 95.871 48.711 c 96.473 50.438 96.996 52.988 96.996
|
||||
54.336 c 96.996 56.738 96.848 56.887 94.52 56.887 c 92.121 56.887 l 89.945
|
||||
49.238 l 88.746 44.961 87.547 41.289 87.246 40.914 c 87.02 40.613 86.348
|
||||
42.113 85.82 44.289 c 85.223 46.461 84.172 50.363 83.348 52.988 c 81.922
|
||||
57.637 l 66.996 57.637 l h
|
||||
66.996 55.012 m f
|
||||
111.246 53.887 m 111.246 52.914 111.77 52.387 112.598 52.387 c 114.395
|
||||
52.387 114.996 52.988 114.473 54.336 c 113.871 55.988 111.246 55.613 111.246
|
||||
53.887 c h
|
||||
111.246 53.887 m f
|
||||
120.246 27.262 m 120.246 25.461 122.195 17.512 124.371 10.387 c 124.973
|
||||
8.512 125.797 5.586 126.246 3.863 c 127.297 -0.336 129.695 -0.711 130.445
|
||||
3.262 c 130.672 4.688 131.871 8.586 132.996 11.961 c 135.172 18.039 l 130.52
|
||||
23.586 l 126.246 28.688 125.645 29.137 123.098 29.137 c 120.547 29.137
|
||||
120.246 28.914 120.246 27.262 c h
|
||||
120.246 27.262 m f
|
||||
0.454902 0.333333 0.866667 rg
|
||||
132.246 123.039 m 132.246 122.363 132.996 121.387 133.895 121.012 c 134.723
|
||||
120.562 135.922 119.586 136.445 118.762 c 137.348 117.414 136.297 115.312
|
||||
126.621 98.512 c 115.82 79.762 l 96.172 79.387 l 76.598 79.012 l 70.672
|
||||
68.887 l 66.172 61.164 64.746 58.086 64.746 55.988 c 64.746 53.137 l 77.348
|
||||
53.137 l 79.223 47.289 l 80.27 44.137 81.848 38.887 82.82 35.664 c 84.395
|
||||
30.262 85.746 28.539 86.797 30.863 c 87.172 31.539 87.32 31.539 87.695
|
||||
30.863 c 88.746 28.461 90.172 30.188 91.672 35.664 c 92.57 38.887 94.07
|
||||
44.062 95.047 47.137 c 96.848 52.762 l 102.473 52.988 l 106.598 53.137 108.246
|
||||
53.512 108.547 54.262 c 108.848 55.012 108.473 55.387 107.496 55.387 c
|
||||
106.672 55.387 105.996 55.613 105.996 55.988 c 105.996 56.289 108.621 60.938
|
||||
111.848 66.414 c 131.422 99.863 140.047 115.012 140.348 116.512 c 140.723
|
||||
118.613 138.695 122.586 136.82 123.637 c 134.496 124.836 132.246 124.539
|
||||
132.246 123.039 c h
|
||||
78.098 54.863 m 76.52 54.711 73.82 54.711 72.098 54.863 c 70.297 55.012
|
||||
71.57 55.164 74.871 55.164 c 78.172 55.164 79.598 55.012 78.098 54.863
|
||||
c h
|
||||
78.098 54.863 m f
|
||||
10.223 121.836 m 8.047 119.137 7.973 116.887 9.996 116.887 c 11.57 116.887
|
||||
12.547 119.289 12.098 122.211 c 11.871 124.012 11.871 124.012 10.223 121.836
|
||||
c h
|
||||
10.223 121.836 m f
|
||||
10.746 112.012 m 10.746 111.488 21.172 93.188 26.57 84.262 c 28.52 80.961
|
||||
34.371 70.914 39.547 61.914 c 48.246 46.688 48.922 45.789 49.371 47.887
|
||||
c 49.672 49.164 50.422 50.438 51.02 50.664 c 51.77 50.961 51.996 51.711
|
||||
51.695 53.211 c 51.395 54.938 51.621 55.312 52.973 55.613 c 53.871 55.762
|
||||
53.195 55.988 51.473 56.062 c 49.746 56.062 48.246 55.762 48.098 55.312
|
||||
c 47.945 54.789 45.473 58.461 42.695 63.336 c 39.848 68.289 37.297 72.414
|
||||
36.996 72.637 c 36.77 72.863 36.098 74.137 35.57 75.488 c 34.973 76.762
|
||||
34.297 77.887 33.996 77.887 c 33.695 77.887 33.172 78.711 32.945 79.762
|
||||
c 32.645 80.812 32.121 81.637 31.746 81.637 c 31.297 81.637 30.996 82.164
|
||||
30.996 82.762 c 30.996 83.363 30.473 83.887 29.871 83.887 c 29.195 83.887
|
||||
28.746 84.562 28.746 85.688 c 28.746 86.738 28.297 88.012 27.695 88.539
|
||||
c 27.172 89.137 26.422 90.336 26.195 91.238 c 25.895 92.137 25.145 92.887
|
||||
24.547 92.887 c 23.945 92.887 23.496 93.562 23.496 94.312 c 23.496 96.562
|
||||
22.672 98.137 21.547 98.137 c 20.945 98.137 20.496 98.812 20.496 99.562
|
||||
c 20.496 101.812 19.672 103.387 18.547 103.387 c 17.945 103.387 17.496
|
||||
104.137 17.496 105.262 c 17.496 106.613 17.121 107.137 15.996 107.137 c
|
||||
14.797 107.137 14.496 107.664 14.496 109.762 c 14.496 112.086 14.27 112.387
|
||||
12.621 112.387 c 11.57 112.387 10.746 112.238 10.746 112.012 c h
|
||||
10.746 112.012 m f
|
||||
16.973 52.688 m 16.746 52.086 16.895 51.188 17.422 50.664 c 18.02 50.062
|
||||
18.246 50.363 18.246 51.863 c 18.246 54.113 17.723 54.488 16.973 52.688
|
||||
c h
|
||||
16.973 52.688 m f
|
||||
45.547 36.262 m 45.621 34.988 46.07 34.387 46.973 34.387 c 48.695 34.387
|
||||
48.621 36.414 46.82 37.312 c 45.621 37.988 45.395 37.836 45.547 36.262
|
||||
c h
|
||||
45.547 36.262 m f
|
||||
56.195 21.789 m 54.848 16.836 54.621 13.387 55.746 13.387 c 56.195 13.387
|
||||
56.496 13.914 56.496 14.512 c 56.496 15.113 56.797 15.637 57.246 15.637
|
||||
c 57.621 15.637 57.77 16.164 57.547 16.762 c 57.246 17.363 57.473 17.887
|
||||
57.996 17.887 c 58.52 17.887 58.746 18.414 58.52 19.012 c 58.223 19.613
|
||||
58.371 20.289 58.82 20.512 c 59.195 20.812 59.348 21.336 59.121 21.711
|
||||
c 58.82 22.086 59.121 22.613 59.797 22.836 c 60.77 23.211 60.695 23.438
|
||||
59.645 24.711 c 58.973 25.461 58.223 26.137 57.922 26.137 c 57.695 26.137
|
||||
56.871 24.188 56.195 21.789 c h
|
||||
56.195 21.789 m f
|
||||
77.645 1.988 m 78.098 0.637 79.746 0.414 79.746 1.688 c 79.746 2.363 79.223
|
||||
2.887 78.547 2.887 c 77.871 2.887 77.496 2.512 77.645 1.988 c h
|
||||
77.645 1.988 m f
|
||||
72.848 0.113 m 73.746 -0.039 75.246 -0.039 76.223 0.113 c 77.121 0.262
|
||||
76.371 0.414 74.496 0.414 c 72.621 0.414 71.871 0.262 72.848 0.113 c h
|
||||
72.848 0.113 m f
|
||||
0.968627 0.576471 0.117647 rg
|
||||
58.746 56.512 m 58.746 55.914 59.27 55.387 59.871 55.387 c 60.473 55.387
|
||||
60.996 55.012 60.996 54.488 c 60.996 53.961 63.547 45.789 66.621 36.336
|
||||
c 69.695 26.812 73.52 14.961 75.098 10.012 c 78.02 1.012 l 86.797 0.789
|
||||
l 95.57 0.562 l 95.047 2.664 l 94.746 3.789 93.77 7.312 92.871 10.387 c
|
||||
90.172 19.461 88.598 25.988 88.895 26.887 c 89.047 27.336 91.746 24.637
|
||||
94.973 20.812 c 99.695 15.188 100.746 14.289 100.82 15.711 c 100.82 16.688
|
||||
101.645 19.539 102.547 22.012 c 104.348 26.512 l 105.473 23.887 l 106.07
|
||||
22.461 108.02 16.613 109.82 10.988 c 113.121 0.637 l 129.922 0.637 l 127.973
|
||||
7.613 l 126.922 11.363 125.348 17.289 124.445 20.738 c 123.473 24.113 122.945
|
||||
26.887 123.172 26.887 c 123.395 26.887 125.945 24.188 128.723 20.812 c
|
||||
133.895 14.738 l 135.996 21.039 l 137.121 24.414 138.77 29.438 139.672 32.137
|
||||
c 140.645 34.836 141.695 38.211 142.07 39.711 c 142.371 41.211 143.348
|
||||
43.914 144.098 45.711 c 144.922 47.512 145.746 50.289 146.047 51.863 c 146.57
|
||||
54.637 l 128.645 54.637 l 127.07 48.863 l 123.473 35.961 121.895 31.086
|
||||
121.145 30.789 c 120.695 30.637 120.098 31.836 119.871 33.414 c 119.57
|
||||
34.988 118.746 37.988 117.996 40.012 c 116.945 42.938 115.145 50.137 114.621
|
||||
53.137 c 114.32 55.086 112.895 55.387 104.195 55.387 c 95.047 55.387 l
|
||||
93.246 49.988 l 92.27 46.988 91.371 44.289 91.297 43.988 c 91.223 43.613
|
||||
91.07 43.164 90.996 42.863 c 90.922 42.488 90.77 41.961 90.621 41.512 c
|
||||
90.547 41.137 90.395 40.613 90.32 40.387 c 90.098 40.012 89.645 38.062
|
||||
89.27 36.262 c 89.121 35.664 88.82 34.988 88.598 34.688 c 88.297 34.461
|
||||
88.297 33.711 88.52 33.113 c 88.82 32.289 88.52 32.062 87.547 32.289 c 86.797
|
||||
32.438 86.047 32.961 86.047 33.488 c 85.973 33.938 85.82 34.539 85.746
|
||||
34.762 c 85.672 34.988 85.445 35.512 85.371 35.887 c 85.297 36.336 85.07
|
||||
36.789 84.996 37.012 c 84.922 37.238 84.77 37.613 84.695 37.988 c 84.621
|
||||
38.289 84.32 38.961 84.02 39.488 c 83.645 39.938 83.57 40.688 83.871 41.062
|
||||
c 84.098 41.438 83.945 41.961 83.496 42.262 c 83.047 42.562 82.895 43.086
|
||||
83.121 43.461 c 83.422 43.836 83.348 44.586 82.973 45.113 c 82.672 45.562
|
||||
82.371 46.164 82.297 46.387 c 82.297 46.613 81.848 47.738 81.395 48.863
|
||||
c 80.871 49.988 80.27 51.789 80.047 52.762 c 79.598 54.488 79.223 54.637
|
||||
74.945 54.637 c 69.172 54.637 66.77 55.238 67.297 56.586 c 67.598 57.414
|
||||
66.695 57.637 63.246 57.637 c 59.723 57.637 58.746 57.414 58.746 56.512
|
||||
c h
|
||||
58.746 56.512 m f
|
||||
0.160784 0.670588 0.886275 rg
|
||||
6.02 19.539 m 2.871 9.711 0.246 1.461 0.246 1.164 c 0.246 0.863 4.223 0.637
|
||||
9.172 0.637 c 18.02 0.637 l 19.973 6.863 l 21.02 10.238 22.672 15.637 23.57
|
||||
18.863 c 25.223 24.711 l 18.922 31.086 l 15.473 34.539 12.473 37.387 12.246
|
||||
37.387 c 12.02 37.387 9.246 29.363 6.02 19.539 c h
|
||||
6.02 19.539 m f
|
||||
45.246 34.238 m 44.645 32.438 43.973 30.711 43.672 30.262 c 43.145 29.512
|
||||
33.996 2.062 33.996 1.164 c 33.996 0.863 38.121 0.637 43.223 0.637 c 52.445
|
||||
0.637 l 54.246 6.488 l 55.145 9.637 56.797 15.039 57.848 18.488 c 59.797
|
||||
24.637 l 53.496 31.012 l 49.973 34.539 46.973 37.387 46.672 37.387 c 46.445
|
||||
37.387 45.77 35.961 45.246 34.238 c h
|
||||
45.246 34.238 m f
|
||||
0.584314 0.490196 0.894118 rg
|
||||
12.02 123.562 m 10.52 122.664 8.496 118.988 8.496 117.113 c 8.496 116.363
|
||||
12.996 107.961 18.547 98.512 c 36.547 67.539 48.172 47.664 48.547 47.289
|
||||
c 48.996 46.836 49.371 47.664 50.496 52.238 c 51.32 55.387 l 58.973 55.387
|
||||
l 66.621 55.461 l 72.695 66.113 l 78.77 76.762 l 98.348 76.988 l 117.848
|
||||
77.137 l 128.797 96.188 l 140.57 116.664 141.172 118.164 137.945 121.914
|
||||
c 136.145 124.012 l 115.07 124.238 l 94.52 124.461 93.996 124.387 93.996
|
||||
122.961 c 93.996 122.137 94.598 121.312 95.348 121.086 c 96.172 120.789
|
||||
97.145 119.812 97.672 118.914 c 98.496 117.336 97.672 115.539 87.77 98.438
|
||||
c 76.895 79.762 l 56.797 79.539 l 34.52 79.312 30.246 79.836 30.246 82.836
|
||||
c 30.246 85.312 29.496 86.887 28.297 86.887 c 27.695 86.887 27.246 87.637
|
||||
27.246 88.688 c 27.246 89.738 26.797 91.012 26.195 91.539 c 25.672 92.137
|
||||
24.922 93.336 24.695 94.238 c 24.395 95.137 23.645 95.887 23.047 95.887
|
||||
c 22.445 95.887 21.996 96.562 21.996 97.312 c 21.996 99.562 21.172 101.137
|
||||
20.047 101.137 c 19.445 101.137 18.996 101.887 18.996 102.938 c 18.996
|
||||
103.988 18.547 105.262 17.945 105.789 c 17.422 106.387 16.672 107.586 16.445
|
||||
108.488 c 16.145 109.387 15.395 110.137 14.797 110.137 c 14.195 110.137
|
||||
13.746 110.887 13.746 111.938 c 13.746 112.988 13.223 114.262 12.621 114.938
|
||||
c 10.895 116.664 11.27 118.613 13.746 120.262 c 17.57 122.812 15.996 125.887
|
||||
12.02 123.562 c h
|
||||
12.02 123.562 m f
|
||||
0.72549 0.666667 0.933333 rg
|
||||
11.348 122.211 m 7.297 118.613 7.746 117.188 19.223 97.387 c 24.848 87.711
|
||||
30.246 79.164 31.07 78.414 c 32.57 77.211 34.672 77.137 55.895 77.289 c
|
||||
79.145 77.512 l 90.02 96.188 l 100.973 115.164 101.945 117.414 99.996 120.938
|
||||
c 98.348 124.164 96.547 124.387 75.246 124.387 c 52.895 124.387 52.895
|
||||
124.387 57.32 119.961 c 58.598 118.688 59.422 117.414 59.27 117.113 c 57.02
|
||||
113.062 39.32 84.414 37.973 82.613 c 35.27 79.012 34.672 79.238 29.645
|
||||
85.762 c 29.348 86.211 28.598 87.637 28.07 88.988 c 27.547 90.262 26.797
|
||||
91.387 26.422 91.387 c 26.047 91.387 25.746 91.836 25.746 92.438 c 25.746
|
||||
93.039 25.297 94.012 24.77 94.539 c 24.246 95.062 23.348 96.637 22.82 97.988
|
||||
c 22.297 99.262 21.547 100.387 21.246 100.387 c 20.945 100.387 20.422 101.211
|
||||
20.195 102.188 c 19.973 103.238 19.297 104.512 18.77 105.039 c 18.32 105.562
|
||||
17.047 107.664 15.996 109.688 c 15.02 111.711 13.746 113.738 13.297 114.262
|
||||
c 12.32 115.238 12.02 116.363 12.172 118.836 c 12.246 119.961 12.922 120.562
|
||||
14.496 120.938 c 16.07 121.238 16.746 121.762 16.746 122.887 c 16.746 125.062
|
||||
14.195 124.762 11.348 122.211 c h
|
||||
11.348 122.211 m f
|
||||
0.862745 0.835294 0.964706 rg
|
||||
12.848 123.188 m 10.445 121.914 9.246 119.961 9.246 117.336 c 9.246 115.312
|
||||
27.621 82.762 30.473 79.613 c 32.422 77.438 37.672 77.211 39.473 79.238
|
||||
c 40.07 79.914 45.395 88.312 51.172 97.762 c 62.945 116.961 63.395 118.312
|
||||
59.57 122.211 c 57.395 124.387 l 36.098 124.387 l 18.848 124.312 14.496
|
||||
124.086 12.848 123.188 c h
|
||||
12.848 123.188 m f
|
||||
Q Q
|
||||
showpage
|
||||
%%Trailer
|
||||
end
|
||||
%%EOF
|
||||
BIN
resources/WireMock.Net-LogoAspire.ico
Normal file
BIN
resources/WireMock.Net-LogoAspire.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
BIN
resources/WireMock.Net-LogoAspire.png
Normal file
BIN
resources/WireMock.Net-LogoAspire.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
96
src/WireMock.Net.Aspire/DistributedApplicationExtensions.cs
Normal file
96
src/WireMock.Net.Aspire/DistributedApplicationExtensions.cs
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Globalization;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using Aspire.Hosting.ApplicationModel;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using RestEase;
|
||||
using WireMock.Client;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Aspire.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Some WireMock.Net extension methods for working with <see cref="DistributedApplication"/>.
|
||||
/// Based on https://github.com/dotnet/aspire/blob/main/src/Aspire.Hosting.Testing/DistributedApplicationHostingTestingExtensions.cs
|
||||
/// </summary>
|
||||
public static class DistributedApplicationExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Create a RestEase Admin client which can be used to call the admin REST endpoint.
|
||||
/// </summary>
|
||||
/// <param name="app">The <see cref="DistributedApplication"/>.</param>
|
||||
/// <param name="resourceName">The resourceName of the resource.</param>
|
||||
/// <param name="endpointName">The resourceName of the endpoint on the resource to communicate with.</param>
|
||||
/// <returns>A <see cref="IWireMockAdminApi"/></returns>
|
||||
public static IWireMockAdminApi CreateWireMockAdminClient(this DistributedApplication app, string resourceName, string? endpointName = default)
|
||||
{
|
||||
ThrowIfNotStarted(app);
|
||||
|
||||
var (resource, endpointUri) = GetResourceAndEndpointUri(app, resourceName);
|
||||
|
||||
var api = RestClient.For<IWireMockAdminApi>(endpointUri);
|
||||
if (resource.Arguments.HasBasicAuthentication)
|
||||
{
|
||||
api.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes($"{resource.Arguments.AdminUsername}:{resource.Arguments.AdminPassword}")));
|
||||
}
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
private static (WireMockServerResource WireMockServerResource, string EndpointUri) GetResourceAndEndpointUri(IHost app, string resourceName, string? endpointName = default)
|
||||
{
|
||||
var wireMockServerResource = GetWireMockServerResource(app, resourceName);
|
||||
|
||||
EndpointReference? endpoint;
|
||||
if (!string.IsNullOrEmpty(endpointName))
|
||||
{
|
||||
endpoint = GetEndpointOrDefault(wireMockServerResource, endpointName);
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint = GetEndpointOrDefault(wireMockServerResource, "http") ?? GetEndpointOrDefault(wireMockServerResource, "https");
|
||||
}
|
||||
|
||||
if (endpoint is null)
|
||||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Endpoint '{0}' for resource '{1}' not found.", endpointName, resourceName), nameof(endpointName));
|
||||
}
|
||||
|
||||
return (wireMockServerResource, endpoint.Url);
|
||||
}
|
||||
|
||||
private static WireMockServerResource GetWireMockServerResource(IHost app, string resourceName)
|
||||
{
|
||||
var applicationModel = app.Services.GetRequiredService<DistributedApplicationModel>();
|
||||
|
||||
var resource = applicationModel.Resources
|
||||
.OfType<WireMockServerResource>()
|
||||
.SingleOrDefault(r => string.Equals(r.Name, resourceName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (resource is null)
|
||||
{
|
||||
throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "WireMockServerResource with name '{0}' not found.", resourceName), nameof(resourceName));
|
||||
}
|
||||
|
||||
return resource;
|
||||
}
|
||||
|
||||
private static EndpointReference? GetEndpointOrDefault(IResourceWithEndpoints wireMockServerResource, string endpointName)
|
||||
{
|
||||
var reference = wireMockServerResource.GetEndpoint(endpointName);
|
||||
|
||||
return reference.IsAllocated ? reference : null;
|
||||
}
|
||||
|
||||
private static void ThrowIfNotStarted(IHost app)
|
||||
{
|
||||
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
|
||||
if (!lifetime.ApplicationStarted.IsCancellationRequested)
|
||||
{
|
||||
throw new InvalidOperationException("The application must be started before resolving endpoints or connection strings");
|
||||
}
|
||||
}
|
||||
}
|
||||
6
src/WireMock.Net.Aspire/Properties/AssemblyInfo.cs
Normal file
6
src/WireMock.Net.Aspire/Properties/AssemblyInfo.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("WireMock.Net.Aspire.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e138ec44d93acac565953052636eb8d5e7e9f27ddb030590055cd1a0ab2069a5623f1f77ca907d78e0b37066ca0f6d63da7eecc3fcb65b76aa8ebeccf7ebe1d11264b8404cd9b1cbbf2c83f566e033b3e54129f6ef28daffff776ba7aebbc53c0d635ebad8f45f78eb3f7e0459023c218f003416e080f96a1a3c5ffeb56bee9e")]
|
||||
|
||||
// Needed for Moq in the UnitTest project
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]
|
||||
47
src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj
Normal file
47
src/WireMock.Net.Aspire/WireMock.Net.Aspire.csproj
Normal file
@@ -0,0 +1,47 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>0.0.1-preview-05</Version>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Description>Aspire extension to start a WireMock.Net server to stub an api.</Description>
|
||||
<AssemblyTitle>WireMock.Net.Aspire</AssemblyTitle>
|
||||
<Authors>Stef Heyenrath</Authors>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<AssemblyName>WireMock.Net.Aspire</AssemblyName>
|
||||
<PackageId>WireMock.Net.Aspire</PackageId>
|
||||
<PackageTags>dotnet;aspire;wiremock;extension</PackageTags>
|
||||
<ProjectGuid>{B6269AAC-170A-4346-8B9A-579DED3D9A12}</ProjectGuid>
|
||||
<PublishRepositoryUrl>true</PublishRepositoryUrl>
|
||||
<AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
|
||||
<EmbedUntrackedSources>true</EmbedUntrackedSources>
|
||||
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||
<GenerateBindingRedirectsOutputType>true</GenerateBindingRedirectsOutputType>
|
||||
<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>
|
||||
<PackageIcon>WireMock.Net-LogoAspire.png</PackageIcon>
|
||||
<ApplicationIcon>../../resources/WireMock.Net-LogoAspire.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Remove="../../resources/WireMock.Net-Logo.png" />
|
||||
<None Include="../../resources/WireMock.Net-LogoAspire.png" Pack="true" PackagePath="" />
|
||||
</ItemGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\WireMock.Net.RestClient\WireMock.Net.RestClient.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
112
src/WireMock.Net.Aspire/WireMockServerArguments.cs
Normal file
112
src/WireMock.Net.Aspire/WireMockServerArguments.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using WireMock.Client.Builders;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Aspire.Hosting;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the arguments required to configure and start a WireMock.Net Server.
|
||||
/// </summary>
|
||||
public class WireMockServerArguments
|
||||
{
|
||||
internal const int HttpContainerPort = 80;
|
||||
|
||||
/// <summary>
|
||||
/// The default HTTP port where WireMock.Net is listening.
|
||||
/// </summary>
|
||||
public const int DefaultPort = 9091;
|
||||
|
||||
private const string DefaultLogger = "WireMockConsoleLogger";
|
||||
|
||||
/// <summary>
|
||||
/// The HTTP port where WireMock.Net is listening.
|
||||
/// If not defined, .NET Aspire automatically assigns a random port.
|
||||
/// </summary>
|
||||
public int? HttpPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The admin username.
|
||||
/// </summary>
|
||||
[MemberNotNullWhen(true, nameof(HasBasicAuthentication))]
|
||||
public string? AdminUsername { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The admin password.
|
||||
/// </summary>
|
||||
[MemberNotNullWhen(true, nameof(HasBasicAuthentication))]
|
||||
public string? AdminPassword { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the static mappings should be read at startup.
|
||||
///
|
||||
/// Default value is <c>false</c>.
|
||||
/// </summary>
|
||||
public bool ReadStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Watch the static mapping files + folder for changes when running.
|
||||
///
|
||||
/// Default value is <c>false</c>.
|
||||
/// </summary>
|
||||
public bool WithWatchStaticMappings { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the path for the (static) mapping json files.
|
||||
/// </summary>
|
||||
public string? MappingsPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the admin interface has Basic Authentication.
|
||||
/// </summary>
|
||||
public bool HasBasicAuthentication => !string.IsNullOrEmpty(AdminUsername) && !string.IsNullOrEmpty(AdminPassword);
|
||||
|
||||
/// <summary>
|
||||
/// Optional delegate that will be invoked to configure the WireMock.Net resource using the <see cref="AdminApiMappingBuilder"/>.
|
||||
/// </summary>
|
||||
public Func<AdminApiMappingBuilder, Task>? ApiMappingBuilder { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Converts the current instance's properties to an array of command-line arguments for starting the WireMock.Net server.
|
||||
/// </summary>
|
||||
/// <returns>An array of strings representing the command-line arguments.</returns>
|
||||
public string[] GetArgs()
|
||||
{
|
||||
var args = new Dictionary<string, string>();
|
||||
|
||||
Add(args, "--WireMockLogger", DefaultLogger);
|
||||
|
||||
if (HasBasicAuthentication)
|
||||
{
|
||||
Add(args, "--AdminUserName", AdminUsername!);
|
||||
Add(args, "--AdminPassword", AdminPassword!);
|
||||
}
|
||||
|
||||
if (ReadStaticMappings)
|
||||
{
|
||||
Add(args, "--ReadStaticMappings", "true");
|
||||
}
|
||||
|
||||
if (WithWatchStaticMappings)
|
||||
{
|
||||
Add(args, "--ReadStaticMappings", "true");
|
||||
Add(args, "--WatchStaticMappings", "true");
|
||||
Add(args, "--WatchStaticMappingsInSubdirectories", "true");
|
||||
}
|
||||
|
||||
return args
|
||||
.SelectMany(k => new[] { k.Key, k.Value })
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
private static void Add(IDictionary<string, string> args, string argument, string value)
|
||||
{
|
||||
args[argument] = value;
|
||||
}
|
||||
|
||||
private static void Add(IDictionary<string, string> args, string argument, Func<string> action)
|
||||
{
|
||||
args[argument] = action();
|
||||
}
|
||||
}
|
||||
162
src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs
Normal file
162
src/WireMock.Net.Aspire/WireMockServerBuilderExtensions.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
// 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;
|
||||
|
||||
/// <summary>
|
||||
/// Provides extension methods for adding WireMock.Net Server resources to the application model.
|
||||
/// </summary>
|
||||
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";
|
||||
|
||||
/// <summary>
|
||||
/// Adds a WireMock.Net Server resource to the application model.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
|
||||
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
|
||||
/// <param name="port">The HTTP port for the WireMock Server.</param>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> 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;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a WireMock.Net Server resource to the application model.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
|
||||
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
|
||||
/// <param name="arguments">The arguments to start the WireMock.Net Server.</param>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a WireMock.Net Server resource to the application model.
|
||||
/// </summary>
|
||||
/// <param name="builder">The <see cref="IDistributedApplicationBuilder"/>.</param>
|
||||
/// <param name="name">The name of the resource. This name will be used as the connection string name when referenced in a dependency.</param>
|
||||
/// <param name="callback">A callback that allows for setting the <see cref="WireMockServerArguments"/>.</param>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> AddWireMock(this IDistributedApplicationBuilder builder, string name, Action<WireMockServerArguments> callback)
|
||||
{
|
||||
Guard.NotNull(builder);
|
||||
Guard.NotNullOrWhiteSpace(name);
|
||||
Guard.NotNull(callback);
|
||||
|
||||
var arguments = new WireMockServerArguments();
|
||||
callback(arguments);
|
||||
|
||||
return builder.AddWireMock(name, arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines if the static mappings should be read at startup.
|
||||
///
|
||||
/// Default set to <c>false</c>.
|
||||
/// </summary>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> WithReadStaticMappings(this IResourceBuilder<WireMockServerResource> wiremock)
|
||||
{
|
||||
Guard.NotNull(wiremock).Resource.Arguments.ReadStaticMappings = true;
|
||||
return wiremock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Watch the static mapping files + folder for changes when running.
|
||||
///
|
||||
/// Default set to <c>false</c>.
|
||||
/// </summary>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> WithWatchStaticMappings(this IResourceBuilder<WireMockServerResource> wiremock)
|
||||
{
|
||||
Guard.NotNull(wiremock).Resource.Arguments.WithWatchStaticMappings = true;
|
||||
return wiremock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the path for the (static) mapping json files.
|
||||
/// </summary>
|
||||
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
||||
/// <param name="mappingsPath">The local path.</param>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> WithMappingsPath(this IResourceBuilder<WireMockServerResource> wiremock, string mappingsPath)
|
||||
{
|
||||
return Guard.NotNull(wiremock)
|
||||
.WithBindMount(Guard.NotNullOrWhiteSpace(mappingsPath), DefaultLinuxMappingsPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the admin username and password for accessing the admin interface from WireMock.Net via HTTP.
|
||||
/// </summary>
|
||||
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
||||
/// <param name="username">The admin username.</param>
|
||||
/// <param name="password">The admin password.</param>
|
||||
/// <returns>A reference to the <see cref="IResourceBuilder{WireMockServerResource}"/>.</returns>
|
||||
public static IResourceBuilder<WireMockServerResource> WithAdminUserNameAndPassword(this IResourceBuilder<WireMockServerResource> wiremock, string username, string password)
|
||||
{
|
||||
Guard.NotNull(wiremock);
|
||||
|
||||
wiremock.Resource.Arguments.AdminUsername = Guard.NotNull(username);
|
||||
wiremock.Resource.Arguments.AdminPassword = Guard.NotNull(password);
|
||||
return wiremock;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use WireMock Client's AdminApiMappingBuilder to configure the WireMock.Net resource.
|
||||
/// </summary>
|
||||
/// <param name="wiremock">The <see cref="IResourceBuilder{WireMockServerResource}"/>.</param>
|
||||
/// <param name="configure">Delegate that will be invoked to configure the WireMock.Net resource.</param>
|
||||
/// <returns></returns>
|
||||
public static IResourceBuilder<WireMockServerResource> WithApiMappingBuilder(this IResourceBuilder<WireMockServerResource> wiremock, Func<AdminApiMappingBuilder, Task> configure)
|
||||
{
|
||||
Guard.NotNull(wiremock);
|
||||
|
||||
wiremock.ApplicationBuilder.Services.TryAddLifecycleHook<WireMockServerLifecycleHook>();
|
||||
wiremock.Resource.Arguments.ApiMappingBuilder = configure;
|
||||
|
||||
return wiremock;
|
||||
}
|
||||
}
|
||||
52
src/WireMock.Net.Aspire/WireMockServerLifecycleHook.cs
Normal file
52
src/WireMock.Net.Aspire/WireMockServerLifecycleHook.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Aspire.Hosting.ApplicationModel;
|
||||
using Aspire.Hosting.Lifecycle;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RestEase;
|
||||
using WireMock.Client;
|
||||
using WireMock.Client.Extensions;
|
||||
|
||||
namespace WireMock.Net.Aspire;
|
||||
|
||||
internal class WireMockServerLifecycleHook(ResourceLoggerService loggerService) : IDistributedApplicationLifecycleHook
|
||||
{
|
||||
public async Task AfterResourcesCreatedAsync(DistributedApplicationModel appModel, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var wireMockServerResources = appModel.Resources
|
||||
.OfType<WireMockServerResource>()
|
||||
.Where(resource => resource.Arguments.ApiMappingBuilder is not null)
|
||||
.ToArray();
|
||||
|
||||
if (wireMockServerResources.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var wireMockServerResource in wireMockServerResources)
|
||||
{
|
||||
var endpoint = wireMockServerResource.GetEndpoint();
|
||||
if (endpoint.IsAllocated)
|
||||
{
|
||||
var adminApi = CreateWireMockAdminApi(wireMockServerResource);
|
||||
|
||||
var logger = loggerService.GetLogger(wireMockServerResource);
|
||||
logger.LogInformation("Checking Health status from WireMock.Net");
|
||||
|
||||
await adminApi.WaitForHealthAsync(cancellationToken: cancellationToken);
|
||||
|
||||
logger.LogInformation("Calling ApiMappingBuilder to add mappings to WireMock.Net");
|
||||
var mappingBuilder = adminApi.GetMappingBuilder();
|
||||
await wireMockServerResource.Arguments.ApiMappingBuilder!.Invoke(mappingBuilder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IWireMockAdminApi CreateWireMockAdminApi(WireMockServerResource resource)
|
||||
{
|
||||
var adminApi = RestClient.For<IWireMockAdminApi>(resource.GetEndpoint().Url);
|
||||
return resource.Arguments.HasBasicAuthentication ?
|
||||
adminApi.WithAuthorization(resource.Arguments.AdminUsername!, resource.Arguments.AdminPassword!) :
|
||||
adminApi;
|
||||
}
|
||||
}
|
||||
33
src/WireMock.Net.Aspire/WireMockServerResource.cs
Normal file
33
src/WireMock.Net.Aspire/WireMockServerResource.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright © WireMock.Net
|
||||
|
||||
using Stef.Validation;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Aspire.Hosting.ApplicationModel;
|
||||
|
||||
/// <summary>
|
||||
/// A resource that represents a WireMock.Net Server.
|
||||
/// </summary>
|
||||
public class WireMockServerResource : ContainerResource, IResourceWithServiceDiscovery
|
||||
{
|
||||
internal WireMockServerArguments Arguments { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WireMockServerResource"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the resource.</param>
|
||||
/// <param name="arguments">The arguments to start the WireMock.Net Server.</param>
|
||||
public WireMockServerResource(string name, WireMockServerArguments arguments) : base(name)
|
||||
{
|
||||
Arguments = Guard.NotNull(arguments);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets an endpoint reference.
|
||||
/// </summary>
|
||||
/// <returns>An <see cref="EndpointReference"/> object representing the endpoint reference.</returns>
|
||||
public EndpointReference GetEndpoint()
|
||||
{
|
||||
return new EndpointReference(this, "http");
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,6 @@
|
||||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using DotNet.Testcontainers.Containers;
|
||||
using JetBrains.Annotations;
|
||||
using RestEase;
|
||||
@@ -103,4 +101,4 @@ public sealed class WireMockContainer : DockerContainer
|
||||
}
|
||||
|
||||
private Uri GetPublicUri() => new UriBuilder(Uri.UriSchemeHttp, Hostname, GetMappedPublicPort(ContainerPort)).Uri;
|
||||
}
|
||||
}
|
||||
16
test/WireMock.Net.Aspire.TestAppHost/Program.cs
Normal file
16
test/WireMock.Net.Aspire.TestAppHost/Program.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using WireMock.Net.Aspire.TestAppHost;
|
||||
|
||||
var builder = DistributedApplication.CreateBuilder(args);
|
||||
|
||||
var mappingsPath = Path.Combine(Directory.GetCurrentDirectory(), "WireMockMappings");
|
||||
|
||||
builder
|
||||
.AddWireMock("wiremock-service")
|
||||
.WithAdminUserNameAndPassword($"user-{Guid.NewGuid()}", $"pwd-{Guid.NewGuid()}")
|
||||
.WithMappingsPath(mappingsPath)
|
||||
.WithWatchStaticMappings()
|
||||
.WithApiMappingBuilder(WeatherForecastApiMock.BuildAsync);
|
||||
|
||||
await builder
|
||||
.Build()
|
||||
.RunAsync();
|
||||
@@ -0,0 +1,36 @@
|
||||
using WireMock.Client.Builders;
|
||||
|
||||
namespace WireMock.Net.Aspire.TestAppHost;
|
||||
|
||||
internal class WeatherForecastApiMock
|
||||
{
|
||||
public static async Task BuildAsync(AdminApiMappingBuilder builder)
|
||||
{
|
||||
var summaries = new[]
|
||||
{
|
||||
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
|
||||
};
|
||||
|
||||
builder.Given(b => b
|
||||
.WithRequest(request => request
|
||||
.UsingGet()
|
||||
.WithPath("/weatherforecast2")
|
||||
)
|
||||
.WithResponse(response => response
|
||||
.WithHeaders(h => h.Add("Content-Type", "application/json"))
|
||||
.WithBodyAsJson(() => Enumerable.Range(1, 5).Select(index =>
|
||||
new WeatherForecast
|
||||
(
|
||||
DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
|
||||
Random.Shared.Next(-20, 55),
|
||||
summaries[Random.Shared.Next(summaries.Length)]
|
||||
))
|
||||
.ToArray())
|
||||
)
|
||||
);
|
||||
|
||||
await builder.BuildAndPostAsync();
|
||||
}
|
||||
}
|
||||
|
||||
internal record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary);
|
||||
@@ -0,0 +1,30 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<IsAspireHost>true</IsAspireHost>
|
||||
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<!-- https://learn.microsoft.com/en-us/dotnet/aspire/extensibility/custom-resources?tabs=windows#create-library-for-resource-extension -->
|
||||
<ProjectReference Include="..\..\src\WireMock.Net.Aspire\WireMock.Net.Aspire.csproj" IsAspireProjectResource="false" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aspire.Hosting.AppHost" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="WireMockMappings\*.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"Guid": "173d495f-940e-4b86-a1f4-4f0fc7be8b8b",
|
||||
"Request": {
|
||||
"Path": "/weatherforecast",
|
||||
"Methods": [
|
||||
"get"
|
||||
]
|
||||
},
|
||||
"Response": {
|
||||
"BodyAsJson": [
|
||||
{
|
||||
"date": "2024-05-24",
|
||||
"temperatureC": -10,
|
||||
"summary": "Freezing"
|
||||
},
|
||||
{
|
||||
"date": "2024-05-25",
|
||||
"temperatureC": 33,
|
||||
"summary": "Hot"
|
||||
}
|
||||
],
|
||||
"Headers": {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
}
|
||||
}
|
||||
62
test/WireMock.Net.Aspire.Tests/DockerUtils.cs
Normal file
62
test/WireMock.Net.Aspire.Tests/DockerUtils.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
75
test/WireMock.Net.Aspire.Tests/IntegrationTests.cs
Normal file
75
test/WireMock.Net.Aspire.Tests/IntegrationTests.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
134
test/WireMock.Net.Aspire.Tests/WireMockServerArgumentsTests.cs
Normal file
134
test/WireMock.Net.Aspire.Tests/WireMockServerArgumentsTests.cs
Normal 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");
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
<SignAssembly>true</SignAssembly>
|
||||
<AssemblyOriginatorKeyFile>../../src/WireMock.Net/WireMock.Net.snk</AssemblyOriginatorKeyFile>
|
||||
|
||||
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
|
||||
|
||||
<!--https://developercommunity.visualstudio.com/content/problem/26347/unit-tests-fail-with-fileloadexception-newtonsoftj-1.html-->
|
||||
@@ -70,7 +69,6 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="System.Threading" Version="4.3.0" />
|
||||
<PackageReference Include="RestEase" Version="1.5.7" />
|
||||
@@ -108,7 +106,7 @@
|
||||
<PackageReference Include="JsonConverter.System.Text.Json" Version="0.5.0" />
|
||||
|
||||
<PackageReference Include="Google.Protobuf" Version="3.25.1" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.59.0" />
|
||||
<PackageReference Include="Grpc.Net.Client" Version="2.60.0" />
|
||||
<PackageReference Include="Grpc.Tools" Version="2.60.0">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
@@ -132,10 +130,10 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="cert.pem">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="Grpc\greet.proto">
|
||||
<GrpcServices>Client</GrpcServices>
|
||||
<GrpcServices>Client</GrpcServices>
|
||||
</None>
|
||||
<None Update="responsebody.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
|
||||
@@ -931,7 +931,7 @@ public class WireMockServerProxyTests
|
||||
//Arrange
|
||||
var wireMockServerSettings = new WireMockServerSettings
|
||||
{
|
||||
Urls = new[] { "http://localhost:9091" },
|
||||
Urls = new[] { "http://localhost:19091" },
|
||||
ProxyAndRecordSettings = new ProxyAndRecordSettings
|
||||
{
|
||||
Url = "http://postman-echo.com",
|
||||
@@ -949,13 +949,13 @@ public class WireMockServerProxyTests
|
||||
var request = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri("http://localhost:9091/post"),
|
||||
RequestUri = new Uri("http://localhost:19091/post"),
|
||||
Content = new StringContent(requestBody)
|
||||
};
|
||||
var request2 = new HttpRequestMessage
|
||||
{
|
||||
Method = HttpMethod.Post,
|
||||
RequestUri = new Uri("http://localhost:9091/post"),
|
||||
RequestUri = new Uri("http://localhost:19091/post"),
|
||||
Content = new StringContent(requestBody)
|
||||
};
|
||||
server.ResetMappings();
|
||||
|
||||
Reference in New Issue
Block a user