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 RegistrationsControllerTests : IDisposable { private readonly RacePlannerDbContext _context; private readonly RegistrationsController _controller; public RegistrationsControllerTests() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _context = new RacePlannerDbContext(options); _controller = new RegistrationsController(_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 Create Registration - Positive Tests [Fact] public async Task CreateRegistration_WithValidData_CreatesRegistration() { // 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 eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new CreateRegistrationRequest { EventId = eventEntity.Id, Category = "Open", EmergencyContact = "Emergency Contact: 123-456-7890" }; // Act var result = await _controller.CreateRegistration(request); // Assert var createdResult = result.Result.Should().BeOfType().Subject; var response = createdResult.Value.Should().BeOfType().Subject; response.EventId.Should().Be(eventEntity.Id); response.ParticipantId.Should().Be(participant.Id); response.Status.Should().Be("Pending"); } [Fact] public async Task CreateRegistration_SetsStatusToPending() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant2@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new CreateRegistrationRequest { EventId = eventEntity.Id, Category = "Open" }; // Act var result = await _controller.CreateRegistration(request); // Assert var createdResult = result.Result.Should().BeOfType().Subject; var response = createdResult.Value.Should().BeOfType().Subject; response.Status.Should().Be("Pending"); } #endregion #region Create Registration - Negative Tests [Fact] public async Task CreateRegistration_ForDraftEvent_ReturnsBadRequest() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant3@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var draftEvent = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Draft); _context.Events.Add(draftEvent); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new CreateRegistrationRequest { EventId = draftEvent.Id, Category = "Open" }; // Act var result = await _controller.CreateRegistration(request); // Assert var badRequestResult = result.Result.Should().BeOfType().Subject; badRequestResult.Value.Should().BeEquivalentTo(new { error = "Event is not open for registration" }); } [Fact] public async Task CreateRegistration_DuplicateRegistration_ReturnsConflict() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant4@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); // Create existing registration var existingRegistration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id); _context.Registrations.Add(existingRegistration); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new CreateRegistrationRequest { EventId = eventEntity.Id, Category = "Open" }; // Act var result = await _controller.CreateRegistration(request); // Assert var conflictResult = result.Result.Should().BeOfType().Subject; conflictResult.Value.Should().BeEquivalentTo(new { error = "Already registered for this event" }); } [Fact] public async Task CreateRegistration_ForFullEvent_ReturnsBadRequest() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant1 = TestDataFactory.CreateUser(email: "participant1@example.com", role: UserRole.Participant); var participant2 = TestDataFactory.CreateUser(email: "participant2@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant1, participant2); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); eventEntity.MaxParticipants = 1; _context.Events.Add(eventEntity); // Fill the event var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant1.Id, RegistrationStatus.Confirmed); _context.Registrations.Add(registration); await _context.SaveChangesAsync(); SetUserContext(participant2.Id, "Participant"); var request = new CreateRegistrationRequest { EventId = eventEntity.Id, Category = "Open" }; // Act var result = await _controller.CreateRegistration(request); // Assert var badRequestResult = result.Result.Should().BeOfType().Subject; badRequestResult.Value.Should().BeEquivalentTo(new { error = "Event is full" }); } [Fact] public async Task CreateRegistration_ForNonExistentEvent_ReturnsNotFound() { // Arrange var participant = TestDataFactory.CreateUser(role: UserRole.Participant); _context.Users.Add(participant); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new CreateRegistrationRequest { EventId = Guid.NewGuid(), Category = "Open" }; // Act var result = await _controller.CreateRegistration(request); // Assert var notFoundResult = result.Result.Should().BeOfType().Subject; notFoundResult.Value.Should().BeEquivalentTo(new { error = "Event not found" }); } #endregion #region Get Registration - Positive Tests [Fact] public async Task GetRegistration_ParticipantCanViewOwnRegistration() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant5@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id); _context.Registrations.Add(registration); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.GetRegistration(registration.Id); // Assert result.Result.Should().BeOfType(); } [Fact] public async Task GetRegistration_OrganizerCanViewAnyRegistrationForTheirEvent() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant6@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id); _context.Registrations.Add(registration); await _context.SaveChangesAsync(); SetUserContext(organizer.Id, "Organizer"); // Act var result = await _controller.GetRegistration(registration.Id); // Assert result.Result.Should().BeOfType(); } [Fact] public async Task GetMyRegistrations_ReturnsParticipantsRegistrations() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant7@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var event1 = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); var event2 = TestDataFactory.CreateEvent(name: "Event 2", organizerId: organizer.Id, status: EventStatus.Published); _context.Events.AddRange(event1, event2); var registration1 = TestDataFactory.CreateRegistration(event1.Id, participant.Id); var registration2 = TestDataFactory.CreateRegistration(event2.Id, participant.Id); _context.Registrations.AddRange(registration1, registration2); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.GetMyRegistrations(); // Assert var okResult = result.Result.Should().BeOfType().Subject; var registrations = okResult.Value.Should().BeAssignableTo>().Subject; registrations.Should().HaveCount(2); } #endregion #region Get Registration - Negative Tests [Fact] public async Task GetRegistration_NonExistentRegistration_ReturnsNotFound() { // Arrange var participant = TestDataFactory.CreateUser(role: UserRole.Participant); _context.Users.Add(participant); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.GetRegistration(Guid.NewGuid()); // Assert result.Result.Should().BeOfType(); } #endregion #region Cancel Registration - Positive Tests [Fact] public async Task CancelRegistration_ParticipantCanCancelOwnRegistration() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant8@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id, RegistrationStatus.Pending); _context.Registrations.Add(registration); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.CancelRegistration(registration.Id); // Assert var okResult = result.Result.Should().BeOfType().Subject; var response = okResult.Value.Should().BeOfType().Subject; response.Status.Should().Be("Cancelled"); // Verify in database var updatedRegistration = await _context.Registrations.FindAsync(registration.Id); updatedRegistration!.Status.Should().Be(RegistrationStatus.Cancelled); } [Fact] public async Task CancelRegistration_OrganizerCanCancelAnyRegistration() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant9@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id, RegistrationStatus.Pending); _context.Registrations.Add(registration); await _context.SaveChangesAsync(); SetUserContext(organizer.Id, "Organizer"); // Act var result = await _controller.CancelRegistration(registration.Id); // Assert var okResult = result.Result.Should().BeOfType().Subject; var response = okResult.Value.Should().BeOfType().Subject; response.Status.Should().Be("Cancelled"); } #endregion #region Cancel Registration - Negative Tests [Fact] public async Task CancelRegistration_NonExistentRegistration_ReturnsNotFound() { // Arrange var participant = TestDataFactory.CreateUser(role: UserRole.Participant); _context.Users.Add(participant); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); // Act var result = await _controller.CancelRegistration(Guid.NewGuid()); // Assert result.Result.Should().BeOfType(); } #endregion #region Update Registration - Positive Tests [Fact] public async Task UpdateRegistration_ParticipantCanUpdateOwnRegistration() { // Arrange var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); var participant = TestDataFactory.CreateUser(email: "participant10@example.com", role: UserRole.Participant); _context.Users.AddRange(organizer, participant); var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); _context.Events.Add(eventEntity); var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id); _context.Registrations.Add(registration); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new UpdateRegistrationRequest { Category = "Updated Category", EmergencyContact = "Updated Emergency Contact" }; // Act var result = await _controller.UpdateRegistration(registration.Id, request); // Assert var okResult = result.Result.Should().BeOfType().Subject; var response = okResult.Value.Should().BeOfType().Subject; response.Category.Should().Be("Updated Category"); } #endregion #region Update Registration - Negative Tests [Fact] public async Task UpdateRegistration_NonExistentRegistration_ReturnsNotFound() { // Arrange var participant = TestDataFactory.CreateUser(role: UserRole.Participant); _context.Users.Add(participant); await _context.SaveChangesAsync(); SetUserContext(participant.Id, "Participant"); var request = new UpdateRegistrationRequest { Category = "Updated" }; // Act var result = await _controller.UpdateRegistration(Guid.NewGuid(), request); // Assert result.Result.Should().BeOfType(); } #endregion }