using Xunit; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; using BCards.Web.Models; using BCards.Web.ViewModels; using BCards.Web.Services; using BCards.IntegrationTests.Fixtures; using BCards.IntegrationTests.Helpers; using System.Net; namespace BCards.IntegrationTests.Tests; public class ModerationWorkflowTests : IClassFixture, IAsyncLifetime { private readonly BCardsWebApplicationFactory _factory; private readonly HttpClient _client; private MongoDbTestFixture _dbFixture = null!; public ModerationWorkflowTests(BCardsWebApplicationFactory factory) { _factory = factory; _client = _factory.CreateClient(); } public async Task InitializeAsync() { using var scope = _factory.Services.CreateScope(); var database = scope.ServiceProvider.GetRequiredService(); _dbFixture = new MongoDbTestFixture(database); await _factory.CleanDatabaseAsync(); await _dbFixture.InitializeTestDataAsync(); } public Task DisposeAsync() => Task.CompletedTask; [Fact] public async Task SubmitPageForModeration_ShouldChangeStatusToPendingModeration() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var page = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.Creating, "tecnologia", 3, 1); var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Act - Submit page for moderation var response = await authenticatedClient.PostAsync($"/Admin/SubmitForModeration/{page.Id}", null); // Assert response.IsSuccessStatusCode.Should().BeTrue(); // Verify page status changed in database var updatedPages = await _dbFixture.GetUserPagesAsync(user.Id); var updatedPage = updatedPages.First(); updatedPage.Status.Should().Be(PageStatus.PendingModeration); updatedPage.ModerationAttempts.Should().BeGreaterThan(0); } [Fact] public async Task SubmitPageForModeration_WithoutActiveLinks_ShouldFail() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var page = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.Creating, "tecnologia", 0, 0); // No links var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Act var response = await authenticatedClient.PostAsync($"/Admin/SubmitForModeration/{page.Id}", null); // Assert response.IsSuccessStatusCode.Should().BeFalse(); // Verify page status didn't change var updatedPages = await _dbFixture.GetUserPagesAsync(user.Id); var updatedPage = updatedPages.First(); updatedPage.Status.Should().Be(PageStatus.Creating); } [Fact] public async Task ApprovePage_ShouldChangeStatusToActive() { // Arrange using var scope = _factory.Services.CreateScope(); var moderationService = scope.ServiceProvider.GetRequiredService(); var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var page = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.PendingModeration, "tecnologia", 3, 1); // Act - Approve the page await moderationService.ApprovePageAsync(page.Id, "test-moderator-id", "Page looks good"); // Assert var updatedPages = await _dbFixture.GetUserPagesAsync(user.Id); var updatedPage = updatedPages.First(); updatedPage.Status.Should().Be(PageStatus.Active); updatedPage.ApprovedAt.Should().BeCloseTo(DateTime.UtcNow, TimeSpan.FromMinutes(1)); updatedPage.ModerationHistory.Should().HaveCount(1); updatedPage.ModerationHistory.First().Status.Should().Be("approved"); } [Fact] public async Task RejectPage_ShouldChangeStatusToRejected() { // Arrange using var scope = _factory.Services.CreateScope(); var moderationService = scope.ServiceProvider.GetRequiredService(); var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var page = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.PendingModeration, "tecnologia", 3, 1); // Act - Reject the page await moderationService.RejectPageAsync(page.Id, "test-moderator-id", "Inappropriate content", new List { "spam", "offensive" }); // Assert var updatedPages = await _dbFixture.GetUserPagesAsync(user.Id); var updatedPage = updatedPages.First(); updatedPage.Status.Should().Be(PageStatus.Inactive); // First rejection goes to Inactive updatedPage.ModerationHistory.Should().HaveCount(1); var rejectionHistory = updatedPage.ModerationHistory.First(); rejectionHistory.Status.Should().Be("rejected"); rejectionHistory.Reason.Should().Be("Inappropriate content"); rejectionHistory.Issues.Should().Contain("spam"); rejectionHistory.Issues.Should().Contain("offensive"); } [Fact] public async Task AccessApprovedPage_WithoutPreviewToken_ShouldSucceed() { // Arrange using var scope = _factory.Services.CreateScope(); var moderationService = scope.ServiceProvider.GetRequiredService(); var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var page = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.PendingModeration, "tecnologia", 3, 1); // Approve the page await moderationService.ApprovePageAsync(page.Id, "test-moderator-id", "Approved"); // Act - Access the page without preview token var response = await _client.GetAsync($"/page/{page.Category}/{page.Slug}"); // Assert response.StatusCode.Should().Be(HttpStatusCode.OK); var content = await response.Content.ReadAsStringAsync(); content.Should().Contain(page.DisplayName); content.Should().NotContain("MODO PREVIEW"); // Should not show preview banner } [Fact] public async Task GetPendingModerationPages_ShouldReturnCorrectPages() { // Arrange using var scope = _factory.Services.CreateScope(); var moderationService = scope.ServiceProvider.GetRequiredService(); var user1 = await _dbFixture.CreateTestUserAsync(PlanType.Basic, "user1@example.com"); var user2 = await _dbFixture.CreateTestUserAsync(PlanType.Basic, "user2@example.com"); // Create pages in different statuses await _dbFixture.CreateTestUserPageAsync(user1.Id, PageStatus.PendingModeration, "tecnologia", 3, 1); await _dbFixture.CreateTestUserPageAsync(user2.Id, PageStatus.PendingModeration, "negocios", 4, 2); await _dbFixture.CreateTestUserPageAsync(user1.Id, PageStatus.Creating, "pessoal", 2, 0); // Should not appear await _dbFixture.CreateTestUserPageAsync(user2.Id, PageStatus.Active, "saude", 5, 1); // Should not appear // Act var pendingPages = await moderationService.GetPendingModerationAsync(); // Assert pendingPages.Should().HaveCount(2); pendingPages.Should().OnlyContain(p => p.Status == PageStatus.PendingModeration); } [Fact] public async Task ModerationStats_ShouldReturnCorrectCounts() { // Arrange using var scope = _factory.Services.CreateScope(); var moderationService = scope.ServiceProvider.GetRequiredService(); var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); // Create pages with different statuses var pendingPage1 = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.PendingModeration, "tecnologia", 3, 1); var pendingPage2 = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.PendingModeration, "negocios", 3, 1); var activePage = await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.PendingModeration, "pessoal", 3, 1); // Approve one page today await moderationService.ApprovePageAsync(activePage.Id, "moderator", "Good"); // Act var stats = await moderationService.GetModerationStatsAsync(); // Assert stats["pending"].Should().Be(2); stats["approvedToday"].Should().Be(1); stats["rejectedToday"].Should().Be(0); } }