using Xunit; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; using BCards.Web.Models; using BCards.Web.ViewModels; using BCards.IntegrationTests.Fixtures; using BCards.IntegrationTests.Helpers; using System.Net.Http.Json; namespace BCards.IntegrationTests.Tests; public class PageCreationTests : IClassFixture, IAsyncLifetime { private readonly BCardsWebApplicationFactory _factory; private readonly HttpClient _client; private MongoDbTestFixture _dbFixture = null!; public PageCreationTests(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 CreatePage_WithBasicPlan_ShouldAllowUpTo5Links() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Act - Create a page with 5 links (should succeed) var pageData = new { DisplayName = "Test Business Page", Category = "tecnologia", BusinessType = "company", Bio = "A test business page", Slug = "test-business", SelectedTheme = "minimalist", Links = new[] { new { Title = "Website", Url = "https://example.com", Description = "Main website", Icon = "fas fa-globe" }, new { Title = "Email", Url = "mailto:contact@example.com", Description = "Contact email", Icon = "fas fa-envelope" }, new { Title = "Phone", Url = "tel:+5511999999999", Description = "Contact phone", Icon = "fas fa-phone" }, new { Title = "LinkedIn", Url = "https://linkedin.com/company/example", Description = "LinkedIn profile", Icon = "fab fa-linkedin" }, new { Title = "Instagram", Url = "https://instagram.com/example", Description = "Instagram profile", Icon = "fab fa-instagram" } } }; var createResponse = await authenticatedClient.PostAsJsonAsync("/Admin/ManagePage", pageData); // Assert createResponse.IsSuccessStatusCode.Should().BeTrue("Basic plan should allow 5 links"); // Verify page was created in database var createdPages = await _dbFixture.GetUserPagesAsync(user.Id); createdPages.Should().HaveCount(1); var createdPage = createdPages.First(); createdPage.DisplayName.Should().Be("Test Business Page"); createdPage.Category.Should().Be("tecnologia"); createdPage.Status.Should().Be(PageStatus.Creating); createdPage.Links.Should().HaveCount(5); } [Fact] public async Task CreatePage_WithBasicPlanExceedingLimits_ShouldFail() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Act - Try to create a page with 6 links (should fail for Basic plan) var pageData = new { DisplayName = "Test Page Exceeding Limits", Category = "tecnologia", BusinessType = "individual", Bio = "A test page with too many links", Slug = "test-exceeding", SelectedTheme = "minimalist", Links = new[] { new { Title = "Link 1", Url = "https://example1.com", Description = "Link 1", Icon = "fas fa-link" }, new { Title = "Link 2", Url = "https://example2.com", Description = "Link 2", Icon = "fas fa-link" }, new { Title = "Link 3", Url = "https://example3.com", Description = "Link 3", Icon = "fas fa-link" }, new { Title = "Link 4", Url = "https://example4.com", Description = "Link 4", Icon = "fas fa-link" }, new { Title = "Link 5", Url = "https://example5.com", Description = "Link 5", Icon = "fas fa-link" }, new { Title = "Link 6", Url = "https://example6.com", Description = "Link 6", Icon = "fas fa-link" } // This should fail } }; var createResponse = await authenticatedClient.PostAsJsonAsync("/Admin/ManagePage", pageData); // Assert createResponse.IsSuccessStatusCode.Should().BeFalse("Basic plan should not allow more than 5 links"); // Verify no page was created var createdPages = await _dbFixture.GetUserPagesAsync(user.Id); createdPages.Should().BeEmpty(); } [Fact] public async Task CreatePage_ShouldStartInCreatingStatus() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Basic); var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Act var pageData = new { DisplayName = "New Page", Category = "pessoal", BusinessType = "individual", Bio = "Test page bio", Slug = "new-page", SelectedTheme = "minimalist", Links = new[] { new { Title = "Portfolio", Url = "https://myportfolio.com", Description = "My work", Icon = "fas fa-briefcase" } } }; var createResponse = await authenticatedClient.PostAsJsonAsync("/Admin/ManagePage", pageData); // Assert createResponse.IsSuccessStatusCode.Should().BeTrue(); var createdPages = await _dbFixture.GetUserPagesAsync(user.Id); var page = createdPages.First(); page.Status.Should().Be(PageStatus.Creating); page.PreviewToken.Should().NotBeNullOrEmpty("Creating pages should have preview tokens"); page.PreviewTokenExpiry.Should().BeAfter(DateTime.UtcNow.AddHours(3), "Preview token should be valid for ~4 hours"); } [Fact] public async Task CreatePage_WithTrialPlan_ShouldAllowOnePageOnly() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Trial); var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Act - Create first page (should succeed) var firstPageData = new { DisplayName = "First Trial Page", Category = "pessoal", BusinessType = "individual", Bio = "First page in trial", Slug = "first-trial", SelectedTheme = "minimalist", Links = new[] { new { Title = "Website", Url = "https://example.com", Description = "My website", Icon = "fas fa-globe" } } }; var firstResponse = await authenticatedClient.PostAsJsonAsync("/Admin/ManagePage", firstPageData); firstResponse.IsSuccessStatusCode.Should().BeTrue("Trial should allow first page"); // Act - Try to create second page (should fail) var secondPageData = new { DisplayName = "Second Trial Page", Category = "tecnologia", BusinessType = "individual", Bio = "Second page in trial - should fail", Slug = "second-trial", SelectedTheme = "minimalist", Links = new[] { new { Title = "LinkedIn", Url = "https://linkedin.com/in/test", Description = "LinkedIn", Icon = "fab fa-linkedin" } } }; var secondResponse = await authenticatedClient.PostAsJsonAsync("/Admin/ManagePage", secondPageData); // Assert secondResponse.IsSuccessStatusCode.Should().BeFalse("Trial should not allow second page"); var createdPages = await _dbFixture.GetUserPagesAsync(user.Id); createdPages.Should().HaveCount(1, "Trial should only have one page"); } [Fact] public async Task CreatePage_ShouldGenerateUniqueSlug() { // Arrange var user = await _dbFixture.CreateTestUserAsync(PlanType.Professional); var authenticatedClient = await AuthenticationHelper.CreateAuthenticatedClientAsync(_factory, user); // Create first page with specific slug await _dbFixture.CreateTestUserPageAsync(user.Id, PageStatus.Active, "tecnologia", 3, 1, "test-slug"); // Act - Try to create another page with same name (should get different slug) var pageData = new { DisplayName = "Test Page", // Same display name, should generate different slug Category = "tecnologia", BusinessType = "individual", Bio = "Another test page", Slug = "test-slug", // Try to use same slug SelectedTheme = "minimalist", Links = new[] { new { Title = "Website", Url = "https://example.com", Description = "Website", Icon = "fas fa-globe" } } }; var createResponse = await authenticatedClient.PostAsJsonAsync("/Admin/ManagePage", pageData); // Assert createResponse.IsSuccessStatusCode.Should().BeTrue(); var userPages = await _dbFixture.GetUserPagesAsync(user.Id); userPages.Should().HaveCount(2); var slugs = userPages.Select(p => p.Slug).ToList(); slugs.Should().OnlyHaveUniqueItems("All pages should have unique slugs"); slugs.Should().Contain("test-slug"); slugs.Should().Contain(slug => slug.StartsWith("test-slug-") || slug == "test-page"); } }