From ac060b6bd5167e9d025031e95312e0fe9a4a690a Mon Sep 17 00:00:00 2001 From: zysim <9867871+zysim@users.noreply.github.com> Date: Sat, 19 Oct 2024 23:52:35 +0800 Subject: [PATCH] Add conflict case --- LeaderboardBackend.Test/Leaderboards.cs | 39 ++++++++----------- .../Controllers/LeaderboardsController.cs | 10 +++-- LeaderboardBackend/Results.cs | 3 ++ .../Services/ILeaderboardService.cs | 2 +- .../Services/Impl/LeaderboardService.cs | 10 ++++- LeaderboardBackend/openapi.json | 21 +++++++++- 6 files changed, 53 insertions(+), 32 deletions(-) diff --git a/LeaderboardBackend.Test/Leaderboards.cs b/LeaderboardBackend.Test/Leaderboards.cs index ab5bf3cf..ec4b4ba9 100644 --- a/LeaderboardBackend.Test/Leaderboards.cs +++ b/LeaderboardBackend.Test/Leaderboards.cs @@ -1,9 +1,6 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Net; -using System.Net.Http; -using System.Threading; using System.Threading.Tasks; using LeaderboardBackend.Models.Entities; using LeaderboardBackend.Models.Requests; @@ -15,7 +12,6 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; -using Microsoft.VisualBasic; using NodaTime; using NodaTime.Testing; using NUnit.Framework; @@ -359,13 +355,10 @@ public async Task RestoreLeaderboard_OK() Leaderboard deletedBoard = new() { Name = "Super Mario World", - Slug = "super-mario-world-deleted", + Slug = "super-mario-world-to-restore", DeletedAt = _clock.GetCurrentInstant() }; - Instant now = Instant.FromUnixTimeSeconds(1); - _clock.Reset(now); - context.Leaderboards.Add(deletedBoard); await context.SaveChangesAsync(); deletedBoard.Id.Should().NotBe(default); @@ -394,21 +387,22 @@ public async Task RestoreLeaderboard_Unauthenticated() await act.Should().ThrowAsync().Where(e => e.Response.StatusCode == HttpStatusCode.Unauthorized); } - [Test] - public async Task RestoreLeaderboard_Unauthorized() + [TestCase("restore-leaderboard-unauth1@example.com", "RestoreBoard1", UserRole.Confirmed)] + [TestCase("restore-leaderboard-unauth2@example.com", "RestoreBoard2", UserRole.Registered)] + public async Task RestoreLeaderboard_Unauthorized(string email, string username, UserRole role) { - IUserService userService = _factory.Services.CreateScope().ServiceProvider.GetRequiredService(); + UserViewModel userModel = await _apiClient.RegisterUser(username, email, "P4ssword"); - RegisterRequest registerRequest = new() - { - Email = "user@example.com", - Password = "Passw0rd", - Username = "unauthorized" - }; + ApplicationContext context = _factory.Services.CreateScope().ServiceProvider.GetRequiredService(); - await userService.CreateUser(registerRequest); + User? user = await context.Users.FindAsync([userModel.Id]); + + context.Users.Update(user!); + user!.Role = role; - string jwt = (await _apiClient.LoginUser(registerRequest.Email, registerRequest.Password)).Token; + await context.SaveChangesAsync(); + + string jwt = (await _apiClient.LoginUser(email, "P4ssword")).Token; Func> act = async () => await _apiClient.Put($"/leaderboard/100/restore", new() { @@ -421,7 +415,7 @@ public async Task RestoreLeaderboard_Unauthorized() [Test] public async Task RestoreLeaderboard_NotFound() { - Func> act = async () => await _apiClient.Put($"/leaderboard/100/restore", new() + Func> act = async () => await _apiClient.Put($"/leaderboard/{1e10}/restore", new() { Jwt = _jwt }); @@ -436,8 +430,8 @@ public async Task RestoreLeaderboard_NotFound_WasNeverDeleted() Leaderboard board = new() { - Name = "Super Mario World", - Slug = "super-mario-world-non-deleted", + Name = "Hyper Mario World", + Slug = "hyper-mario-world-non-deleted", }; context.Leaderboards.Add(board); @@ -451,6 +445,5 @@ public async Task RestoreLeaderboard_NotFound_WasNeverDeleted() await act.Should().ThrowAsync() .Where(e => e.Response.StatusCode == HttpStatusCode.NotFound); - // TODO: Don't know how to test for the response message. } } diff --git a/LeaderboardBackend/Controllers/LeaderboardsController.cs b/LeaderboardBackend/Controllers/LeaderboardsController.cs index 0b66c095..092ba2a0 100644 --- a/LeaderboardBackend/Controllers/LeaderboardsController.cs +++ b/LeaderboardBackend/Controllers/LeaderboardsController.cs @@ -90,10 +90,11 @@ public async Task> CreateLeaderboard( [Authorize(Policy = UserTypes.ADMINISTRATOR)] [HttpPut("leaderboard/{id:long}/restore")] [SwaggerOperation("Restores a deleted leaderboard.", OperationId = "restoreLeaderboard")] - [SwaggerResponse(200)] + [SwaggerResponse(200, "The restored `Leaderboard`s view model.", typeof(LeaderboardViewModel))] [SwaggerResponse(401)] [SwaggerResponse(403, "The requesting `User` is unauthorized to restore `Leaderboard`s.")] - [SwaggerResponse(404, "The `Leaderboard` was not found, or it wasn't deleted in the first place.")] + [SwaggerResponse(404, "The `Leaderboard` was not found, or it wasn't deleted in the first place.", typeof(string))] + [SwaggerResponse(409, "Another `Leaderboard` with the same slug has been created since, and therefore can't be restored.", typeof(LeaderboardViewModel))] public async Task> RestoreLeaderboard( long id ) @@ -106,8 +107,9 @@ long id neverDeleted => { ModelState.AddModelError("Leaderboard", "LeaderboardWasNeverPreviouslyDeleted"); - return NotFound(new ValidationProblemDetails(ModelState)); - } + return NotFound("Was never deleted"); + }, + conflict => Conflict(conflict.Board) ); } } diff --git a/LeaderboardBackend/Results.cs b/LeaderboardBackend/Results.cs index abeadeff..cba5ddb7 100644 --- a/LeaderboardBackend/Results.cs +++ b/LeaderboardBackend/Results.cs @@ -1,3 +1,5 @@ +using LeaderboardBackend.Models.Entities; + namespace LeaderboardBackend.Result; public readonly record struct AccountConfirmed; @@ -10,5 +12,6 @@ namespace LeaderboardBackend.Result; public readonly record struct CreateLeaderboardConflict; public readonly record struct LeaderboardNotFound; public readonly record struct LeaderboardNeverDeleted; +public readonly record struct RestoreLeaderboardConflict(Leaderboard Board); public readonly record struct UserNotFound; public readonly record struct UserBanned; diff --git a/LeaderboardBackend/Services/ILeaderboardService.cs b/LeaderboardBackend/Services/ILeaderboardService.cs index a0979e4b..c6432365 100644 --- a/LeaderboardBackend/Services/ILeaderboardService.cs +++ b/LeaderboardBackend/Services/ILeaderboardService.cs @@ -18,4 +18,4 @@ public interface ILeaderboardService public partial class CreateLeaderboardResult : OneOfBase; [GenerateOneOf] -public partial class RestoreLeaderboardResult : OneOfBase; +public partial class RestoreLeaderboardResult : OneOfBase; diff --git a/LeaderboardBackend/Services/Impl/LeaderboardService.cs b/LeaderboardBackend/Services/Impl/LeaderboardService.cs index 5b5130d6..c7b09b85 100644 --- a/LeaderboardBackend/Services/Impl/LeaderboardService.cs +++ b/LeaderboardBackend/Services/Impl/LeaderboardService.cs @@ -7,7 +7,7 @@ namespace LeaderboardBackend.Services; -public class LeaderboardService(ApplicationContext applicationContext, IClock clock) : ILeaderboardService +public class LeaderboardService(ApplicationContext applicationContext) : ILeaderboardService { public async Task GetLeaderboard(long id) => await applicationContext.Leaderboards.FindAsync(id); @@ -58,9 +58,15 @@ public async Task RestoreLeaderboard(long id) return new LeaderboardNeverDeleted(); } + Leaderboard? maybe = await applicationContext.Leaderboards.SingleOrDefaultAsync(board => board.Slug == lb.Slug && board.DeletedAt == null); + + if (maybe != null) + { + return new RestoreLeaderboardConflict(maybe); + } + applicationContext.Leaderboards.Update(lb); - lb.UpdatedAt = clock.GetCurrentInstant(); lb.DeletedAt = null; await applicationContext.SaveChangesAsync(); diff --git a/LeaderboardBackend/openapi.json b/LeaderboardBackend/openapi.json index a49938e0..01166b48 100644 --- a/LeaderboardBackend/openapi.json +++ b/LeaderboardBackend/openapi.json @@ -697,7 +697,7 @@ "description": "Internal Server Error" }, "200": { - "description": "OK", + "description": "The restored `Leaderboard`s view model.", "content": { "application/json": { "schema": { @@ -713,7 +713,24 @@ "description": "The requesting `User` is unauthorized to restore `Leaderboard`s." }, "404": { - "description": "The `Leaderboard` was not found, or it wasn't deleted in the first place." + "description": "The `Leaderboard` was not found, or it wasn't deleted in the first place.", + "content": { + "application/json": { + "schema": { + "type": "string" + } + } + } + }, + "409": { + "description": "Another `Leaderboard` with the same slug has been created since, and therefore can't be restored.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LeaderboardViewModel" + } + } + } } } }