From 00d777d985d9994c40d2c59992a7fff288960f6d Mon Sep 17 00:00:00 2001 From: Martin Berg Alstad <600878@stud.hvl.no> Date: Thu, 13 Jul 2023 13:51:02 +0200 Subject: [PATCH] WebSocketService tests --- .../Services/WebSocketServiceTests.cs | 166 +++++++++++++++++- .../Interfaces/IWebSocketService.cs | 3 +- pac-man-board-game/Services/GameGroup.cs | 1 + .../Services/WebSocketService.cs | 11 +- 4 files changed, 174 insertions(+), 7 deletions(-) diff --git a/BackendTests/Services/WebSocketServiceTests.cs b/BackendTests/Services/WebSocketServiceTests.cs index d299bef..3066a38 100644 --- a/BackendTests/Services/WebSocketServiceTests.cs +++ b/BackendTests/Services/WebSocketServiceTests.cs @@ -1,6 +1,170 @@ +using System.Net.WebSockets; +using BackendTests.TestUtils; +using Microsoft.Extensions.Logging; +using NSubstitute; +using pacMan.Game; +using pacMan.Interfaces; +using pacMan.Services; +using pacMan.Utils; + namespace BackendTests.Services; public class WebSocketServiceTests // TODO: Implement { - + private IWebSocketService _service = null!; + + [SetUp] + public void SetUp() + { + _service = new WebSocketService(Substitute.For>()); + } + + #region Send(Websocket, ArraySegment) + + [Test] + public void Send_OpenWebsocket() + { + var segment = "test".ToArraySegment(); + using var webSocket = Substitute.For(); + + _service.Send(webSocket, segment); + + webSocket.ReceivedWithAnyArgs().SendAsync(default, default, default, default); + } + + [Test] + public void Send_ClosedWebsocket() + { + var segment = "test".ToArraySegment(); + using var webSocket = Substitute.For(); + webSocket.State.Returns(WebSocketState.Closed); + + Assert.That(webSocket.State, Is.Not.EqualTo(WebSocketState.Open).Or.EqualTo(WebSocketState.CloseReceived)); + + webSocket + .WhenForAnyArgs(x => x.SendAsync(default, default, default, default)) + .Do(_ => throw new WebSocketException()); + + Assert.ThrowsAsync(async () => await _service.Send(webSocket, segment)); + + webSocket.Received().SendAsync(segment, WebSocketMessageType.Text, true, CancellationToken.None); + } + + #endregion + + #region Receive(Websocket, byte[]) + + [Test] + public void Receive_ExactBuffer() + { + const int bufferSize = 16; + var buffer = new byte[bufferSize]; + var segment = "0123456789ABCDEF".ToArraySegment(); + var webSocket = Substitute.For(); + + webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None) + .Returns(new WebSocketReceiveResult(bufferSize, WebSocketMessageType.Text, true)) + .AndDoes(_ => buffer = segment.ToArray()); + + var result = _service.Receive(webSocket, buffer).Result; + + Assert.Multiple(() => + { + Assert.That(result, Has.Count.EqualTo(bufferSize)); + Assert.That(buffer.GetString(buffer.Length), Is.EqualTo(segment.ToArray().GetString(buffer.Length))); + }); + } + + [Test] + public void Receive_CloseWebsocket() + { + using var webSocket = Substitute.For(); + webSocket.State.Returns(WebSocketState.Closed); + + Assert.That(webSocket.State, Is.Not.EqualTo(WebSocketState.Open).Or.EqualTo(WebSocketState.CloseReceived)); + + webSocket + .WhenForAnyArgs(x => x.ReceiveAsync(default, default)) + .Do(_ => throw new WebSocketException()); + + Assert.ThrowsAsync(async () => await _service.Receive(webSocket, new byte[] { })); + + webSocket.ReceivedWithAnyArgs().ReceiveAsync(default, CancellationToken.None); + } + + #endregion + + #region Close(Websocket, WebSocketCloseStatus, string?) + + [Test] + public void Close_OpenWebsocket() + { + using var webSocket = Substitute.For(); + webSocket.State.Returns(WebSocketState.Open); + + Assert.That(webSocket.State, Is.EqualTo(WebSocketState.Open).Or.EqualTo(WebSocketState.CloseReceived)); + + _service.Close(webSocket, WebSocketCloseStatus.NormalClosure, null).Wait(); + + webSocket.ReceivedWithAnyArgs().CloseAsync(default, default, CancellationToken.None); + } + + [Test] + public void Close_ClosedWebsocket() + { + using var webSocket = Substitute.For(); + webSocket.State.Returns(WebSocketState.Closed); + + Assert.That(webSocket.State, Is.Not.EqualTo(WebSocketState.Open).Or.EqualTo(WebSocketState.CloseReceived)); + + webSocket + .WhenForAnyArgs(x => x.CloseAsync(default, default, default)) + .Do(_ => throw new WebSocketException()); + + Assert.ThrowsAsync(async () => + await _service.Close(webSocket, WebSocketCloseStatus.NormalClosure, null)); + + webSocket.ReceivedWithAnyArgs().CloseAsync(default, default, CancellationToken.None); + } + + #endregion + + #region AddPlayer(IPlayer) + + [Test] + public void AddPlayer_ToEmptyGroup() + { + var player = Players.Create("white"); + var group = _service.AddPlayer(player); + + Assert.Multiple(() => + { + Assert.That(group.Players, Has.Count.EqualTo(1)); + Assert.That(group.RandomPlayer, Is.EqualTo(player)); + Assert.That(_service.Games, Has.Count.EqualTo(1)); + }); + } + + [Test] + public void AddPlayer_ToFullGroup() + { + for (int i = 0; i < 4; i++) + { + var player = Players.Create(i.ToString()); + _service.AddPlayer(player); + } + var player5 = Players.Create("white"); + + var group = _service.AddPlayer(player5); + + Assert.Multiple(() => + { + Assert.That(group.Players, Has.Count.EqualTo(1)); + Assert.That(group.RandomPlayer, Is.EqualTo(player5)); + Assert.That(_service.Games, Has.Count.EqualTo(2)); + Assert.That(_service.Games.First(), Has.Count.EqualTo(Rules.MaxPlayers)); + }); + } + + #endregion } diff --git a/pac-man-board-game/Interfaces/IWebSocketService.cs b/pac-man-board-game/Interfaces/IWebSocketService.cs index 65c8264..edd26fe 100644 --- a/pac-man-board-game/Interfaces/IWebSocketService.cs +++ b/pac-man-board-game/Interfaces/IWebSocketService.cs @@ -6,11 +6,12 @@ namespace pacMan.Interfaces; public interface IWebSocketService { + SynchronizedCollection Games { get; } + int CountConnected { get; } event Func, Task>? Connections; Task Send(WebSocket webSocket, ArraySegment segment); void SendToAll(ArraySegment segment); Task Receive(WebSocket webSocket, byte[] buffer); Task Close(WebSocket webSocket, WebSocketCloseStatus closeStatus, string? closeStatusDescription); - int CountConnected(); GameGroup AddPlayer(IPlayer player); } diff --git a/pac-man-board-game/Services/GameGroup.cs b/pac-man-board-game/Services/GameGroup.cs index 21d8a4c..337975e 100644 --- a/pac-man-board-game/Services/GameGroup.cs +++ b/pac-man-board-game/Services/GameGroup.cs @@ -11,6 +11,7 @@ public class GameGroup : IEnumerable public List Players { get; } = new(); public IPlayer RandomPlayer => Players[_random.Next(Players.Count)]; + public int Count => Players.Count; public IEnumerator GetEnumerator() => Players.GetEnumerator(); diff --git a/pac-man-board-game/Services/WebSocketService.cs b/pac-man-board-game/Services/WebSocketService.cs index 919668f..7b13d7e 100644 --- a/pac-man-board-game/Services/WebSocketService.cs +++ b/pac-man-board-game/Services/WebSocketService.cs @@ -5,7 +5,7 @@ using pacMan.Utils; namespace pacMan.Services; -public class WebSocketService : IWebSocketService // TODO add tests +public class WebSocketService : IWebSocketService { private readonly ILogger _logger; @@ -17,7 +17,9 @@ public class WebSocketService : IWebSocketService // TODO add tests public SynchronizedCollection Games { get; } = new(); - public event Func, Task>? Connections; + public int CountConnected => Connections?.GetInvocationList().Length ?? 0; + + public event Func, Task>? Connections; // TODO remove and use GameGroup public async Task Send(WebSocket webSocket, ArraySegment segment) { @@ -27,7 +29,7 @@ public class WebSocketService : IWebSocketService // TODO add tests true, CancellationToken.None); - _logger.Log(LogLevel.Trace, "Message sent to WebSocket"); + _logger.Log(LogLevel.Debug, "Message sent to WebSocket"); } public void SendToAll(ArraySegment segment) @@ -50,11 +52,10 @@ public class WebSocketService : IWebSocketService // TODO add tests closeStatus, closeStatusDescription, CancellationToken.None); + _logger.Log(LogLevel.Information, "WebSocket connection closed"); } - public int CountConnected() => Connections?.GetInvocationList().Length ?? 0; - public GameGroup AddPlayer(IPlayer player) { var index = 0;