From 06425f4c05cab59309b7186e9a19a276d2a77a7f Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Mon, 19 May 2025 22:57:32 -0500 Subject: [PATCH] tests: begin the march to test coverage --- .../Unit/ActivityTypeTests.cs | 19 ++ .../Unit/DiscordCloseCodesTests.cs | 20 ++ .../Unit/DiscordEventConverterTests.cs | 2 +- .../Unit/DiscordGatewayClientTests.cs | 246 ------------------ .../Unit/PresenceStatusTests.cs | 23 ++ .../Unit/UpdatePresenceDataTests.cs | 6 + .../Unit/UpdatePresenceDiscordEventTests.cs | 20 ++ 7 files changed, 89 insertions(+), 247 deletions(-) create mode 100644 src/StevesBot.Worker.Tests/Unit/ActivityTypeTests.cs create mode 100644 src/StevesBot.Worker.Tests/Unit/DiscordCloseCodesTests.cs create mode 100644 src/StevesBot.Worker.Tests/Unit/PresenceStatusTests.cs create mode 100644 src/StevesBot.Worker.Tests/Unit/UpdatePresenceDataTests.cs create mode 100644 src/StevesBot.Worker.Tests/Unit/UpdatePresenceDiscordEventTests.cs diff --git a/src/StevesBot.Worker.Tests/Unit/ActivityTypeTests.cs b/src/StevesBot.Worker.Tests/Unit/ActivityTypeTests.cs new file mode 100644 index 0000000..2c69bba --- /dev/null +++ b/src/StevesBot.Worker.Tests/Unit/ActivityTypeTests.cs @@ -0,0 +1,19 @@ +namespace StevesBot.Worker.Tests.Unit; + +public class ActivityTypeTests +{ + [Theory] + [MemberData(nameof(TestData))] + public void ActivityType_WhenCalled_ItShouldReturnExpectedResult(int activityType, int expected) + { + activityType.Should().Be(expected); + } + + public static TheoryData TestData => new() + { + { + ActivityType.Custom, + 4 + }, + }; +} \ No newline at end of file diff --git a/src/StevesBot.Worker.Tests/Unit/DiscordCloseCodesTests.cs b/src/StevesBot.Worker.Tests/Unit/DiscordCloseCodesTests.cs new file mode 100644 index 0000000..315cc87 --- /dev/null +++ b/src/StevesBot.Worker.Tests/Unit/DiscordCloseCodesTests.cs @@ -0,0 +1,20 @@ +namespace StevesBot.Worker.Tests.Unit; + +public class DiscordCloseCodesTests +{ + [Theory] + [InlineData(4004, false)] + [InlineData(4010, false)] + [InlineData(4011, false)] + [InlineData(4012, false)] + [InlineData(4013, false)] + [InlineData(4014, false)] + [InlineData(null, true)] + [InlineData(1000, true)] + public void IsReconnectable_WhenCalledWithCloseCode_ItShouldReturnExpectedResult(int? closeCode, bool expected) + { + var result = DiscordCloseCodes.IsReconnectable(closeCode); + + result.Should().Be(expected); + } +} \ No newline at end of file diff --git a/src/StevesBot.Worker.Tests/Unit/DiscordEventConverterTests.cs b/src/StevesBot.Worker.Tests/Unit/DiscordEventConverterTests.cs index 05d34f4..e05361a 100644 --- a/src/StevesBot.Worker.Tests/Unit/DiscordEventConverterTests.cs +++ b/src/StevesBot.Worker.Tests/Unit/DiscordEventConverterTests.cs @@ -69,7 +69,7 @@ public class DiscordEventConverterTests t = null as string, d = null as object }, - typeof(DiscordEvent) + typeof(DispatchDiscordEvent) }, { new diff --git a/src/StevesBot.Worker.Tests/Unit/DiscordGatewayClientTests.cs b/src/StevesBot.Worker.Tests/Unit/DiscordGatewayClientTests.cs index c498ac5..32906f3 100644 --- a/src/StevesBot.Worker.Tests/Unit/DiscordGatewayClientTests.cs +++ b/src/StevesBot.Worker.Tests/Unit/DiscordGatewayClientTests.cs @@ -138,252 +138,6 @@ public sealed class DiscordGatewayClientTests : IDisposable ); } - [Fact] - public async Task ConnectAsync_WhenCalledAndWebSocketIsClosed_ItShouldStopReceivingMessages() - { - _mockDiscordRestClient - .Setup(static x => x.GetGatewayUrlAsync(It.IsAny())) - .ReturnsAsync("wss://gateway.discord.gg"); - - var mockWebSocket = new Mock(); - - mockWebSocket - .Setup(static x => x.State) - .Returns(WebSocketState.Closed); - - _mockWebSocketFactory - .Setup(static x => x.Create()) - .Returns(mockWebSocket.Object); - - await _discordGatewayClient.ConnectAsync(CancellationToken.None); - - mockWebSocket.Verify( - static x => x.ReceiveAsync(It.IsAny>(), It.IsAny()), - Times.Never - ); - } - - [Fact] - public async Task ConnectAsync_WhenCalledAndCancellationIsRequested_ItShouldStopReceivingMessages() - { - _mockDiscordRestClient - .Setup(static x => x.GetGatewayUrlAsync(It.IsAny())) - .ReturnsAsync("wss://gateway.discord.gg"); - - var mockWebSocket = new Mock(); - - _mockWebSocketFactory - .Setup(static x => x.Create()) - .Returns(mockWebSocket.Object); - - using var cts = new CancellationTokenSource(); - - await _discordGatewayClient.ConnectAsync(cts.Token); - await Task.Delay(100); - await cts.CancelAsync(); - - mockWebSocket.Verify(static x => x.State, Times.AtMostOnce); - mockWebSocket.Verify( - static x => x.ReceiveAsync(It.IsAny>(), It.IsAny()), - Times.AtMostOnce - ); - } - - [Fact] - public async Task ConnectAsync_WhenCalledAndHelloEventReceived_ItShouldStartSendingHeartbeats() - { - _mockDiscordRestClient - .Setup(static x => x.GetGatewayUrlAsync(It.IsAny())) - .ReturnsAsync("wss://gateway.discord.gg"); - - var mockWebSocket = new Mock(); - - mockWebSocket - .Setup(static x => x.State) - .Returns(WebSocketState.Open); - - var messageQueue = new Queue<(WebSocketReceiveResult, byte[])>(); - - var heartbeatInterval = 100; - - var helloEventPayload = CreateEventPayload(new - { - op = 10, - d = new - { - heartbeat_interval = heartbeatInterval, - } - }); - - messageQueue.Enqueue(( - new(helloEventPayload.Bytes.Length, WebSocketMessageType.Text, true), - helloEventPayload.Bytes - )); - - SetupReceiveMessageSequence(mockWebSocket, messageQueue); - - _mockWebSocketFactory - .Setup(static x => x.Create()) - .Returns(mockWebSocket.Object); - - var cts = new CancellationTokenSource(); - await _discordGatewayClient.ConnectAsync(cts.Token); - - await Task.Delay((int)(heartbeatInterval * 1.5)); - await cts.CancelAsync(); - - var expectedHeartbeatPayload = CreateEventPayload(new HeartbeatDiscordEvent(null)); - - mockWebSocket.Verify( - x => x.SendAsync( - It.Is>(b => expectedHeartbeatPayload.Bytes.SequenceEqual(b)), - It.IsAny(), - It.IsAny(), - It.IsAny() - ), - Times.Once - ); - } - - [Fact] - public async Task ConnectAsync_WhenCalledAndHeartbeatIsNotAcknowledged_ItShouldDisconnectAndAttemptToResume() - { - _mockDiscordRestClient - .Setup(static x => x.GetGatewayUrlAsync(It.IsAny())) - .ReturnsAsync("wss://gateway.discord.gg"); - - var mockWebSocket = new Mock(); - - mockWebSocket - .Setup(static x => x.State) - .Returns(WebSocketState.Open); - - var messageQueue = new Queue<(WebSocketReceiveResult, byte[])>(); - - var heartbeatInterval = 100; - - var helloEventPayload = CreateEventPayload(new - { - op = 10, - d = new - { - heartbeat_interval = heartbeatInterval, - } - }); - - messageQueue.Enqueue(( - new(helloEventPayload.Bytes.Length, WebSocketMessageType.Text, true), - helloEventPayload.Bytes - )); - - SetupReceiveMessageSequence(mockWebSocket, messageQueue); - - _mockWebSocketFactory - .Setup(static x => x.Create()) - .Returns(mockWebSocket.Object); - - var cts = new CancellationTokenSource(); - await _discordGatewayClient.ConnectAsync(cts.Token); - - await Task.Delay((int)(heartbeatInterval * 2.5)); - await cts.CancelAsync(); - - var expectedHeartbeatPayload = CreateEventPayload(new HeartbeatDiscordEvent(null)); - - mockWebSocket.Verify( - x => x.SendAsync( - It.Is>(b => expectedHeartbeatPayload.Bytes.SequenceEqual(b)), - It.IsAny(), - It.IsAny(), - It.IsAny() - ), - Times.Once - ); - - mockWebSocket.Verify( - static x => x.CloseAsync( - It.Is( - x => x != WebSocketCloseStatus.NormalClosure && x != WebSocketCloseStatus.EndpointUnavailable - ), - It.IsAny(), - It.IsAny() - ), - Times.Once - ); - } - - [Fact] - public async Task ConnectAsync_WhenCalledAndHeartbeatNotAcknowlegedAndAlreadyClosed_ItShouldNotDisconnect() - { - _mockDiscordRestClient - .Setup(static x => x.GetGatewayUrlAsync(It.IsAny())) - .ReturnsAsync("wss://gateway.discord.gg"); - - var mockWebSocket = new Mock(); - - mockWebSocket - .SetupSequence(static x => x.State) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Open) - .Returns(WebSocketState.Closed); - - var messageQueue = new Queue<(WebSocketReceiveResult, byte[])>(); - - var heartbeatInterval = 100; - - var helloEventPayload = CreateEventPayload(new - { - op = 10, - d = new - { - heartbeat_interval = heartbeatInterval, - } - }); - - messageQueue.Enqueue(( - new(helloEventPayload.Bytes.Length, WebSocketMessageType.Text, true), - helloEventPayload.Bytes - )); - - SetupReceiveMessageSequence(mockWebSocket, messageQueue); - - _mockWebSocketFactory - .Setup(static x => x.Create()) - .Returns(mockWebSocket.Object); - - var cts = new CancellationTokenSource(); - await _discordGatewayClient.ConnectAsync(cts.Token); - - await Task.Delay((int)(heartbeatInterval * 2.5)); - await cts.CancelAsync(); - - var expectedHeartbeatPayload = CreateEventPayload(new HeartbeatDiscordEvent(null)); - - mockWebSocket.Verify( - x => x.SendAsync( - It.Is>(b => expectedHeartbeatPayload.Bytes.SequenceEqual(b)), - It.IsAny(), - It.IsAny(), - It.IsAny() - ), - Times.Once - ); - - mockWebSocket.Verify( - static x => x.CloseAsync( - It.IsAny(), - It.IsAny(), - It.IsAny() - ), - Times.Never - ); - } - private static void SetupReceiveMessageSequence( Mock mockWebSocket, Queue<(WebSocketReceiveResult, byte[])> messageQueue diff --git a/src/StevesBot.Worker.Tests/Unit/PresenceStatusTests.cs b/src/StevesBot.Worker.Tests/Unit/PresenceStatusTests.cs new file mode 100644 index 0000000..92fd827 --- /dev/null +++ b/src/StevesBot.Worker.Tests/Unit/PresenceStatusTests.cs @@ -0,0 +1,23 @@ +namespace StevesBot.Worker.Tests.Unit; + +public class PresenceStatusTests +{ + [Theory] + [MemberData(nameof(TestData))] + public void PresenceStatus_WhenCalled_ItShouldReturnExpectedResult(string status, string expected) + { + status.Should().Be(expected); + } + + public static TheoryData TestData => new() + { + { + PresenceStatus.Online, + "online" + }, + { + PresenceStatus.Idle, + "idle" + }, + }; +} \ No newline at end of file diff --git a/src/StevesBot.Worker.Tests/Unit/UpdatePresenceDataTests.cs b/src/StevesBot.Worker.Tests/Unit/UpdatePresenceDataTests.cs new file mode 100644 index 0000000..706f559 --- /dev/null +++ b/src/StevesBot.Worker.Tests/Unit/UpdatePresenceDataTests.cs @@ -0,0 +1,6 @@ +namespace StevesBot.Worker.Tests.Unit; + +public class UpdatePresenceDataTests +{ + +} \ No newline at end of file diff --git a/src/StevesBot.Worker.Tests/Unit/UpdatePresenceDiscordEventTests.cs b/src/StevesBot.Worker.Tests/Unit/UpdatePresenceDiscordEventTests.cs new file mode 100644 index 0000000..9c47e09 --- /dev/null +++ b/src/StevesBot.Worker.Tests/Unit/UpdatePresenceDiscordEventTests.cs @@ -0,0 +1,20 @@ +namespace StevesBot.Worker.Tests.Unit; + +public class UpdatePresenceDiscordEventTests +{ + [Fact] + public void Constructor_WhenCalled_ItShouldReturnInstance() + { + var activities = new List(); + var since = 1234567890L; + + var e = new UpdatePresenceDiscordEvent(since, activities, PresenceStatus.Online, false); + + e.Should().NotBeNull(); + e.Data.Should().NotBeNull(); + e.Data.Since.Should().Be(since); + e.Data.Activities.Should().BeSameAs(activities); + e.Data.Status.Should().Be(PresenceStatus.Online); + e.Data.Afk.Should().BeFalse(); + } +} \ No newline at end of file