using System.Net; using System.Net.Sockets; using MrGameEng.Net; using Xunit; namespace MrGameEng.Net.Tests; public class WebSocketLoopbackTests { [Fact] public async Task ClientAndServer_ExchangeBinaryMessages_OverLoopback() { var port = FreePort(); using var server = new WebSocketServer(port); server.Start(); using var client = await WebSocketClient.ConnectAsync( new Uri($"ws://localhost:{port}/"), new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token ); var connection = await WaitFor( () => server.TryAcceptConnection(out var c) ? c : null, "server accept" ); // Сервер → клиент. connection.Send([1, 2, 3, 250]); var received = await WaitFor( () => client.TryReceive(out var m) ? m : null, "client receive" ); Assert.Equal(new byte[] { 1, 2, 3, 250 }, received); // Клиент → сервер (ClientWebSocket маскирует кадры — сервер обязан размаскировать). client.Send([9, 8, 7]); var echoed = await WaitFor( () => connection.TryReceive(out var m) ? m : null, "server receive" ); Assert.Equal(new byte[] { 9, 8, 7 }, echoed); Assert.True(connection.IsOpen); Assert.True(client.IsOpen); } [Fact] public async Task ClosingTheClient_ClosesTheServerConnection() { var port = FreePort(); using var server = new WebSocketServer(port); server.Start(); var client = await WebSocketClient.ConnectAsync( new Uri($"ws://localhost:{port}/"), new CancellationTokenSource(TimeSpan.FromSeconds(10)).Token ); var connection = await WaitFor( () => server.TryAcceptConnection(out var c) ? c : null, "server accept" ); client.Close(); await WaitFor(() => connection.IsOpen ? null : "closed", "server-side close"); Assert.False(connection.IsOpen); } private static async Task WaitFor(Func poll, string what) where T : class { for (var i = 0; i < 200; i++) { if (poll() is { } result) { return result; } await Task.Delay(25); } throw new TimeoutException($"Timed out waiting for {what}."); } private static int FreePort() { var listener = new TcpListener(IPAddress.Loopback, 0); listener.Start(); var port = ((IPEndPoint)listener.LocalEndpoint).Port; listener.Stop(); return port; } }