Skip to content

Commit 38f14e1

Browse files
authored
Merge branch 'main' into setup-database
2 parents 488fca9 + 5c5899c commit 38f14e1

28 files changed

+841
-30
lines changed

.github/workflows/format.yml

+7-10
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,11 @@ jobs:
2121
- name: Run dotnet format
2222
run: dotnet format
2323
- name: Commit and push changes
24-
run: >
24+
run: |
2525
git config --global user.name 'github-actions[bot]'
26-
27-
git config --global user.email
28-
'github-actions[bot]@users.noreply.github.com'
29-
30-
git add .
31-
32-
git commit -m "Apply dotnet format"
33-
34-
git push
26+
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
27+
if ! git diff-index --quiet HEAD; then
28+
git add .
29+
git commit -m "Apply dotnet format"
30+
git push
31+
fi

Epsilon.Tests/Controllers/WebSocketControllerTest.cs

+90-6
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
using System.Net.WebSockets;
2+
using System.Reactive.Subjects;
3+
using System.Text;
24
using AutoFixture;
35
using Epsilon.Controllers;
6+
using Epsilon.Handler.WebsocketMessageHandler;
7+
using Epsilon.Models;
8+
using Epsilon.Services.WebsocketStateService;
49
using Microsoft.AspNetCore.Http;
510
using Microsoft.AspNetCore.Mvc;
611
using Moq;
12+
using Newtonsoft.Json;
713
using Xunit;
814

915
namespace Epsilon.Tests.Controllers;
1016

1117
public class WebSocketControllerTest
1218
{
19+
private readonly Mock<IMessageHandler<string>> _mockWebsocketMessageHandler = new();
20+
private readonly Mock<IWebsocketStateService> _websocketStateService = new();
1321
private readonly Mock<HttpContext> _mockHttpContext = new();
1422
private readonly Mock<WebSocketManager> _mockWebSocketManager = new();
1523
private readonly Mock<WebSocket> _mockWebSocket = new();
@@ -26,7 +34,11 @@ public WebSocketControllerTest()
2634
.Setup(context => context.WebSockets)
2735
.Returns(_mockWebSocketManager.Object);
2836

29-
_webSocketController = new WebSocketController
37+
_websocketStateService
38+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
39+
.Returns(_fixture.Create<WebsocketState>());
40+
41+
_webSocketController = new WebSocketController(_websocketStateService.Object, _mockWebsocketMessageHandler.Object)
3042
{
3143
ControllerContext = new ControllerContext
3244
{
@@ -50,21 +62,24 @@ public async Task WebSocket_ShouldCloseConnection_WhenCloseMessageSent()
5062

5163
await _webSocketController.WebSocket();
5264

65+
_websocketStateService.Verify(service => service.CreateWebsocket(It.IsAny<string>()));
66+
_websocketStateService.Verify(service => service.DeleteWebsocket(It.IsAny<string>()));
67+
5368
_mockWebSocket.Verify(socket =>
5469
socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed", It.IsAny<CancellationToken>())
5570
);
5671
}
5772

5873
[Fact]
59-
public async Task WebSocket_ShouldEchoMessage_WhenWeSendAMessage()
74+
public async Task WebSocket_ShouldHandleMessage_WhenWeSendAMessage()
6075
{
61-
var isFirst = true;
76+
var runs = 0;
6277
var message = _fixture.CreateMany<byte>(1024).ToArray();
6378
_mockWebSocket
6479
.Setup(socket => socket.ReceiveAsync(It.IsAny<ArraySegment<byte>>(), It.IsAny<CancellationToken>()))
6580
.ReturnsAsync((ArraySegment<byte> buffer, CancellationToken _) =>
6681
{
67-
if (!isFirst)
82+
if (runs > 5)
6883
{
6984
return new WebSocketReceiveResult(
7085
0,
@@ -75,7 +90,8 @@ public async Task WebSocket_ShouldEchoMessage_WhenWeSendAMessage()
7590
);
7691
}
7792

78-
isFirst = false;
93+
runs++;
94+
7995
var count = Math.Min(buffer.Count, message.Length);
8096
Array.Copy(message, buffer.Array!, count);
8197
return new WebSocketReceiveResult(
@@ -87,13 +103,81 @@ public async Task WebSocket_ShouldEchoMessage_WhenWeSendAMessage()
87103

88104
await _webSocketController.WebSocket();
89105

106+
_mockWebsocketMessageHandler.Verify(handler =>
107+
handler.HandleMessage(Encoding.UTF8.GetString(message), It.IsAny<string>()), Times.Exactly(6)
108+
);
109+
110+
_websocketStateService.Verify(service => service.CreateWebsocket(It.IsAny<string>()));
111+
_websocketStateService.Verify(service => service.DeleteWebsocket(It.IsAny<string>()));
90112

91113
_mockWebSocket.Verify(socket =>
92-
socket.SendAsync(new ArraySegment<byte>(message, 0, message.Length), WebSocketMessageType.Text, false, It.IsAny<CancellationToken>())
114+
socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed", It.IsAny<CancellationToken>())
115+
);
116+
}
117+
118+
[Fact]
119+
public async Task WebSocket_ShouldSendMessage_WhenWePushOneToObservable()
120+
{
121+
var replay = new ReplaySubject<object>();
122+
_websocketStateService
123+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
124+
.Returns(new WebsocketState("", false, replay));
125+
126+
var message = _fixture.Create<WebsocketMessage<LoginRequest>>();
127+
replay.OnNext(message);
128+
129+
_mockWebSocket
130+
.Setup(socket => socket.ReceiveAsync(It.IsAny<ArraySegment<byte>>(), It.IsAny<CancellationToken>()))
131+
.ReturnsAsync(new WebSocketReceiveResult(
132+
0,
133+
WebSocketMessageType.Close,
134+
true,
135+
WebSocketCloseStatus.NormalClosure,
136+
"Closed"
137+
));
138+
139+
await _webSocketController.WebSocket();
140+
141+
var messageBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
142+
143+
_mockWebSocket.Verify(socket =>
144+
socket.SendAsync(new ArraySegment<byte>(messageBytes, 0, messageBytes.Length), WebSocketMessageType.Text, true, It.IsAny<CancellationToken>())
93145
);
94146

95147
_mockWebSocket.Verify(socket =>
96148
socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed", It.IsAny<CancellationToken>())
97149
);
98150
}
151+
152+
[Fact]
153+
public async Task WebSocket_ShouldNotSendMessage_WhenWeCloseTheWebsocket()
154+
{
155+
var replay = new ReplaySubject<object>();
156+
_websocketStateService
157+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
158+
.Returns(new WebsocketState("", false, replay));
159+
160+
_mockWebSocket
161+
.Setup(socket => socket.ReceiveAsync(It.IsAny<ArraySegment<byte>>(), It.IsAny<CancellationToken>()))
162+
.ReturnsAsync(new WebSocketReceiveResult(
163+
0,
164+
WebSocketMessageType.Close,
165+
true,
166+
WebSocketCloseStatus.NormalClosure,
167+
"Closed"
168+
));
169+
170+
await _webSocketController.WebSocket();
171+
172+
var message = _fixture.Create<WebsocketMessage<LoginRequest>>();
173+
replay.OnNext(message);
174+
175+
_mockWebSocket.Verify(socket =>
176+
socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed", It.IsAny<CancellationToken>())
177+
);
178+
179+
_mockWebSocket.Verify(socket =>
180+
socket.SendAsync(It.IsAny<ArraySegment<byte>>(), WebSocketMessageType.Text, true, It.IsAny<CancellationToken>()), Times.Never
181+
);
182+
}
99183
}

Epsilon.Tests/Epsilon.Tests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1919
</PackageReference>
2020
<PackageReference Include="FluentAssertions" Version="7.0.0-alpha.4"/>
21+
<PackageReference Include="FluentAssertions.Reactive" Version="0.2.0" />
2122
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
2223
<PackageReference Include="Moq" Version="4.20.70" />
2324
<PackageReference Include="xunit" Version="2.8.1"/>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using AutoFixture;
2+
using Epsilon.Handler.WebsocketMessageHandler;
3+
using Epsilon.Models;
4+
using Epsilon.Services.WebsocketStateService;
5+
using FluentAssertions;
6+
using Moq;
7+
using Newtonsoft.Json;
8+
using Xunit;
9+
10+
namespace Epsilon.Tests.Handler;
11+
12+
public class LoginRequestMessageHandlerTest
13+
{
14+
private readonly Mock<IWebsocketStateService> _websocketStateService = new();
15+
private readonly LoginRequestMessageHandler _loginRequestMessageHandler;
16+
private readonly Fixture _fixture = new();
17+
18+
public LoginRequestMessageHandlerTest()
19+
{
20+
_websocketStateService
21+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
22+
.Returns(_fixture.Create<WebsocketState>());
23+
24+
_loginRequestMessageHandler = new LoginRequestMessageHandler(
25+
_websocketStateService.Object
26+
);
27+
}
28+
29+
[Fact]
30+
public void LoginRequestMessageHandler_ShouldDoNothing_WhenLoginRequestIsNull()
31+
{
32+
var sessionId = Guid.NewGuid().ToString();
33+
34+
var action = () => _loginRequestMessageHandler.HandleMessage(null, sessionId);
35+
36+
action.Should().NotThrow();
37+
}
38+
39+
[Fact]
40+
public void LoginRequestMessageHandler_ShouldSetUsername_WhenWeSendALoginRequest()
41+
{
42+
var loginRequest = _fixture.Create<LoginRequest>();
43+
var sessionId = Guid.NewGuid().ToString();
44+
45+
_loginRequestMessageHandler.HandleMessage(loginRequest, sessionId);
46+
47+
_websocketStateService.Verify(service =>
48+
service.SetWebsocketState(sessionId, It.Is<WebsocketState>(state =>
49+
state.Username == loginRequest.Username && state.IsLoggedIn
50+
))
51+
);
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
using System.Reactive.Linq;
2+
using AutoFixture;
3+
using Epsilon.Handler.WebsocketMessageHandler;
4+
using Epsilon.Models;
5+
using Epsilon.Services.WebsocketStateService;
6+
using FluentAssertions;
7+
using FluentAssertions.Reactive;
8+
using Moq;
9+
using Xunit;
10+
11+
namespace Epsilon.Tests.Handler;
12+
13+
public class MessageRequestMessageHandlerTest
14+
{
15+
private readonly Mock<IWebsocketStateService> _websocketStateService = new();
16+
private readonly MessageRequestMessageHandler _messageRequestMessageHandler;
17+
private readonly List<WebsocketState> _websocketStates;
18+
private readonly Fixture _fixture = new();
19+
20+
public MessageRequestMessageHandlerTest()
21+
{
22+
_websocketStates = _fixture.CreateMany<WebsocketState>().ToList();
23+
24+
_websocketStateService
25+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
26+
.Returns(_fixture.Create<WebsocketState>() with
27+
{
28+
IsLoggedIn = true
29+
});
30+
31+
_websocketStateService
32+
.Setup(service => service.GetAllActiveWebsockets())
33+
.Returns(_websocketStates);
34+
35+
_messageRequestMessageHandler = new MessageRequestMessageHandler(
36+
_websocketStateService.Object
37+
);
38+
}
39+
40+
[Fact]
41+
public void MessageRequestMessageHandler_ShouldDoNothing_WhenMessageRequestIsNull()
42+
{
43+
var sessionId = Guid.NewGuid().ToString();
44+
45+
var action = () => _messageRequestMessageHandler.HandleMessage(null, sessionId);
46+
47+
action.Should().NotThrow();
48+
}
49+
50+
[Fact]
51+
public void MessageRequestMessageHandler_ShouldDoNothing_WhenWeAreNotLoggedIn()
52+
{
53+
const string recipient = "Recipient";
54+
var messageRequest = new MessageRequest("Hello :)", recipient);
55+
var sessionId = Guid.NewGuid().ToString();
56+
_websocketStates.Add(_fixture.Create<WebsocketState>() with
57+
{
58+
Username = recipient,
59+
IsLoggedIn = true
60+
});
61+
62+
_websocketStateService
63+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
64+
.Returns(_fixture.Create<WebsocketState>() with
65+
{
66+
IsLoggedIn = false,
67+
Username = "MyUsername"
68+
});
69+
70+
_messageRequestMessageHandler.HandleMessage(messageRequest, sessionId);
71+
72+
foreach (var websocketState in _websocketStates)
73+
{
74+
websocketState.OutgoingMessages.AsObservable().Observe().Should().NotPush();
75+
}
76+
}
77+
78+
[Fact]
79+
public void MessageRequestMessageHandler_ShouldSendMessage_WhenWeAreLoggedIn()
80+
{
81+
const string recipient = "Recipient";
82+
var messageRequest = new MessageRequest("Hello :)", recipient);
83+
var sessionId = Guid.NewGuid().ToString();
84+
_websocketStates.Add(_fixture.Create<WebsocketState>() with
85+
{
86+
Username = recipient,
87+
IsLoggedIn = true
88+
});
89+
90+
_websocketStateService
91+
.Setup(service => service.GetWebsocketState(It.IsAny<string>()))
92+
.Returns(_fixture.Create<WebsocketState>() with
93+
{
94+
IsLoggedIn = true,
95+
Username = "MyUsername"
96+
});
97+
98+
_messageRequestMessageHandler.HandleMessage(messageRequest, sessionId);
99+
100+
foreach (var websocketState in _websocketStates)
101+
{
102+
if (websocketState.Username == recipient)
103+
{
104+
websocketState.OutgoingMessages.AsObservable().Observe().Should()
105+
.PushMatch(s => s.Equals(new WebsocketMessage<MessageResponse>(
106+
MessageType.MessageResponse,
107+
new MessageResponse("Hello :)", "MyUsername"))
108+
));
109+
}
110+
else
111+
{
112+
websocketState.OutgoingMessages.AsObservable().Observe().Should().NotPush();
113+
}
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)