diff --git a/backend/backend.Tests/Controllers/PaymentsControllerTests.cs b/backend/backend.Tests/Controllers/PaymentsControllerTests.cs new file mode 100644 index 0000000..885512d --- /dev/null +++ b/backend/backend.Tests/Controllers/PaymentsControllerTests.cs @@ -0,0 +1,381 @@ +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 PaymentsControllerTests : IDisposable +{ + private readonly RacePlannerDbContext _context; + private readonly PaymentsController _controller; + + public PaymentsControllerTests() + { + var options = new DbContextOptionsBuilder() + .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .Options; + + _context = new RacePlannerDbContext(options); + _controller = new PaymentsController(_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 Record Payment - Positive Tests + + [Fact] + public async Task RecordPayment_WithValidData_RecordsPayment() + { + // 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); + + var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id, RegistrationStatus.Confirmed); + _context.Registrations.Add(registration); + await _context.SaveChangesAsync(); + + SetUserContext(organizer.Id, "Organizer"); + + var request = new CreatePaymentRequest + { + RegistrationId = registration.Id, + Amount = 50.00m, + Method = PaymentMethod.Cash, + Notes = "Cash payment received" + }; + + // Act + var result = await _controller.RecordPayment(request); + + // Assert + var createdResult = result.Result.Should().BeOfType().Subject; + var response = createdResult.Value.Should().BeOfType().Subject; + response.Amount.Should().Be(50.00m); + response.Method.Should().Be("Cash"); + } + + [Fact] + public async Task RecordPayment_OnlinePayment_WithTransactionId() + { + // 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); + + var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id, RegistrationStatus.Confirmed); + _context.Registrations.Add(registration); + await _context.SaveChangesAsync(); + + SetUserContext(organizer.Id, "Organizer"); + + var request = new CreatePaymentRequest + { + RegistrationId = registration.Id, + Amount = 75.00m, + Method = PaymentMethod.Online, + TransactionId = "txn_12345", + Notes = "Online payment" + }; + + // Act + var result = await _controller.RecordPayment(request); + + // Assert + var createdResult = result.Result.Should().BeOfType().Subject; + var response = createdResult.Value.Should().BeOfType().Subject; + response.Method.Should().Be("Online"); + response.TransactionId.Should().Be("txn_12345"); + } + + #endregion + + #region Record Payment - Negative Tests + + [Fact] + public async Task RecordPayment_ForCancelledRegistration_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 eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); + _context.Events.Add(eventEntity); + + var registration = TestDataFactory.CreateRegistration(eventEntity.Id, participant.Id, RegistrationStatus.Cancelled); + _context.Registrations.Add(registration); + await _context.SaveChangesAsync(); + + SetUserContext(organizer.Id, "Organizer"); + + var request = new CreatePaymentRequest + { + RegistrationId = registration.Id, + Amount = 50.00m, + Method = PaymentMethod.Cash + }; + + // Act + var result = await _controller.RecordPayment(request); + + // Assert + var badRequestResult = result.Result.Should().BeOfType().Subject; + badRequestResult.Value.Should().BeEquivalentTo(new { error = "Cannot record payment for cancelled registration" }); + } + + [Fact] + public async Task RecordPayment_ForNonExistentRegistration_ReturnsNotFound() + { + // Arrange + var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); + _context.Users.Add(organizer); + await _context.SaveChangesAsync(); + + SetUserContext(organizer.Id, "Organizer"); + + var request = new CreatePaymentRequest + { + RegistrationId = Guid.NewGuid(), + Amount = 50.00m, + Method = PaymentMethod.Cash + }; + + // Act + var result = await _controller.RecordPayment(request); + + // Assert + var notFoundResult = result.Result.Should().BeOfType().Subject; + notFoundResult.Value.Should().BeEquivalentTo(new { error = "Registration not found" }); + } + + [Fact] + public async Task RecordPayment_NonOrganizer_ReturnsForbidden() + { + // Arrange + var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); + var participant = TestDataFactory.CreateUser(email: "participant4@example.com", role: UserRole.Participant); + var otherOrganizer = TestDataFactory.CreateUser(email: "other@example.com", role: UserRole.Organizer); + _context.Users.AddRange(organizer, participant, otherOrganizer); + + 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(otherOrganizer.Id, "Organizer"); + + var request = new CreatePaymentRequest + { + RegistrationId = registration.Id, + Amount = 50.00m, + Method = PaymentMethod.Cash + }; + + // Act + var result = await _controller.RecordPayment(request); + + // Assert + result.Result.Should().BeOfType(); + } + + #endregion + + #region Get Payment Status - Positive Tests + + [Fact] + public async Task GetPaymentStatus_UnpaidRegistration_ReturnsUnpaid() + { + // 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.GetPaymentStatus(registration.Id); + + // Assert + var okResult = result.Result.Should().BeOfType().Subject; + var response = okResult.Value; + response.Should().NotBeNull(); + } + + [Fact] + public async Task GetPaymentStatus_PaidRegistration_ReturnsPaid() + { + // 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(); + + // Record a payment + var payment = TestDataFactory.CreatePayment(registration.Id, 50.00m, PaymentMethod.Cash); + _context.Payments.Add(payment); + await _context.SaveChangesAsync(); + + SetUserContext(participant.Id, "Participant"); + + // Act + var result = await _controller.GetPaymentStatus(registration.Id); + + // Assert + var okResult = result.Result.Should().BeOfType().Subject; + var response = okResult.Value; + response.Should().NotBeNull(); + } + + #endregion + + #region Get Payment Status - Negative Tests + + [Fact] + public async Task GetPaymentStatus_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.GetPaymentStatus(Guid.NewGuid()); + + // Assert + result.Result.Should().BeOfType(); + } + + #endregion + + #region Get Payment Report - Positive Tests + + [Fact] + public async Task GetPaymentReport_ReturnsEventPaymentSummary() + { + // Arrange + var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); + 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(organizer, participant1, participant2); + + var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); + _context.Events.Add(eventEntity); + + var registration1 = TestDataFactory.CreateRegistration(eventEntity.Id, participant1.Id, RegistrationStatus.Confirmed); + var registration2 = TestDataFactory.CreateRegistration(eventEntity.Id, participant2.Id, RegistrationStatus.Confirmed); + _context.Registrations.AddRange(registration1, registration2); + await _context.SaveChangesAsync(); + + // Add payment for first registration + var payment = TestDataFactory.CreatePayment(registration1.Id, 50.00m, PaymentMethod.Cash); + _context.Payments.Add(payment); + await _context.SaveChangesAsync(); + + SetUserContext(organizer.Id, "Organizer"); + + // Act + var result = await _controller.GetPaymentReport(eventEntity.Id); + + // Assert + var okResult = result.Result.Should().BeOfType().Subject; + var response = okResult.Value.Should().BeOfType().Subject; + response.TotalCollected.Should().Be(50.00m); + response.TotalRegistrations.Should().Be(2); + response.PaidRegistrations.Should().Be(1); + response.UnpaidRegistrations.Should().Be(1); + } + + #endregion + + #region Get Payment Report - Negative Tests + + [Fact] + public async Task GetPaymentReport_NonOrganizer_ReturnsForbidden() + { + // Arrange + var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); + var otherOrganizer = TestDataFactory.CreateUser(email: "other@example.com", role: UserRole.Organizer); + _context.Users.AddRange(organizer, otherOrganizer); + + var eventEntity = TestDataFactory.CreateEvent(organizerId: organizer.Id, status: EventStatus.Published); + _context.Events.Add(eventEntity); + await _context.SaveChangesAsync(); + + SetUserContext(otherOrganizer.Id, "Organizer"); + + // Act + var result = await _controller.GetPaymentReport(eventEntity.Id); + + // Assert + result.Result.Should().BeOfType(); + } + + [Fact] + public async Task GetPaymentReport_NonExistentEvent_ReturnsNotFound() + { + // Arrange + var organizer = TestDataFactory.CreateUser(role: UserRole.Organizer); + _context.Users.Add(organizer); + await _context.SaveChangesAsync(); + + SetUserContext(organizer.Id, "Organizer"); + + // Act + var result = await _controller.GetPaymentReport(Guid.NewGuid()); + + // Assert + result.Result.Should().BeOfType(); + } + + #endregion +} diff --git a/backend/backend.Tests/Controllers/RegistrationsControllerTests.cs b/backend/backend.Tests/Controllers/RegistrationsControllerTests.cs new file mode 100644 index 0000000..4063183 --- /dev/null +++ b/backend/backend.Tests/Controllers/RegistrationsControllerTests.cs @@ -0,0 +1,477 @@ +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 +}