This commit is contained in:
Stef Heyenrath
2026-02-10 19:11:14 +01:00
parent ceab19514f
commit c1cf61862e
11 changed files with 125735 additions and 76 deletions

View File

@@ -136,34 +136,34 @@ public static class Program
// Handle different commands // Handle different commands
if (text.StartsWith("/help")) if (text.StartsWith("/help"))
{ {
await context.SendTextAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>"); await context.SendAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>");
} }
else if (text.StartsWith("/time")) else if (text.StartsWith("/time"))
{ {
await context.SendTextAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); await context.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
} }
else if (text.StartsWith("/echo ")) else if (text.StartsWith("/echo "))
{ {
await context.SendTextAsync(text.Substring(6)); await context.SendAsync(text.Substring(6));
} }
else if (text.StartsWith("/upper ")) else if (text.StartsWith("/upper "))
{ {
await context.SendTextAsync(text.Substring(7).ToUpper()); await context.SendAsync(text.Substring(7).ToUpper());
} }
else if (text.StartsWith("/reverse ")) else if (text.StartsWith("/reverse "))
{ {
var toReverse = text.Substring(9); var toReverse = text.Substring(9);
var reversed = new string(toReverse.Reverse().ToArray()); var reversed = new string(toReverse.Reverse().ToArray());
await context.SendTextAsync(reversed); await context.SendAsync(reversed);
} }
else if (text == "/quit") else if (text == "/quit")
{ {
await context.SendTextAsync("Goodbye!"); await context.SendAsync("Goodbye!");
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client requested disconnect"); await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Client requested disconnect");
} }
else else
{ {
await context.SendTextAsync($"Unknown command: {text}. Type /help for available commands."); await context.SendAsync($"Unknown command: {text}. Type /help for available commands.");
} }
} }
}) })
@@ -263,7 +263,7 @@ public static class Program
.WithWebSocket(ws => ws .WithWebSocket(ws => ws
.WithMessageHandler(async (msg, ctx) => .WithMessageHandler(async (msg, ctx) =>
{ {
await ctx.SendTextAsync("Welcome to the game lobby! Type 'ready' to start or 'quit' to leave."); await ctx.SendAsync("Welcome to the game lobby! Type 'ready' to start or 'quit' to leave.");
}) })
) )
); );
@@ -285,16 +285,16 @@ public static class Program
if (text == "ready") if (text == "ready")
{ {
ctx.SetScenarioState("Playing"); ctx.SetScenarioState("Playing");
await ctx.SendTextAsync("Game started! Type 'attack' to attack, 'defend' to defend, or 'quit' to exit."); await ctx.SendAsync("Game started! Type 'attack' to attack, 'defend' to defend, or 'quit' to exit.");
} }
else if (text == "quit") else if (text == "quit")
{ {
await ctx.SendTextAsync("You left the lobby. Goodbye!"); await ctx.SendAsync("You left the lobby. Goodbye!");
await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Player quit"); await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Player quit");
} }
else else
{ {
await ctx.SendTextAsync("In lobby. Type 'ready' to start or 'quit' to leave."); await ctx.SendAsync("In lobby. Type 'ready' to start or 'quit' to leave.");
} }
}) })
) )
@@ -316,21 +316,21 @@ public static class Program
if (text == "attack") if (text == "attack")
{ {
await ctx.SendTextAsync("You attacked! Critical hit! 💥"); await ctx.SendAsync("You attacked! Critical hit! 💥");
} }
else if (text == "defend") else if (text == "defend")
{ {
await ctx.SendTextAsync("You defended! Shield up! 🛡️"); await ctx.SendAsync("You defended! Shield up! 🛡️");
} }
else if (text == "quit") else if (text == "quit")
{ {
ctx.SetScenarioState("GameOver"); ctx.SetScenarioState("GameOver");
await ctx.SendTextAsync("Game over! Thanks for playing."); await ctx.SendAsync("Game over! Thanks for playing.");
await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Game ended"); await ctx.CloseAsync(WebSocketCloseStatus.NormalClosure, "Game ended");
} }
else else
{ {
await ctx.SendTextAsync("Unknown action. Type 'attack', 'defend', or 'quit'."); await ctx.SendAsync("Unknown action. Type 'attack', 'defend', or 'quit'.");
} }
}) })
) )
@@ -418,7 +418,7 @@ public static class Program
.WithWebSocket(ws => ws .WithWebSocket(ws => ws
.WithMessageHandler(async (msg, ctx) => .WithMessageHandler(async (msg, ctx) =>
{ {
await ctx.SendTextAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); await ctx.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
}) })
) )
); );
@@ -440,7 +440,7 @@ public static class Program
length = msg.Text?.Length ?? 0, length = msg.Text?.Length ?? 0,
type = msg.MessageType.ToString() type = msg.MessageType.ToString()
}; };
await ctx.SendJsonAsync(response); await ctx.SendAsJsonAsync(response);
}) })
) )
); );
@@ -456,7 +456,7 @@ public static class Program
.WithAcceptProtocol("chat") .WithAcceptProtocol("chat")
.WithMessageHandler(async (msg, ctx) => .WithMessageHandler(async (msg, ctx) =>
{ {
await ctx.SendTextAsync($"Using protocol: chat. Message: {msg.Text}"); await ctx.SendAsync($"Using protocol: chat. Message: {msg.Text}");
}) })
) )
); );
@@ -546,7 +546,7 @@ public static class Program
{ {
if (message.MessageType == WebSocketMessageType.Text) if (message.MessageType == WebSocketMessageType.Text)
{ {
await context.SendTextAsync($"Echo: {message.Text}"); await context.SendAsync($"Echo: {message.Text}");
} }
}) })
) )
@@ -586,7 +586,7 @@ public static class Program
.WithWebSocket(ws => ws .WithWebSocket(ws => ws
.WithMessageHandler(async (msg, ctx) => .WithMessageHandler(async (msg, ctx) =>
{ {
await ctx.SendTextAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); await ctx.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
}) })
) )
); );
@@ -607,7 +607,7 @@ public static class Program
message = msg.Text, message = msg.Text,
connectionId = ctx.ConnectionId connectionId = ctx.ConnectionId
}; };
await ctx.SendJsonAsync(response); await ctx.SendAsJsonAsync(response);
}) })
) )
); );
@@ -626,7 +626,7 @@ public static class Program
.WithWebSocket(ws => ws .WithWebSocket(ws => ws
.WithMessageHandler(async (msg, ctx) => .WithMessageHandler(async (msg, ctx) =>
{ {
await ctx.SendTextAsync("Welcome! Type 'ready' to start."); await ctx.SendAsync("Welcome! Type 'ready' to start.");
}) })
) )
); );
@@ -645,7 +645,7 @@ public static class Program
if (msg.Text?.ToLower() == "ready") if (msg.Text?.ToLower() == "ready")
{ {
ctx.SetScenarioState("Playing"); ctx.SetScenarioState("Playing");
await ctx.SendTextAsync("Game started!"); await ctx.SendAsync("Game started!");
} }
}) })
) )

View File

@@ -64,6 +64,33 @@ internal class WebSocketBuilder : IWebSocketBuilder
return this; return this;
} }
public IWebSocketBuilder WithText(string text)
{
Guard.NotNull(text);
return WithMessageHandler(async (message, context) =>
{
await context.SendAsync(text);
});
}
public IWebSocketBuilder WithBytes(byte[] bytes)
{
Guard.NotNull(bytes);
return WithMessageHandler(async (message, context) =>
{
await context.SendAsync(bytes);
});
}
public IWebSocketBuilder WithJson(object data)
{
Guard.NotNull(data);
return WithMessageHandler(async (message, context) =>
{
await context.SendAsJsonAsync(data);
});
}
public IWebSocketBuilder WithMessageHandler(Func<WebSocketMessage, IWebSocketContext, Task> handler) public IWebSocketBuilder WithMessageHandler(Func<WebSocketMessage, IWebSocketContext, Task> handler)
{ {
MessageHandler = Guard.NotNull(handler); MessageHandler = Guard.NotNull(handler);

View File

@@ -53,7 +53,7 @@ internal class WebSocketConnectionRegistry
{ {
var tasks = _connections.Values var tasks = _connections.Values
.Where(c => c.WebSocket.State == WebSocketState.Open) .Where(c => c.WebSocket.State == WebSocketState.Open)
.Select(c => c.SendTextAsync(text, cancellationToken)); .Select(c => c.SendAsync(text, cancellationToken));
await Task.WhenAll(tasks); await Task.WhenAll(tasks);
} }

View File

@@ -1,10 +1,7 @@
// Copyright © WireMock.Net // Copyright © WireMock.Net
using System;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Text; using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Newtonsoft.Json; using Newtonsoft.Json;
using Stef.Validation; using Stef.Validation;
@@ -68,7 +65,7 @@ public class WireMockWebSocketContext : IWebSocketContext
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SendTextAsync(string text, CancellationToken cancellationToken = default) public Task SendAsync(string text, CancellationToken cancellationToken = default)
{ {
var bytes = Encoding.UTF8.GetBytes(text); var bytes = Encoding.UTF8.GetBytes(text);
return WebSocket.SendAsync( return WebSocket.SendAsync(
@@ -80,7 +77,7 @@ public class WireMockWebSocketContext : IWebSocketContext
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SendBytesAsync(byte[] bytes, CancellationToken cancellationToken = default) public Task SendAsync(byte[] bytes, CancellationToken cancellationToken = default)
{ {
return WebSocket.SendAsync( return WebSocket.SendAsync(
new ArraySegment<byte>(bytes), new ArraySegment<byte>(bytes),
@@ -91,10 +88,10 @@ public class WireMockWebSocketContext : IWebSocketContext
} }
/// <inheritdoc /> /// <inheritdoc />
public Task SendJsonAsync(object data, CancellationToken cancellationToken = default) public Task SendAsJsonAsync(object data, CancellationToken cancellationToken = default)
{ {
var json = JsonConvert.SerializeObject(data); var json = JsonConvert.SerializeObject(data);
return SendTextAsync(json, cancellationToken); return SendAsync(json, cancellationToken);
} }
/// <inheritdoc /> /// <inheritdoc />
@@ -124,7 +121,7 @@ public class WireMockWebSocketContext : IWebSocketContext
scenarioState.NextState = nextState; scenarioState.NextState = nextState;
scenarioState.Started = true; scenarioState.Started = true;
scenarioState.Finished = nextState == null; scenarioState.Finished = nextState == null;
// Reset counter when manually setting state // Reset counter when manually setting state
scenarioState.Counter = 0; scenarioState.Counter = 0;
} }

View File

@@ -23,6 +23,27 @@ public interface IWebSocketBuilder
[PublicAPI] [PublicAPI]
IWebSocketBuilder WithEcho(); IWebSocketBuilder WithEcho();
/// <summary>
/// Send a specific text message in response to any received message
/// </summary>
/// <param name="text">The text message to send</param>
[PublicAPI]
IWebSocketBuilder WithText(string text);
/// <summary>
/// Send specific binary data in response to any received message
/// </summary>
/// <param name="bytes">The binary data to send</param>
[PublicAPI]
IWebSocketBuilder WithBytes(byte[] bytes);
/// <summary>
/// Send a JSON object in response to any received message
/// </summary>
/// <param name="data">The object to serialize and send as JSON</param>
[PublicAPI]
IWebSocketBuilder WithJson(object data);
/// <summary> /// <summary>
/// Handle incoming WebSocket messages /// Handle incoming WebSocket messages
/// </summary> /// </summary>

View File

@@ -41,17 +41,17 @@ public interface IWebSocketContext
/// <summary> /// <summary>
/// Send text message to the client /// Send text message to the client
/// </summary> /// </summary>
Task SendTextAsync(string text, CancellationToken cancellationToken = default); Task SendAsync(string text, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Send binary message to the client /// Send binary message to the client
/// </summary> /// </summary>
Task SendBytesAsync(byte[] bytes, CancellationToken cancellationToken = default); Task SendAsync(byte[] bytes, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Send JSON message to the client /// Send JSON message to the client
/// </summary> /// </summary>
Task SendJsonAsync(object data, CancellationToken cancellationToken = default); Task SendAsJsonAsync(object data, CancellationToken cancellationToken = default);
/// <summary> /// <summary>
/// Close the WebSocket connection /// Close the WebSocket connection

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
// Copyright © WireMock.Net
using System.Net.WebSockets;
using System.Text;
namespace WireMock.Net.Tests.WebSockets;
internal static class ClientWebSocketExtensions
{
internal static Task SendAsync(this ClientWebSocket client, string text, bool endOfMessage = true, CancellationToken cancellationToken = default)
{
return client.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(text)), WebSocketMessageType.Text, endOfMessage, cancellationToken);
}
internal static async Task<string> ReceiveAsTextAsync(this ClientWebSocket client, CancellationToken cancellationToken = default)
{
var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), cancellationToken);
if (result.MessageType != WebSocketMessageType.Text)
{
throw new InvalidOperationException($"Expected a text message but received a {result.MessageType} message.");
}
if (!result.EndOfMessage)
{
throw new InvalidOperationException("Received message is too large for the buffer. Consider increasing the buffer size.");
}
return Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
}
}

View File

@@ -44,8 +44,47 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
client.State.Should().Be(WebSocketState.Open); client.State.Should().Be(WebSocketState.Open);
var testMessage = "Hello, WebSocket!"; var testMessage = "Hello, WebSocket!";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client.SendAsync(testMessage);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
// Assert
var received = await client.ReceiveAsTextAsync();
received.Should().Be(testMessage);
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
}
[Fact]
public async Task WithText_Should_Send_Configured_Text()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var responseMessage = "This is a predefined response";
server
.Given(Request.Create()
.WithPath("/ws/message")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithText(responseMessage)
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/message");
// Act
await client.ConnectAsync(uri, CancellationToken.None);
client.State.Should().Be(WebSocketState.Open);
var testMessage = "Any message from client";
await client.SendAsync(testMessage);
var receiveBuffer = new byte[1024]; var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -54,7 +93,247 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
result.MessageType.Should().Be(WebSocketMessageType.Text); result.MessageType.Should().Be(WebSocketMessageType.Text);
result.EndOfMessage.Should().BeTrue(); result.EndOfMessage.Should().BeTrue();
var received = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count); var received = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
received.Should().Be(testMessage); received.Should().Be(responseMessage);
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
}
[Fact]
public async Task WithText_Should_Send_Same_Text_For_Multiple_Messages()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var responseMessage = "Fixed response";
server
.Given(Request.Create()
.WithPath("/ws/message")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithText(responseMessage)
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/message");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" };
// Act & Assert
foreach (var testMessage in testMessages)
{
await client.SendAsync(testMessage);
var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
var received = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
received.Should().Be(responseMessage, $"should always return the fixed response regardless of input message '{testMessage}'");
}
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
}
[Fact]
public async Task WithBytes_Should_Send_Configured_Bytes()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var responseBytes = new byte[] { 0xDE, 0xAD, 0xBE, 0xEF };
server
.Given(Request.Create()
.WithPath("/ws/binary")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithBytes(responseBytes)
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/binary");
// Act
await client.ConnectAsync(uri, CancellationToken.None);
client.State.Should().Be(WebSocketState.Open);
var testMessage = "Any message from client";
await client.SendAsync(testMessage);
var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
// Assert
result.MessageType.Should().Be(WebSocketMessageType.Binary);
result.EndOfMessage.Should().BeTrue();
var receivedData = new byte[result.Count];
Array.Copy(receiveBuffer, receivedData, result.Count);
receivedData.Should().BeEquivalentTo(responseBytes);
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
}
[Fact]
public async Task WithBytes_Should_Send_Same_Bytes_For_Multiple_Messages()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var responseBytes = new byte[] { 0x01, 0x02, 0x03 };
server
.Given(Request.Create()
.WithPath("/ws/binary")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithBytes(responseBytes)
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/binary");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" };
// Act & Assert
foreach (var testMessage in testMessages)
{
await client.SendAsync(testMessage);
var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
result.MessageType.Should().Be(WebSocketMessageType.Binary);
var receivedData = new byte[result.Count];
Array.Copy(receiveBuffer, receivedData, result.Count);
receivedData.Should().BeEquivalentTo(responseBytes, $"should always return the fixed bytes regardless of input message '{testMessage}'");
}
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
}
[Fact]
public async Task WithJson_Should_Send_Configured_Json()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var responseData = new
{
status = "ok",
message = "This is a predefined JSON response",
timestamp = new DateTime(2024, 1, 1, 12, 0, 0, DateTimeKind.Utc)
};
server
.Given(Request.Create()
.WithPath("/ws/json")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithJson(responseData)
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/json");
// Act
await client.ConnectAsync(uri, CancellationToken.None);
client.State.Should().Be(WebSocketState.Open);
var testMessage = "Any message from client";
await client.SendAsync(testMessage);
var receiveBuffer = new byte[2048];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
// Assert
result.MessageType.Should().Be(WebSocketMessageType.Text);
result.EndOfMessage.Should().BeTrue();
var received = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
var json = JObject.Parse(received);
json["status"]!.ToString().Should().Be("ok");
json["message"]!.ToString().Should().Be("This is a predefined JSON response");
json["timestamp"].Should().NotBeNull();
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
}
[Fact]
public async Task WithJson_Should_Send_Same_Json_For_Multiple_Messages()
{
// Arrange
using var server = WireMockServer.Start(new WireMockServerSettings
{
Logger = new TestOutputHelperWireMockLogger(output),
Urls = ["ws://localhost:0"]
});
var responseData = new
{
id = 42,
name = "Fixed JSON Response"
};
server
.Given(Request.Create()
.WithPath("/ws/json")
.WithWebSocketUpgrade()
)
.RespondWith(Response.Create()
.WithWebSocket(ws => ws
.WithJson(responseData)
)
);
using var client = new ClientWebSocket();
var uri = new Uri($"{server.Url!}/ws/json");
await client.ConnectAsync(uri, CancellationToken.None);
var testMessages = new[] { "First", "Second", "Third" };
// Act & Assert
foreach (var testMessage in testMessages)
{
await client.SendAsync(testMessage);
var receiveBuffer = new byte[2048];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
var received = Encoding.UTF8.GetString(receiveBuffer, 0, result.Count);
var json = JObject.Parse(received);
json["id"]!.Value<int>().Should().Be(42);
json["name"]!.ToString().Should().Be("Fixed JSON Response", $"should always return the fixed JSON regardless of input message '{testMessage}'");
}
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None); await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
} }
@@ -87,8 +366,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act & Assert // Act & Assert
foreach (var testMessage in testMessages) foreach (var testMessage in testMessages)
{ {
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client.SendAsync(testMessage);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[1024]; var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -164,8 +442,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
// Act // Act
var sendBytes = Encoding.UTF8.GetBytes(string.Empty); await client.SendAsync(string.Empty);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[1024]; var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -201,7 +478,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
if (text.StartsWith("/help")) if (text.StartsWith("/help"))
{ {
await context.SendTextAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>"); await context.SendAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>");
} }
} }
}) })
@@ -213,8 +490,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
await client.ConnectAsync(uri, CancellationToken.None); await client.ConnectAsync(uri, CancellationToken.None);
// Act // Act
var sendBytes = Encoding.UTF8.GetBytes("/help"); await client.SendAsync("/help");
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[1024]; var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -254,25 +530,29 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
if (text.StartsWith("/help")) if (text.StartsWith("/help"))
{ {
await context.SendTextAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>"); await context.SendAsync("Available commands: /help, /time, /echo <text>, /upper <text>, /reverse <text>");
} }
else if (text.StartsWith("/time")) else if (text.StartsWith("/time"))
{ {
await context.SendTextAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC"); await context.SendAsync($"Server time: {DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC");
} }
else if (text.StartsWith("/echo ")) else if (text.StartsWith("/echo "))
{ {
await context.SendTextAsync(text.Substring(6)); await context.SendAsync(text.Substring(6));
} }
else if (text.StartsWith("/upper ")) else if (text.StartsWith("/upper "))
{ {
await context.SendTextAsync(text.Substring(7).ToUpper()); await context.SendAsync(text.Substring(7).ToUpper());
} }
else if (text.StartsWith("/reverse ")) else if (text.StartsWith("/reverse "))
{ {
var toReverse = text.Substring(9); var toReverse = text.Substring(9);
var reversed = new string(toReverse.Reverse().ToArray()); var reversed = new string(toReverse.Reverse().ToArray());
await context.SendTextAsync(reversed); await context.SendAsync(reversed);
}
else if (text.StartsWith("/close"))
{
await context.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing connection");
} }
} }
}) })
@@ -285,18 +565,17 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
var commands = new (string, Action<string>)[] var commands = new (string, Action<string>)[]
{ {
("/help", (string response) => response.Should().Contain("Available commands")), ("/help", response => response.Should().Contain("Available commands")),
("/time", (string response) => response.Should().Contain("Server time")), ("/time", response => response.Should().Contain("Server time")),
("/echo Test", (string response) => response.Should().Be("Test")), ("/echo Test", response => response.Should().Be("Test")),
("/upper test", (string response) => response.Should().Be("TEST")), ("/upper test", response => response.Should().Be("TEST")),
("/reverse hello", (string response) => response.Should().Be("olleh")) ("/reverse hello", response => response.Should().Be("olleh"))
}; };
// Act & Assert // Act & Assert
foreach (var (command, assertion) in commands) foreach (var (command, assertion) in commands)
{ {
var sendBytes = Encoding.UTF8.GetBytes(command); await client.SendAsync(command);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[1024]; var receiveBuffer = new byte[1024];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -305,6 +584,8 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
assertion(received); assertion(received);
} }
await client.SendAsync("/close");
await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None); await client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Test complete", CancellationToken.None);
} }
@@ -335,7 +616,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
length = msg.Text?.Length ?? 0, length = msg.Text?.Length ?? 0,
type = msg.MessageType.ToString() type = msg.MessageType.ToString()
}; };
await ctx.SendJsonAsync(response); await ctx.SendAsJsonAsync(response);
}) })
) )
); );
@@ -346,8 +627,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act // Act
var testMessage = "Test JSON message"; var testMessage = "Test JSON message";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client.SendAsync(testMessage);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[2048]; var receiveBuffer = new byte[2048];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -392,7 +672,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
type = msg.MessageType.ToString(), type = msg.MessageType.ToString(),
connectionId = ctx.ConnectionId.ToString() connectionId = ctx.ConnectionId.ToString()
}; };
await ctx.SendJsonAsync(response); await ctx.SendAsJsonAsync(response);
}) })
) )
); );
@@ -406,8 +686,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act & Assert // Act & Assert
foreach (var testMessage in testMessages) foreach (var testMessage in testMessages)
{ {
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client.SendAsync(testMessage);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[2048]; var receiveBuffer = new byte[2048];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -459,7 +738,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
new { id = 2, name = "Item2" } new { id = 2, name = "Item2" }
} }
}; };
await ctx.SendJsonAsync(response); await ctx.SendAsJsonAsync(response);
}) })
) )
); );
@@ -470,8 +749,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act // Act
var testMessage = "Complex test"; var testMessage = "Complex test";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client.SendAsync(testMessage);
await client.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer = new byte[2048]; var receiveBuffer = new byte[2048];
var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None); var result = await client.ReceiveAsync(new ArraySegment<byte>(receiveBuffer), CancellationToken.None);
@@ -540,8 +818,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act - Send message from client1 // Act - Send message from client1
var testMessage = "Hello everyone!"; var testMessage = "Hello everyone!";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client1.SendAsync(testMessage);
await client1.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
// Assert - All clients should receive the broadcast // Assert - All clients should receive the broadcast
var receiveBuffer1 = new byte[1024]; var receiveBuffer1 = new byte[1024];
@@ -616,8 +893,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act - Send message from client1 (client2 is now closed) // Act - Send message from client1 (client2 is now closed)
var testMessage = "Still here"; var testMessage = "Still here";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client1.SendAsync(testMessage);
await client1.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
// Assert - Only client1 should receive // Assert - Only client1 should receive
var receiveBuffer1 = new byte[1024]; var receiveBuffer1 = new byte[1024];
@@ -680,8 +956,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act - Send message from client1 // Act - Send message from client1
var testMessage = "JSON broadcast test"; var testMessage = "JSON broadcast test";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await client1.SendAsync(testMessage);
await client1.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
// Assert - Both clients should receive JSON // Assert - Both clients should receive JSON
var receiveBuffer1 = new byte[2048]; var receiveBuffer1 = new byte[2048];
@@ -757,8 +1032,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act & Assert // Act & Assert
foreach (var msg in messages) foreach (var msg in messages)
{ {
var sendBytes = Encoding.UTF8.GetBytes(msg); await client1.SendAsync(msg);
await client1.SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
var receiveBuffer1 = new byte[1024]; var receiveBuffer1 = new byte[1024];
var result1 = await client1.ReceiveAsync(new ArraySegment<byte>(receiveBuffer1), CancellationToken.None); var result1 = await client1.ReceiveAsync(new ArraySegment<byte>(receiveBuffer1), CancellationToken.None);
@@ -826,8 +1100,7 @@ public class WebSocketIntegrationTests(ITestOutputHelper output)
// Act - Send message from first client // Act - Send message from first client
var testMessage = "Mass broadcast"; var testMessage = "Mass broadcast";
var sendBytes = Encoding.UTF8.GetBytes(testMessage); await clients[0].SendAsync(testMessage);
await clients[0].SendAsync(new ArraySegment<byte>(sendBytes), WebSocketMessageType.Text, true, CancellationToken.None);
// Assert - All clients should receive // Assert - All clients should receive
var receiveTasks = clients.Select(async client => var receiveTasks = clients.Select(async client =>

File diff suppressed because it is too large Load Diff