mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-04-07 09:23:32 +02:00
Compare commits
3 Commits
2.2.0
...
Fix_WebSoc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3eca5d44ee | ||
|
|
479bb0b8ec | ||
|
|
a453e00fdb |
@@ -1,29 +1,18 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace WireMock.Net.WebApplication;
|
namespace WireMock.Net.WebApplication;
|
||||||
|
|
||||||
public class App : IHostedService
|
public class App(IWireMockService service) : IHostedService
|
||||||
{
|
{
|
||||||
private readonly IWireMockService _service;
|
|
||||||
|
|
||||||
public App(IWireMockService service)
|
|
||||||
{
|
|
||||||
_service = service;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task StartAsync(CancellationToken cancellationToken)
|
public Task StartAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_service.Start();
|
service.Start();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task StopAsync(CancellationToken cancellationToken)
|
public Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_service.Stop();
|
service.Stop();
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using Microsoft.Extensions.Hosting;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using WireMock.Settings;
|
using WireMock.Settings;
|
||||||
|
|
||||||
namespace WireMock.Net.WebApplication;
|
namespace WireMock.Net.WebApplication;
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net.Abstractions\WireMock.Net.Abstractions.csproj" />
|
|
||||||
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
<ProjectReference Include="..\..\src\WireMock.Net\WireMock.Net.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
// Copyright © WireMock.Net
|
// Copyright © WireMock.Net
|
||||||
|
|
||||||
using System;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using WireMock.Admin.Requests;
|
using WireMock.Admin.Requests;
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"WireMockServerSettings": {
|
"WireMockServerSettings": {
|
||||||
"StartAdminInterface": true,
|
"StartAdminInterface": true,
|
||||||
"Urls": [
|
"Urls": [
|
||||||
"http://localhost"
|
"http://localhost:0"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!--
|
<!--
|
||||||
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
Configure your application settings in appsettings.json. Learn more at http://go.microsoft.com/fwlink/?LinkId=786380
|
||||||
-->
|
-->
|
||||||
<system.webServer>
|
<system.webServer>
|
||||||
<handlers>
|
<handlers>
|
||||||
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
|
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
|
||||||
</handlers>
|
</handlers>
|
||||||
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
|
<httpProtocol>
|
||||||
</system.webServer>
|
<customHeaders>
|
||||||
|
<add name="X-Frame-Options" value="SAMEORIGIN" />
|
||||||
|
</customHeaders>
|
||||||
|
</httpProtocol>
|
||||||
|
<aspNetCore processPath="%LAUNCHER_PATH%" arguments="%LAUNCHER_ARGS%" stdoutLogEnabled="false" stdoutLogFile=".\logs\stdout" forwardWindowsAuthToken="false" />
|
||||||
|
</system.webServer>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -122,6 +122,14 @@ internal class WireMockMiddleware(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Transition scenario state immediately after matching, before any delay (global or
|
||||||
|
// per-mapping) so that concurrent retries arriving during a delay period see the
|
||||||
|
// updated state and match the correct next mapping instead of re-matching this one.
|
||||||
|
if (targetMapping.Scenario != null)
|
||||||
|
{
|
||||||
|
UpdateScenarioState(targetMapping);
|
||||||
|
}
|
||||||
|
|
||||||
if (!targetMapping.IsAdminInterface && options.RequestProcessingDelay > TimeSpan.Zero)
|
if (!targetMapping.IsAdminInterface && options.RequestProcessingDelay > TimeSpan.Zero)
|
||||||
{
|
{
|
||||||
await Task.Delay(options.RequestProcessingDelay.Value).ConfigureAwait(false);
|
await Task.Delay(options.RequestProcessingDelay.Value).ConfigureAwait(false);
|
||||||
@@ -147,11 +155,6 @@ internal class WireMockMiddleware(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetMapping.Scenario != null)
|
|
||||||
{
|
|
||||||
UpdateScenarioState(targetMapping);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetMapping.IsAdminInterface && targetMapping.Webhooks?.Length > 0)
|
if (!targetMapping.IsAdminInterface && targetMapping.Webhooks?.Length > 0)
|
||||||
{
|
{
|
||||||
await SendToWebhooksAsync(targetMapping, request, response).ConfigureAwait(false);
|
await SendToWebhooksAsync(targetMapping, request, response).ConfigureAwait(false);
|
||||||
|
|||||||
@@ -373,7 +373,7 @@ public class StatefulBehaviorTests
|
|||||||
// Act and Assert
|
// Act and Assert
|
||||||
server.SetScenarioState(scenario, "Buy milk");
|
server.SetScenarioState(scenario, "Buy milk");
|
||||||
server.Scenarios.First(s => s.Name == scenario).Should().BeEquivalentTo(new { Name = scenario, NextState = "Buy milk" });
|
server.Scenarios.First(s => s.Name == scenario).Should().BeEquivalentTo(new { Name = scenario, NextState = "Buy milk" });
|
||||||
|
|
||||||
var getResponse1 = await client.GetStringAsync("/todo/items", cancelationToken);
|
var getResponse1 = await client.GetStringAsync("/todo/items", cancelationToken);
|
||||||
getResponse1.Should().Be("Buy milk");
|
getResponse1.Should().Be("Buy milk");
|
||||||
|
|
||||||
@@ -413,6 +413,120 @@ public class StatefulBehaviorTests
|
|||||||
action.Should().ThrowAsync<HttpRequestException>();
|
action.Should().ThrowAsync<HttpRequestException>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Scenarios_FirstRequestWithDelay_StateTransitions_BeforeDelayCompletes()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cancellationToken = TestContext.Current.CancellationToken;
|
||||||
|
var path = $"/foo_{Guid.NewGuid()}";
|
||||||
|
using var server = WireMockServer.Start();
|
||||||
|
|
||||||
|
// Mapping 1: start state, has a 500 ms delay
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path).UsingGet())
|
||||||
|
.InScenario("1260")
|
||||||
|
.WillSetStateTo("State1")
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithBody("delayed response")
|
||||||
|
.WithDelay(TimeSpan.FromMilliseconds(500)));
|
||||||
|
|
||||||
|
// Mapping 2: only matches after state has transitioned to "State1"
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path).UsingGet())
|
||||||
|
.InScenario("1260")
|
||||||
|
.WhenStateIs("State1")
|
||||||
|
.RespondWith(Response.Create().WithBody("immediate response"));
|
||||||
|
|
||||||
|
var client = new HttpClient();
|
||||||
|
|
||||||
|
// Act: fire request 1 but don't await it yet — it will sit in a 500 ms delay
|
||||||
|
var request1Task = client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
|
||||||
|
// Give the server a moment to match & transition state before the delay completes
|
||||||
|
await Task.Delay(100, cancellationToken);
|
||||||
|
|
||||||
|
// Request 2 is sent while request 1 is still being delayed.
|
||||||
|
// After the fix the state has already transitioned, so request 2 matches Mapping 2.
|
||||||
|
var response2 = await client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
|
||||||
|
var response1 = await request1Task;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response1.Should().Be("delayed response");
|
||||||
|
response2.Should().Be("immediate response");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Scenarios_WithGlobalRequestProcessingDelay_StateTransitions_BeforeDelayCompletes()
|
||||||
|
{
|
||||||
|
// Arrange: use the global RequestProcessingDelay instead of a per-mapping delay
|
||||||
|
var cancellationToken = TestContext.Current.CancellationToken;
|
||||||
|
var path = $"/foo_{Guid.NewGuid()}";
|
||||||
|
using var server = WireMockServer.Start();
|
||||||
|
server.AddGlobalProcessingDelay(TimeSpan.FromMilliseconds(500));
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path).UsingGet())
|
||||||
|
.InScenario("s")
|
||||||
|
.WillSetStateTo("State1")
|
||||||
|
.RespondWith(Response.Create().WithBody("delayed response"));
|
||||||
|
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path).UsingGet())
|
||||||
|
.InScenario("s")
|
||||||
|
.WhenStateIs("State1")
|
||||||
|
.RespondWith(Response.Create().WithBody("immediate response"));
|
||||||
|
|
||||||
|
var client = new HttpClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var request1Task = client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
await Task.Delay(100, cancellationToken);
|
||||||
|
var response2 = await client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
var response1 = await request1Task;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
response1.Should().Be("delayed response");
|
||||||
|
response2.Should().Be("immediate response");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Scenarios_WithDelay_And_TimesInSameState_Should_Transition_After_Required_Hits()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var cancellationToken = TestContext.Current.CancellationToken;
|
||||||
|
var path = $"/foo_{Guid.NewGuid()}";
|
||||||
|
using var server = WireMockServer.Start();
|
||||||
|
|
||||||
|
// Mapping 1: requires 2 hits before transitioning; has a short delay
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path).UsingGet())
|
||||||
|
.InScenario("s")
|
||||||
|
.WillSetStateTo("State1", 2)
|
||||||
|
.RespondWith(Response.Create()
|
||||||
|
.WithBody("first")
|
||||||
|
.WithDelay(TimeSpan.FromMilliseconds(50)));
|
||||||
|
|
||||||
|
// Mapping 2: matches after state is "State1"
|
||||||
|
server
|
||||||
|
.Given(Request.Create().WithPath(path).UsingGet())
|
||||||
|
.InScenario("s")
|
||||||
|
.WhenStateIs("State1")
|
||||||
|
.RespondWith(Response.Create().WithBody("second"));
|
||||||
|
|
||||||
|
var client = new HttpClient();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var response1 = await client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
var response2 = await client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
var response3 = await client.GetStringAsync(server.Url + path, cancellationToken);
|
||||||
|
|
||||||
|
// Assert: state only transitions after 2 hits, so request 3 is the first to match Mapping 2
|
||||||
|
response1.Should().Be("first");
|
||||||
|
response2.Should().Be("first");
|
||||||
|
response3.Should().Be("second");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Scenarios_Should_process_request_if_equals_state_and_multiple_state_defined()
|
public async Task Scenarios_Should_process_request_if_equals_state_and_multiple_state_defined()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -811,10 +811,10 @@ public class WebSocketIntegrationTests(ITestOutputHelper output, ITestContextAcc
|
|||||||
// Act
|
// Act
|
||||||
await client.SendAsync(new ArraySegment<byte>(testData), WebSocketMessageType.Binary, true, _ct);
|
await client.SendAsync(new ArraySegment<byte>(testData), WebSocketMessageType.Binary, true, _ct);
|
||||||
|
|
||||||
var receivedData = await client.ReceiveAsBytesAsync(cancellationToken: _ct);
|
|
||||||
|
|
||||||
await Task.Delay(500, _ct);
|
await Task.Delay(500, _ct);
|
||||||
|
|
||||||
|
var receivedData = await client.ReceiveAsBytesAsync(cancellationToken: _ct);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
receivedData.Should().BeEquivalentTo(testData, "binary data should be proxied and echoed back");
|
receivedData.Should().BeEquivalentTo(testData, "binary data should be proxied and echoed back");
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Meziantou.Extensions.Logging.Xunit.v3" Version="1.1.24" />
|
<PackageReference Include="Meziantou.Extensions.Logging.Xunit.v3" Version="1.1.24" />
|
||||||
<PackageReference Include="Verify.XunitV3" Version="31.13.2" />
|
<PackageReference Include="Verify.XunitV3" Version="31.13.2" />
|
||||||
|
<PackageReference Include="xRetry.v3" Version="1.0.0-rc3" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
|||||||
Reference in New Issue
Block a user