using System.Net; using System.Net.Http.Json; using FluentAssertions; using MembershipService.API.Application.Commands; using Xunit; namespace MembershipService.FunctionalTests.Controllers; /// /// EN: Functional tests for Admin Level endpoints. /// VI: Functional tests cho Admin Level endpoints. /// [Collection("Sequential")] public class AdminLevelsControllerTests : IClassFixture { private readonly CustomWebApplicationFactory _factory; public AdminLevelsControllerTests(CustomWebApplicationFactory factory) { _factory = factory; } #region POST /api/v1/levels - Create Level [Fact] public async Task CreateLevel_WithAuth_ShouldReturn201() { // Arrange var client = _factory.CreateAuthenticatedClient(); var command = new CreateLevelDefinitionCommand { LevelNumber = 10, Name = "Test Level", RequiredExp = 5000, Description = "Test level for functional tests", BadgeColor = "#FF5733" }; // Act var response = await client.PostAsJsonAsync("/api/v1/levels", command); // Assert response.StatusCode.Should().Be(HttpStatusCode.Created); var result = await response.Content.ReadFromJsonAsync(); result.Should().NotBeNull(); result!.LevelNumber.Should().Be(10); result.Name.Should().Be("Test Level"); result.RequiredExp.Should().Be(5000); } [Fact] public async Task CreateLevel_WithoutAuth_ShouldReturn401() { // Arrange var client = _factory.CreateClient(); // No auth var command = new CreateLevelDefinitionCommand { LevelNumber = 11, Name = "Unauthorized Level", RequiredExp = 6000 }; // Act var response = await client.PostAsJsonAsync("/api/v1/levels", command); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task CreateLevel_DuplicateLevelNumber_ShouldReturn409() { // Arrange var client = _factory.CreateAuthenticatedClient(); // First create a level var firstCommand = new CreateLevelDefinitionCommand { LevelNumber = 50, // Use unique number to avoid conflicts with other tests Name = "First Level", RequiredExp = 500 }; var firstResponse = await client.PostAsJsonAsync("/api/v1/levels", firstCommand); firstResponse.StatusCode.Should().Be(HttpStatusCode.Created); // Now try to create duplicate var duplicateCommand = new CreateLevelDefinitionCommand { LevelNumber = 50, // Same level number - should conflict Name = "Duplicate", RequiredExp = 500 }; // Act var response = await client.PostAsJsonAsync("/api/v1/levels", duplicateCommand); // Assert response.StatusCode.Should().Be(HttpStatusCode.Conflict); } #endregion #region PUT /api/v1/levels/{id} - Update Level [Fact] public async Task UpdateLevel_WithAuth_ShouldReturn200() { // Arrange var client = _factory.CreateAuthenticatedClient(); // First create a level to update var createCommand = new CreateLevelDefinitionCommand { LevelNumber = 60, Name = "ToUpdate", RequiredExp = 6000, Description = "Original description" }; var createResponse = await client.PostAsJsonAsync("/api/v1/levels", createCommand); createResponse.StatusCode.Should().Be(HttpStatusCode.Created); var createdLevel = await createResponse.Content.ReadFromJsonAsync(); var updateCommand = new UpdateLevelDefinitionCommand { Id = createdLevel!.Id, Name = "Updated Level", Description = "Updated description" }; // Act var response = await client.PutAsJsonAsync($"/api/v1/levels/{createdLevel.Id}", updateCommand); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content.ReadFromJsonAsync(); result.Should().NotBeNull(); result!.Name.Should().Be("Updated Level"); result.Description.Should().Be("Updated description"); } [Fact] public async Task UpdateLevel_WithoutAuth_ShouldReturn401() { // Arrange var client = _factory.CreateClient(); // No auth var randomId = Guid.NewGuid(); var command = new UpdateLevelDefinitionCommand { Id = randomId, Name = "Unauthorized Update" }; // Act var response = await client.PutAsJsonAsync($"/api/v1/levels/{randomId}", command); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task UpdateLevel_NotFound_ShouldReturn404() { // Arrange var client = _factory.CreateAuthenticatedClient(); var nonExistentId = Guid.NewGuid(); var command = new UpdateLevelDefinitionCommand { Id = nonExistentId, Name = "NonExistent" }; // Act var response = await client.PutAsJsonAsync($"/api/v1/levels/{nonExistentId}", command); // Assert response.StatusCode.Should().Be(HttpStatusCode.NotFound); } #endregion #region DELETE /api/v1/levels/{id} - Deactivate Level [Fact] public async Task DeactivateLevel_WithAuth_ShouldReturn204() { // Arrange var client = _factory.CreateAuthenticatedClient(); // First create a level to deactivate var createCommand = new CreateLevelDefinitionCommand { LevelNumber = 99, Name = "ToDeactivate", RequiredExp = 9999 }; var createResponse = await client.PostAsJsonAsync("/api/v1/levels", createCommand); var createdLevel = await createResponse.Content.ReadFromJsonAsync(); // Act var response = await client.DeleteAsync($"/api/v1/levels/{createdLevel!.Id}"); // Assert response.StatusCode.Should().Be(HttpStatusCode.NoContent); } [Fact] public async Task DeactivateLevel_WithoutAuth_ShouldReturn401() { // Arrange var client = _factory.CreateClient(); // No auth var randomId = Guid.NewGuid(); // Act var response = await client.DeleteAsync($"/api/v1/levels/{randomId}"); // Assert response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task DeactivateLevel_NotFound_ShouldReturn404() { // Arrange var client = _factory.CreateAuthenticatedClient(); var nonExistentId = Guid.NewGuid(); // Act var response = await client.DeleteAsync($"/api/v1/levels/{nonExistentId}"); // Assert response.StatusCode.Should().Be(HttpStatusCode.NotFound); } #endregion #region Helper DTOs private class LevelDto { public Guid Id { get; set; } public int LevelNumber { get; set; } public string Name { get; set; } = string.Empty; } #endregion }