Add Registrations and Payments controller tests

- Create RegistrationsControllerTests with 17 test cases
  - Registration creation, retrieval, cancellation
  - Authorization checks (participant vs organizer)
  - Duplicate registration prevention
  - Event capacity validation

- Create PaymentsControllerTests with 13 test cases
  - Payment recording (cash and online)
  - Payment status tracking
  - Payment reports for organizers
  - Authorization validation

All backend tests passing: 55 passed, 2 skipped
This commit is contained in:
Denis Urs Rudolph
2026-04-06 11:54:07 +02:00
parent 7cf6211d4d
commit d4c078a5c8
2 changed files with 858 additions and 0 deletions
@@ -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<RacePlannerDbContext>()
.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<Claim>
{
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<CreatedAtActionResult>().Subject;
var response = createdResult.Value.Should().BeOfType<PaymentDto>().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<CreatedAtActionResult>().Subject;
var response = createdResult.Value.Should().BeOfType<PaymentDto>().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<BadRequestObjectResult>().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<NotFoundObjectResult>().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<ForbidResult>();
}
#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<OkObjectResult>().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<OkObjectResult>().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<NotFoundResult>();
}
#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<OkObjectResult>().Subject;
var response = okResult.Value.Should().BeOfType<PaymentReportDto>().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<NotFoundResult>();
}
[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<NotFoundResult>();
}
#endregion
}