using System.Net; using System.Net.Http.Json; using FluentAssertions; using MembershipService.API.Application.Queries; using Xunit; namespace MembershipService.FunctionalTests.Controllers; /// /// EN: Functional tests for LevelsController. /// VI: Functional tests cho LevelsController. /// [Collection("Sequential")] public class LevelsControllerTests : IClassFixture { private readonly CustomWebApplicationFactory _factory; public LevelsControllerTests(CustomWebApplicationFactory factory) { _factory = factory; } [Fact] public async Task GetLevels_PublicEndpoint_ShouldReturnOkWithoutAuth() { // Arrange var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); } [Fact] public async Task GetLevels_WithSeededData_ShouldReturnLevelsList() { // Arrange await _factory.SeedLevelDefinitionsAsync(); var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels"); var levels = await response.Content.ReadFromJsonAsync>(); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); levels.Should().NotBeNull(); levels!.Should().HaveCountGreaterOrEqualTo(5); } [Fact] public async Task GetLevels_ShouldReturnOrderedByLevelNumber() { // Arrange await _factory.SeedLevelDefinitionsAsync(); var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels"); var levels = await response.Content.ReadFromJsonAsync>(); // Assert levels.Should().NotBeNull(); levels!.Should().BeInAscendingOrder(l => l.LevelNumber); } [Fact] public async Task GetLevels_ShouldReturnCorrectLevelData() { // Arrange await _factory.SeedLevelDefinitionsAsync(); var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels"); var levels = await response.Content.ReadFromJsonAsync>(); // Assert levels.Should().NotBeNull(); if (levels == null || !levels.Any()) return; var bronze = levels.FirstOrDefault(l => l.LevelNumber == 1); bronze.Should().NotBeNull(); bronze!.Name.Should().Be("Bronze"); bronze.RequiredExp.Should().Be(0); bronze.BadgeColor.Should().Be("#CD7F32"); } [Fact] public async Task GetLevels_IncludeInactiveFalse_ShouldOnlyReturnActive() { // Arrange await _factory.SeedLevelDefinitionsAsync(); var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels?includeInactive=false"); var levels = await response.Content.ReadFromJsonAsync>(); // Assert levels.Should().NotBeNull(); if (levels != null && levels.Any()) { levels.Should().AllSatisfy(l => l.IsActive.Should().BeTrue()); } } [Fact] public async Task GetLevels_ShouldReturnExpectedExpThresholds() { // Arrange await _factory.SeedLevelDefinitionsAsync(); var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels"); var levels = await response.Content.ReadFromJsonAsync>(); // Assert levels.Should().NotBeNull(); if (levels == null || !levels.Any()) return; var expectedThresholds = new Dictionary { { 1, 0 }, // Bronze { 2, 100 }, // Silver { 3, 300 }, // Gold { 4, 600 }, // Platinum { 5, 1000 } // Diamond }; foreach (var expected in expectedThresholds) { var level = levels.FirstOrDefault(l => l.LevelNumber == expected.Key); if (level != null) { level.RequiredExp.Should().Be(expected.Value); } } } [Fact] public async Task GetLevels_EachLevelShouldHaveUniqueId() { // Arrange await _factory.SeedLevelDefinitionsAsync(); var client = _factory.CreateClient(); // Act var response = await client.GetAsync("/api/v1/levels"); var levels = await response.Content.ReadFromJsonAsync>(); // Assert levels.Should().NotBeNull(); if (levels != null && levels.Any()) { var ids = levels.Select(l => l.Id).ToList(); ids.Should().OnlyHaveUniqueItems(); } } }