Skip to content

Commit cb66874

Browse files
new RevealGameAsync method and unit tests
This commit introduces several new unit tests for the `RevealGameAsync` method in the `GamesClient` class. The tests cover scenarios such as successful game reveal, activity tracking during the reveal, and error handling for both patch and get requests. A mocked `HttpMessageHandler` is used to simulate API responses. Additionally, the `UpdateGameRequest` constructor is updated to use a named parameter for the `End` property, enhancing code clarity.
1 parent 4b81d18 commit cb66874

2 files changed

Lines changed: 217 additions & 1 deletion

File tree

src/clients/Codebreaker.GameAPIs.Client.Tests/GamesClientTests.cs

Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,114 @@ public async Task CancelGameAsync_Should_TrackActivity()
236236
Assert.True(stopActivityReceived);
237237
}
238238

239+
[Fact]
240+
public async Task RevealGameAsync_Should_CancelAndReturnGameInfo()
241+
{
242+
// Arrange
243+
(var httpClient, var handlerMock) = GetHttpClientForRevealGame();
244+
GamesClient gamesClient = new(httpClient, NullLogger<GamesClient>.Instance);
245+
var gameId = Guid.Parse("91f3c729-5e6e-459a-b656-2d77f3f45dd9");
246+
247+
// Act
248+
var game = await gamesClient.RevealGameAsync(gameId, "test", GameType.Game6x4, TestContext.Current.CancellationToken);
249+
250+
// Assert
251+
Assert.NotNull(game);
252+
Assert.Equal(gameId, game.Id);
253+
Assert.Equal("Game6x4", game.GameType);
254+
Assert.Equal("test", game.PlayerName);
255+
Assert.Equal(1, game.LastMoveNumber);
256+
Assert.Equal(4, game.NumberCodes);
257+
Assert.Equal(12, game.MaxMoves);
258+
Assert.False(game.IsVictory);
259+
Assert.NotNull(game.Codes);
260+
Assert.NotEmpty(game.Codes);
261+
Assert.NotNull(game.Moves);
262+
Assert.Single(game.Moves);
263+
264+
handlerMock.Protected().Verify(
265+
"SendAsync",
266+
Times.Exactly(2), // PATCH and GET
267+
ItExpr.IsAny<HttpRequestMessage>(),
268+
ItExpr.IsAny<CancellationToken>());
269+
}
270+
271+
[Fact]
272+
public async Task RevealGameAsync_Should_TrackActivity()
273+
{
274+
// Arrange
275+
(var httpClient, var handlerMock) = GetHttpClientForRevealGame();
276+
bool startActivityReceived = false;
277+
bool stopActivityReceived = false;
278+
bool revealedEventReceived = false;
279+
var gameId = Guid.Parse("91f3c729-5e6e-459a-b656-2d77f3f45dd9");
280+
281+
using ActivityListener listener = new()
282+
{
283+
ShouldListenTo = _ => true,
284+
ActivityStarted = activity =>
285+
{
286+
if (activity.OperationName == "RevealGameAsync")
287+
{
288+
startActivityReceived = true;
289+
Assert.Equal(ActivityKind.Client, activity.Kind);
290+
}
291+
},
292+
ActivityStopped = activity =>
293+
{
294+
if (activity.OperationName == "RevealGameAsync")
295+
{
296+
stopActivityReceived = true;
297+
ActivityEvent? revealedEvent = activity.Events.FirstOrDefault(e => e.Name == "GameRevealed");
298+
if (revealedEvent != null)
299+
{
300+
revealedEventReceived = true;
301+
var tag = revealedEvent.Value.Tags.FirstOrDefault(t => t.Key == "gameId");
302+
Assert.Equal(gameId.ToString(), tag.Value);
303+
}
304+
Assert.Equal(ActivityKind.Client, activity.Kind);
305+
}
306+
},
307+
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData
308+
};
309+
ActivitySource.AddActivityListener(listener);
310+
GamesClient gamesClient = new(httpClient, NullLogger<GamesClient>.Instance);
311+
312+
// Act
313+
await gamesClient.RevealGameAsync(gameId, "test", GameType.Game6x4, TestContext.Current.CancellationToken);
314+
315+
// Assert
316+
Assert.True(startActivityReceived);
317+
Assert.True(stopActivityReceived);
318+
Assert.True(revealedEventReceived);
319+
}
320+
321+
[Fact]
322+
public async Task RevealGameAsync_Should_Throw_OnPatchError()
323+
{
324+
// Arrange
325+
(var httpClient, var handlerMock) = GetHttpClientForRevealGame(patchFails: true);
326+
GamesClient gamesClient = new(httpClient, NullLogger<GamesClient>.Instance);
327+
var gameId = Guid.Parse("91f3c729-5e6e-459a-b656-2d77f3f45dd9");
328+
329+
// Act & Assert
330+
await Assert.ThrowsAsync<HttpRequestException>(() =>
331+
gamesClient.RevealGameAsync(gameId, "test", GameType.Game6x4, TestContext.Current.CancellationToken));
332+
}
333+
334+
[Fact]
335+
public async Task RevealGameAsync_Should_Throw_OnGetError()
336+
{
337+
// Arrange
338+
(var httpClient, var handlerMock) = GetHttpClientForRevealGame(getFails: true);
339+
GamesClient gamesClient = new(httpClient, NullLogger<GamesClient>.Instance);
340+
var gameId = Guid.Parse("91f3c729-5e6e-459a-b656-2d77f3f45dd9");
341+
342+
// Act & Assert
343+
await Assert.ThrowsAsync<HttpRequestException>(() =>
344+
gamesClient.RevealGameAsync(gameId, "test", GameType.Game6x4, TestContext.Current.CancellationToken));
345+
}
346+
239347
private static (HttpClient Client, Mock<HttpMessageHandler> Handler) GetHttpClientReturningACreatedGameSkeleton()
240348
{
241349
Mock<IConfiguration> configMock = new();
@@ -564,4 +672,112 @@ private static (HttpClient Client, Mock<HttpMessageHandler> Handler) GetHttpClie
564672
BaseAddress = new Uri(configMock.Object["GameAPIs"] ?? throw new InvalidOperationException())
565673
}, handlerMock);
566674
}
675+
676+
private static (HttpClient Client, Mock<HttpMessageHandler> Handler) GetHttpClientForRevealGame(bool patchFails = false, bool getFails = false)
677+
{
678+
Mock<IConfiguration> configMock = new();
679+
configMock.Setup(x => x[It.IsAny<string>()]).Returns("http://localhost:5000");
680+
Mock<HttpMessageHandler> handlerMock = new(MockBehavior.Strict);
681+
var gameId = "91f3c729-5e6e-459a-b656-2d77f3f45dd9";
682+
string getReturnMessage = """
683+
{
684+
"id": "91f3c729-5e6e-459a-b656-2d77f3f45dd9",
685+
"gameType": "Game6x4",
686+
"playerName": "test",
687+
"playerIsAuthenticated": false,
688+
"startTime": "2024-02-14T18:14:49.4420411Z",
689+
"endTime": "2024-02-14T18:20:00.0000000Z",
690+
"duration": "00:05:10.0000000",
691+
"lastMoveNumber": 1,
692+
"numberCodes": 4,
693+
"maxMoves": 12,
694+
"isVictory": false,
695+
"fieldValues": {
696+
"colors": [
697+
"Red",
698+
"Green",
699+
"Blue",
700+
"Yellow",
701+
"Purple",
702+
"Orange"
703+
]
704+
},
705+
"codes": [
706+
"Yellow",
707+
"Yellow",
708+
"Green",
709+
"Green"
710+
],
711+
"moves": [
712+
{
713+
"id": "963baba8-44b8-45e5-81f3-193959ae5bf6",
714+
"moveNumber": 1,
715+
"guessPegs": [
716+
"Red",
717+
"Green",
718+
"Blue",
719+
"Yellow"
720+
],
721+
"keyPegs": [
722+
"White",
723+
"White"
724+
]
725+
}
726+
]
727+
}
728+
""";
729+
if (!patchFails)
730+
{
731+
handlerMock.Protected()
732+
.Setup<Task<HttpResponseMessage>>(
733+
"SendAsync",
734+
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Patch),
735+
ItExpr.IsAny<CancellationToken>())
736+
.ReturnsAsync(new HttpResponseMessage
737+
{
738+
StatusCode = HttpStatusCode.OK
739+
}).Verifiable();
740+
}
741+
else
742+
{
743+
handlerMock.Protected()
744+
.Setup<Task<HttpResponseMessage>>(
745+
"SendAsync",
746+
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Patch),
747+
ItExpr.IsAny<CancellationToken>())
748+
.ReturnsAsync(new HttpResponseMessage
749+
{
750+
StatusCode = HttpStatusCode.BadRequest
751+
}).Verifiable();
752+
}
753+
if (!getFails)
754+
{
755+
handlerMock.Protected()
756+
.Setup<Task<HttpResponseMessage>>(
757+
"SendAsync",
758+
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get),
759+
ItExpr.IsAny<CancellationToken>())
760+
.ReturnsAsync(new HttpResponseMessage
761+
{
762+
StatusCode = HttpStatusCode.OK,
763+
Content = new StringContent(getReturnMessage, Encoding.UTF8, "application/json")
764+
}).Verifiable();
765+
}
766+
else
767+
{
768+
handlerMock.Protected()
769+
.Setup<Task<HttpResponseMessage>>(
770+
"SendAsync",
771+
ItExpr.Is<HttpRequestMessage>(req => req.Method == HttpMethod.Get),
772+
ItExpr.IsAny<CancellationToken>())
773+
.ReturnsAsync(new HttpResponseMessage
774+
{
775+
StatusCode = HttpStatusCode.NotFound
776+
}).Verifiable();
777+
}
778+
return (new(handlerMock.Object)
779+
{
780+
BaseAddress = new Uri(configMock.Object["GameAPIs"] ?? throw new InvalidOperationException())
781+
}, handlerMock);
782+
}
567783
}

src/clients/Codebreaker.GameAPIs.Client/GamesClient.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ public async Task<GameInfo> RevealGameAsync(Guid id, string playerName, GameType
8989
try
9090
{
9191
// First cancel/end the game
92-
var request = new UpdateGameRequest(id, gameType, playerName, 0, true);
92+
var request = new UpdateGameRequest(id, gameType, playerName, 0, End: true);
9393
var cancelResponse = await httpClient.PatchAsJsonAsync($"/games/{id}", request, s_jsonOptions, cancellationToken);
9494
cancelResponse.EnsureSuccessStatusCode();
9595

0 commit comments

Comments
 (0)