using System.Security.Claims; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Moq; using FluentAssertions; using RacePlannerApi.Controllers; using RacePlannerApi.Data; using RacePlannerApi.DTOs; using RacePlannerApi.Models; using backend.Tests.Utilities; namespace backend.Tests.Controllers; public class DashboardControllerTests : IDisposable { private readonly RacePlannerDbContext _context; private readonly DashboardController _controller; public DashboardControllerTests() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _context = new RacePlannerDbContext(options); _controller = new DashboardController(_context); } private void SetUserContext(Guid userId, string role = "Participant") { var claims = new List { new Claim(ClaimTypes.NameIdentifier, userId.ToString()), new Claim(ClaimTypes.Role, role) }; var identity = new ClaimsIdentity(claims, "TestAuthType"); var principal = new ClaimsPrincipal(identity); _controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = principal } }; } public void Dispose() { _context.Dispose(); } #region Organizer Dashboard - Positive Tests [Fact] public async Task GetOrganizerDashboard_ReturnsDashboardData() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); _context.Users.Add(organizer); // Create events var pastEvent = TestDataFactory.CreateEvent( name: "Past Event", eventDate: DateTime.UtcNow.AddDays(-10), organizerId: organizer.Id, status: EventStatus.Completed); var draftEvent = TestDataFactory.CreateEvent( name: "Draft Event", eventDate: DateTime.UtcNow.AddDays(30), organizerId: organizer.Id, status: EventStatus.Draft); var publishedEvent = TestDataFactory.CreateEvent( name: "Published Event", eventDate: DateTime.UtcNow.AddDays(15), organizerId: organizer.Id, status: EventStatus.Published); publishedEvent.MaxParticipants = 100; _context.Events.AddRange(pastEvent, draftEvent, publishedEvent); await _context.SaveChangesAsync(); // Add registrations with payments var participant1 = TestDataFactory.CreateUser(email: "p1@example.com", role: UserRole.Participant); var participant2 = TestDataFactory.CreateUser(email: "p2@example.com", role: UserRole.Participant); _context.Users.AddRange(participant1, participant2); await _context.SaveChangesAsync(); var reg1 = TestDataFactory.CreateRegistration(publishedEvent.Id, participant1.Id, RegistrationStatus.Confirmed); var reg2 = TestDataFactory.CreateRegistration(publishedEvent.Id, participant2.Id, RegistrationStatus.Pending); _context.Registrations.AddRange(reg1, reg2); await _context.SaveChangesAsync(); var payment = TestDataFactory.CreatePayment(reg1.Id, 50.00m, PaymentMethod.Cash); _context.Payments.Add(payment); await _context.SaveChangesAsync(); SetUserContext(organizer.Id, "Organizer"); // Act var result = await _controller.GetOrganizerDashboard(); // Assert var okResult = result.Result.Should().BeOfType().Subject; var dashboard = okResult.Value.Should().BeOfType().Subject; dashboard.TotalEvents.Should().Be(3); dashboard.PublishedEvents.Should().Be(1); dashboard.DraftEvents.Should().Be(1); dashboard.TotalRegistrations.Should().Be(2); dashboard.ConfirmedRegistrations.Should().Be(1); dashboard.PendingRegistrations.Should().Be(1); dashboard.TotalRevenue.Should().Be(50.00m); dashboard.UpcomingEvents.Should().HaveCount(2); } [Fact] public async Task GetOrganizerDashboard_EmptyOrganizer_ReturnsEmptyDashboard() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); _context.Users.Add(organizer); await _context.SaveChangesAsync(); SetUserContext(organizer.Id, "Organizer"); // Act var result = await _controller.GetOrganizerDashboard(); // Assert var okResult = result.Result.Should().BeOfType().Subject; var dashboard = okResult.Value.Should().BeOfType().Subject; dashboard.TotalEvents.Should().Be(0); dashboard.TotalRegistrations.Should().Be(0); dashboard.TotalRevenue.Should().Be(0); dashboard.UpcomingEvents.Should().BeEmpty(); } #endregion #region Participant Dashboard - Positive Tests [Fact] public async Task GetParticipantDashboard_ReturnsDashboardData() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var upcomingEvent = TestDataFactory.CreateEvent( name: "Upcoming Event", eventDate: DateTime.UtcNow.AddDays(15), organizerId: organizer.Id, status: EventStatus.Published); var pastEvent = TestDataFactory.CreateEvent( name: "Past Event", eventDate: DateTime.UtcNow.AddDays(-10), organizerId: organizer.Id, status: EventStatus.Completed); var cancelledEvent = TestDataFactory.CreateEvent(name: "Cancelled Event", organizerId: organizer.Id, status: EventStatus.Published); _context.Events.AddRange(upcomingEvent, pastEvent, cancelledEvent); await _context.SaveChangesAsync(); var upcomingReg = TestDataFactory.CreateRegistration(upcomingEvent.Id, participant.Id, RegistrationStatus.Confirmed); var pastReg = TestDataFactory.CreateRegistration(pastEvent.Id, participant.Id, RegistrationStatus.Completed); var cancelledReg = TestDataFactory.CreateRegistration(cancelledEvent.Id, participant.Id, RegistrationStatus.Cancelled); _context.Registrations.AddRange(upcomingReg, pastReg, cancelledReg); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.GetParticipantDashboard(); // Assert var okResult = result.Result.Should().BeOfType().Subject; var dashboard = okResult.Value.Should().BeOfType().Subject; dashboard.TotalRegistrations.Should().Be(3); dashboard.UpcomingEvents.Should().Be(1); dashboard.CompletedEvents.Should().Be(1); dashboard.CancelledRegistrations.Should().Be(1); dashboard.MyRegistrations.Should().HaveCount(3); dashboard.UpcomingEventList.Should().HaveCount(1); } [Fact] public async Task GetParticipantDashboard_NoRegistrations_ReturnsEmptyDashboard() { // Arrange var participant = TestDataFactory.CreateUser(role: UserRole.Participant); _context.Users.Add(participant); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.GetParticipantDashboard(); // Assert var okResult = result.Result.Should().BeOfType().Subject; var dashboard = okResult.Value.Should().BeOfType().Subject; dashboard.TotalRegistrations.Should().Be(0); dashboard.UpcomingEvents.Should().Be(0); dashboard.MyRegistrations.Should().BeEmpty(); } #endregion #region Dashboard - Negative Tests [Fact] public async Task GetOrganizerDashboard_Unauthenticated_ReturnsUnauthorized() { // Arrange _controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() } }; // Act var result = await _controller.GetOrganizerDashboard(); // Assert result.Result.Should().BeOfType(); } [Fact] public async Task GetParticipantDashboard_Unauthenticated_ReturnsUnauthorized() { // Arrange _controller.ControllerContext = new ControllerContext { HttpContext = new DefaultHttpContext { User = new ClaimsPrincipal() } }; // Act var result = await _controller.GetParticipantDashboard(); // Assert result.Result.Should().BeOfType(); } #endregion #region Event Capacity Tests [Fact] public async Task GetOrganizerDashboard_ShowsEventsNearCapacity() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); _context.Users.Add(organizer); // Event at 90% capacity (should appear in near capacity list) var nearCapacityEvent = TestDataFactory.CreateEvent( name: "Near Capacity", eventDate: DateTime.UtcNow.AddDays(10), organizerId: organizer.Id, status: EventStatus.Published); nearCapacityEvent.MaxParticipants = 10; // Event at 50% capacity (should NOT appear) var normalEvent = TestDataFactory.CreateEvent( name: "Normal", eventDate: DateTime.UtcNow.AddDays(10), organizerId: organizer.Id, status: EventStatus.Published); normalEvent.MaxParticipants = 100; _context.Events.AddRange(nearCapacityEvent, normalEvent); await _context.SaveChangesAsync(); // Add registrations var participant1 = TestDataFactory.CreateUser(email: "p1@test.com", role: UserRole.Participant); var participant2 = TestDataFactory.CreateUser(email: "p2@test.com", role: UserRole.Participant); _context.Users.AddRange(participant1, participant2); await _context.SaveChangesAsync(); // Fill near capacity event to 90% for (int i = 0; i < 9; i++) { var p = TestDataFactory.CreateUser(email: $"test{i}@test.com", role: UserRole.Participant); _context.Users.Add(p); var reg = TestDataFactory.CreateRegistration(nearCapacityEvent.Id, p.Id, RegistrationStatus.Confirmed); _context.Registrations.Add(reg); } // Fill normal event to 50% for (int i = 0; i < 50; i++) { var p = TestDataFactory.CreateUser(email: $"normal{i}@test.com", role: UserRole.Participant); _context.Users.Add(p); var reg = TestDataFactory.CreateRegistration(normalEvent.Id, p.Id, RegistrationStatus.Confirmed); _context.Registrations.Add(reg); } await _context.SaveChangesAsync(); SetUserContext(organizer.Id, "Organizer"); // Act var result = await _controller.GetOrganizerDashboard(); // Assert var okResult = result.Result.Should().BeOfType().Subject; var dashboard = okResult.Value.Should().BeOfType().Subject; dashboard.EventsNearCapacity.Should().HaveCount(1); dashboard.EventsNearCapacity.First().Name.Should().Be("Near Capacity"); } #endregion }