Files
WireMock.Net/test/WireMock.Net.Tests/WebSockets/WireMockServerWebSocketIntegrationTests.cs
Stef Heyenrath a292f28dda Version 2.x (#1359)
* Version 2.x

* Setup .NET 9

* 12

* cleanup some #if for NETSTANDARD1_3

* cleanup + fix tests for net8

* openapi

* NO ConfigureAwait(false) + cleanup

* .

* #endif

* HashSet

* WireMock.Net.NUnit

* HttpContext

* Add WebSockets (#1423)

* Add WebSockets

* Add tests

* fix

* more tests

* Add tests

* ...

* remove IOwin

* -

* tests

* fluent

* ok

* match

* .

* byte[]

* x

* func

* func

* byte

* trans

* ...

* frameworks.........

* jmes

* xxx

* sc

* using var httpClient = new HttpClient();

* usings

* maxRetries

* up

* xunit v3

* ct

* ---

* ct

* ct2

* T Unit

* WireMock.Net.TUnitTests / 10

* t unit first

* --project

* no tunit

* t2

* --project

* --project

* ci -  --project

* publish ./test/wiremock-coverage.xml

* windows

* .

* log

* ...

* log

* goed

* BodyType

* .

* .

* --scenario

* ...

* pact

* ct

* .

* WireMock.Net.RestClient.AwesomeAssertions (#1427)

* WireMock.Net.RestClient.AwesomeAssertions

* ok

* atpath

* fix test

* sonar fixes

* ports

* proxy test

* FIX?

* ---

* await Task.Delay(100, _ct);

* ?

* --project

* Aspire: use IDistributedApplicationEventingSubscriber (#1428)

* broadcast

* ok

* more tsts

* .

* Collection

* up

* .

* 2

* remove nfluent

* <VersionPrefix>2.0.0-preview-02</VersionPrefix>

* ...

* .

* nuget icon

* .

* <PackageReference Include="JmesPath.Net" Version="1.1.0" />

* x

* 500

* .

* fix some warnings

* ws
2026-03-11 17:02:47 +01:00

564 lines
20 KiB
C#

// Copyright © WireMock.Net
using System.Net.WebSockets;
using WireMock.Net.Xunit;
using WireMock.RequestBuilders;
using WireMock.ResponseBuilders;
using WireMock.Server;
using WireMock.Settings;
namespace WireMock.Net.Tests.WebSockets;
[Collection(nameof(WireMockServerWebSocketIntegrationTests))]
public class WireMockServerWebSocketIntegrationTests(ITestOutputHelper output, ITestContextAccessor testContext)
{
private readonly CancellationToken _ct = testContext.Current.CancellationToken;
[Fact]
public async Task GetWebSocketConnections_Should_Return_All_Active_Connections()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
server
.Given(Request.Create()
.WithPath("/ws/test")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithEcho()
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
using var client3 = new ClientWebSocket();
var uri = new Uri($"{server.Url}/ws/test");
// Act
await client1.ConnectAsync(uri, _ct);
await client2.ConnectAsync(uri, _ct);
await client3.ConnectAsync(uri, _ct);
await Task.Delay(500, _ct);
// Assert
var connections = server.GetWebSocketConnections();
connections.Should().HaveCount(3);
connections.Should().AllSatisfy(c => c.Should().NotBeNull());
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client3.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
[Fact]
public async Task GetWebSocketConnections_Should_Return_Empty_When_No_Connections()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
server
.Given(Request.Create()
.WithPath("/ws/test")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithEcho()
)
);
// Act
var connections = server.GetWebSocketConnections();
// Assert
connections.Should().BeEmpty();
}
[Fact]
public async Task GetWebSocketConnections_Should_Return_Connections_For_Specific_Mapping()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var mapping1Guid = Guid.NewGuid();
var mapping2Guid = Guid.NewGuid();
server
.Given(Request.Create()
.WithPath("/ws/echo1")
.WithWebSocketUpgrade()
)
.WithGuid(mapping1Guid)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithEcho()
)
);
server
.Given(Request.Create()
.WithPath("/ws/echo2")
.WithWebSocketUpgrade()
)
.WithGuid(mapping2Guid)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithEcho()
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
using var client3 = new ClientWebSocket();
var uri1 = new Uri($"{server.Url}/ws/echo1");
var uri2 = new Uri($"{server.Url}/ws/echo2");
// Act
await client1.ConnectAsync(uri1, _ct);
await client2.ConnectAsync(uri1, _ct);
await client3.ConnectAsync(uri2, _ct);
await Task.Delay(500, _ct);
// Assert
var allConnections = server.GetWebSocketConnections();
allConnections.Should().HaveCount(3);
var mapping1Connections = server.GetWebSocketConnections(mapping1Guid);
mapping1Connections.Should().HaveCount(2);
var mapping2Connections = server.GetWebSocketConnections(mapping2Guid);
mapping2Connections.Should().HaveCount(1);
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client3.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
[Fact]
public async Task AbortWebSocketConnectionAsync_Should_Close_Specific_Connection()
{
// Arrange
var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
server
.Given(Request.Create()
.WithPath("/ws/test")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(30))
.WithEcho()
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Url}/ws/test");
await client1.ConnectAsync(uri, _ct);
await client2.ConnectAsync(uri, _ct);
await Task.Delay(500, _ct);
var connections = server.GetWebSocketConnections();
connections.Should().HaveCount(2);
var connectionIdToAbort = connections.First().ConnectionId;
// Act
await server.AbortWebSocketConnectionAsync(connectionIdToAbort, "Abort by test", _ct);
// Assert
var remainingConnections = server.GetWebSocketConnections();
remainingConnections.Should().HaveCount(1);
var remainingConnection = remainingConnections.First();
remainingConnection.ConnectionId.Should().NotBe(connectionIdToAbort);
}
[Fact]
public async Task BroadcastToWebSocketsAsync_Should_Broadcast_Text_To_Specific_Mapping()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var broadcastMessage = "Server broadcast message";
var mappingGuid = Guid.NewGuid();
server
.Given(Request.Create()
.WithPath("/ws/broadcast")
.WithWebSocketUpgrade()
)
.WithGuid(mappingGuid)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithMessageHandler(async (message, context) =>
{
if (message.MessageType == WebSocketMessageType.Text)
{
var text = message.Text ?? string.Empty;
if (text.StartsWith("ready"))
{
await context.SendAsync("ready!");
}
}
})
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Url}/ws/broadcast");
await client1.ConnectAsync(uri, _ct);
await client2.ConnectAsync(uri, _ct);
// Signal ready
await client1.SendAsync("ready", cancellationToken: _ct);
await client2.SendAsync("ready", cancellationToken: _ct);
var text1 = await client1.ReceiveAsTextAsync(cancellationToken: _ct);
var text2 = await client2.ReceiveAsTextAsync(cancellationToken: _ct);
text1.Should().Be("ready!");
text2.Should().Be("ready!");
// Act
await server.BroadcastToWebSocketsAsync(mappingGuid, broadcastMessage, cancellationToken: _ct);
// Assert
var received1 = await client1.ReceiveAsTextAsync(cancellationToken: _ct);
var received2 = await client2.ReceiveAsTextAsync(cancellationToken: _ct);
received1.Should().Be(broadcastMessage);
received2.Should().Be(broadcastMessage);
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
[Fact]
public async Task BroadcastToWebSocketsAsync_Should_Broadcast_Binary_To_Specific_Mapping()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var broadcastData = new byte[] { 0x01, 0x02, 0x03, 0x04 };
var mappingGuid = Guid.NewGuid();
server
.Given(Request.Create()
.WithPath("/ws/broadcast-binary")
.WithWebSocketUpgrade()
)
.WithGuid(mappingGuid)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithMessageHandler(async (message, context) =>
{
if (message.MessageType == WebSocketMessageType.Text)
{
var text = message.Text ?? string.Empty;
if (text.StartsWith("ready"))
{
await context.SendAsync("ready!");
}
}
})
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Url}/ws/broadcast-binary");
await client1.ConnectAsync(uri, _ct);
await client2.ConnectAsync(uri, _ct);
// Signal ready
await client1.SendAsync("ready", cancellationToken: _ct);
await client2.SendAsync("ready", cancellationToken: _ct);
var text1 = await client1.ReceiveAsTextAsync(cancellationToken: _ct);
var text2 = await client2.ReceiveAsTextAsync(cancellationToken: _ct);
text1.Should().Be("ready!");
text2.Should().Be("ready!");
// Act
await server.BroadcastToWebSocketsAsync(mappingGuid, broadcastData, cancellationToken: _ct);
// Assert
var received1 = await client1.ReceiveAsBytesAsync(cancellationToken: _ct);
var received2 = await client2.ReceiveAsBytesAsync(cancellationToken: _ct);
received1.Should().BeEquivalentTo(broadcastData);
received2.Should().BeEquivalentTo(broadcastData);
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
[Fact]
public async Task BroadcastToAllWebSocketsAsync_Should_Broadcast_Text_To_All_Mappings()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var broadcastMessage = "Broadcast to all mappings";
server
.Given(Request.Create()
.WithPath("/ws/mapping1")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithMessageHandler(async (message, context) =>
{
if (message.MessageType == WebSocketMessageType.Text)
{
var text = message.Text ?? string.Empty;
if (text.StartsWith("ready"))
{
await context.SendAsync("ready!");
}
}
})
)
);
server
.Given(Request.Create()
.WithPath("/ws/mapping2")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithMessageHandler(async (message, context) =>
{
if (message.MessageType == WebSocketMessageType.Text)
{
var text = message.Text ?? string.Empty;
if (text.StartsWith("ready"))
{
await context.SendAsync("ready!");
}
}
})
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri1 = new Uri($"{server.Url}/ws/mapping1");
var uri2 = new Uri($"{server.Url}/ws/mapping2");
await client1.ConnectAsync(uri1, _ct);
await client2.ConnectAsync(uri2, _ct);
// Signal ready
await client1.SendAsync("ready", cancellationToken: _ct);
await client2.SendAsync("ready", cancellationToken: _ct);
var text1 = await client1.ReceiveAsTextAsync(cancellationToken: _ct);
var text2 = await client2.ReceiveAsTextAsync(cancellationToken: _ct);
text1.Should().Be("ready!");
text2.Should().Be("ready!");
// Act
await server.BroadcastToAllWebSocketsAsync(broadcastMessage, cancellationToken: _ct);
// Assert - both clients from different mappings should receive the broadcast
var received1 = await client1.ReceiveAsTextAsync(cancellationToken: _ct);
var received2 = await client2.ReceiveAsTextAsync(cancellationToken: _ct);
received1.Should().Be(broadcastMessage);
received2.Should().Be(broadcastMessage);
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
[Fact]
public async Task BroadcastToAllWebSocketsAsync_Should_Broadcast_Binary_To_All_Mappings()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var broadcastData = new byte[] { 0xAA, 0xBB, 0xCC };
server
.Given(Request.Create()
.WithPath("/ws/mapping1")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithMessageHandler(async (message, context) =>
{
if (message.MessageType == WebSocketMessageType.Text)
{
var text = message.Text ?? string.Empty;
if (text.StartsWith("ready"))
{
await context.SendAsync("ready!");
}
}
})
)
);
server
.Given(Request.Create()
.WithPath("/ws/mapping2")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithMessageHandler(async (message, context) =>
{
if (message.MessageType == WebSocketMessageType.Text)
{
var text = message.Text ?? string.Empty;
if (text.StartsWith("ready"))
{
await context.SendAsync("ready!");
}
}
})
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri1 = new Uri($"{server.Url}/ws/mapping1");
var uri2 = new Uri($"{server.Url}/ws/mapping2");
await client1.ConnectAsync(uri1, _ct);
await client2.ConnectAsync(uri2, _ct);
// Signal ready
await client1.SendAsync("ready", cancellationToken: _ct);
await client2.SendAsync("ready", cancellationToken: _ct);
var text1 = await client1.ReceiveAsTextAsync(cancellationToken: _ct);
var text2 = await client2.ReceiveAsTextAsync(cancellationToken: _ct);
text1.Should().Be("ready!");
text2.Should().Be("ready!");
// Act
await server.BroadcastToAllWebSocketsAsync(broadcastData, cancellationToken: _ct);
// Assert
var received1 = await client1.ReceiveAsBytesAsync(cancellationToken: _ct);
var received2 = await client2.ReceiveAsBytesAsync(cancellationToken: _ct);
received1.Should().BeEquivalentTo(broadcastData);
received2.Should().BeEquivalentTo(broadcastData);
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
[Fact]
public async Task GetWebSocketConnections_Should_Update_After_Client_Disconnect()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
server
.Given(Request.Create()
.WithPath("/ws/test")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithCloseTimeout(TimeSpan.FromSeconds(5))
.WithEcho()
)
);
using var client1 = new ClientWebSocket();
using var client2 = new ClientWebSocket();
var uri = new Uri($"{server.Url}/ws/test");
await client1.ConnectAsync(uri, _ct);
await client2.ConnectAsync(uri, _ct);
await Task.Delay(500, _ct);
var initialConnections = server.GetWebSocketConnections();
initialConnections.Should().HaveCount(2);
// Act
await client1.CloseAsync(WebSocketCloseStatus.NormalClosure, "Disconnect", _ct);
await Task.Delay(500, _ct);
// Assert
var remainingConnections = server.GetWebSocketConnections();
remainingConnections.Should().HaveCount(1);
await client2.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", _ct);
}
}