Files
ROLAC/API/ROLAC.API.Tests/Services/OfferingSessionServiceTests.cs
T
2026-05-28 16:47:19 -07:00

133 lines
4.9 KiB
C#

using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Moq;
using ROLAC.API.Data;
using ROLAC.API.Data.Interceptors;
using ROLAC.API.DTOs.Giving;
using ROLAC.API.Entities;
using ROLAC.API.Services;
using Xunit;
namespace ROLAC.API.Tests.Services;
public class OfferingSessionServiceTests
{
private static IHttpContextAccessor BuildAccessor(string userId = "test-user")
{
var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId) };
var ctx = new DefaultHttpContext { User = new(new ClaimsIdentity(claims)) };
var mock = new Mock<IHttpContextAccessor>();
mock.Setup(x => x.HttpContext).Returns(ctx);
return mock.Object;
}
private static AppDbContext BuildDb(string userId = "test-user")
{
var interceptor = new AuditSaveChangesInterceptor(BuildAccessor(userId));
return new AppDbContext(
new DbContextOptionsBuilder<AppDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.AddInterceptors(interceptor)
.Options);
}
private static async Task<int> SeedCategoryAsync(AppDbContext db)
{
var c = new GivingCategory { Name_en = "Tithe", IsActive = true };
db.GivingCategories.Add(c);
await db.SaveChangesAsync();
return c.Id;
}
private static CreateOfferingSessionRequest BuildRequest(int catId, DateOnly date) => new()
{
SessionDate = date, CashTotal = 150m, CheckTotal = 0m,
Givings =
[
new() { GivingCategoryId = catId, Amount = 100m, PaymentMethod = "Cash" },
new() { GivingCategoryId = catId, Amount = 50m, PaymentMethod = "Cash", IsAnonymous = true },
],
};
[Fact]
public async Task CreateAsync_RecomputesSystemTotalAndDifference_ServerSide()
{
using var db = BuildDb();
var catId = await SeedCategoryAsync(db);
var svc = new OfferingSessionService(db, BuildAccessor());
var id = await svc.CreateAsync(BuildRequest(catId, new DateOnly(2026, 5, 31)));
var saved = await db.OfferingSessions.FindAsync(id);
Assert.Equal("Submitted", saved!.Status);
Assert.Equal(150m, saved.SystemTotal);
Assert.Equal(0m, saved.Difference);
Assert.NotNull(saved.SubmittedAt);
Assert.Equal(2, await db.Givings.CountAsync(g => g.OfferingSessionId == id));
}
[Fact]
public async Task CreateAsync_LinesGetSessionDateAndAnonymousNullsMember()
{
using var db = BuildDb();
var catId = await SeedCategoryAsync(db);
var svc = new OfferingSessionService(db, BuildAccessor());
var id = await svc.CreateAsync(BuildRequest(catId, new DateOnly(2026, 5, 31)));
var lines = await db.Givings.Where(g => g.OfferingSessionId == id).ToListAsync();
Assert.All(lines, l => Assert.Equal(new DateOnly(2026,5,31), l.GivingDate));
Assert.Contains(lines, l => l.IsAnonymous && l.MemberId == null);
}
[Fact]
public async Task CreateAsync_Throws_OnDuplicateSessionDate()
{
using var db = BuildDb();
var catId = await SeedCategoryAsync(db);
var svc = new OfferingSessionService(db, BuildAccessor());
await svc.CreateAsync(BuildRequest(catId, new DateOnly(2026, 5, 31)));
await Assert.ThrowsAsync<InvalidOperationException>(() =>
svc.CreateAsync(BuildRequest(catId, new DateOnly(2026, 5, 31))));
}
[Fact]
public async Task ReplaceAsync_Throws_WhenSessionIsSubmitted()
{
using var db = BuildDb();
var catId = await SeedCategoryAsync(db);
var svc = new OfferingSessionService(db, BuildAccessor());
var id = await svc.CreateAsync(BuildRequest(catId, new DateOnly(2026, 5, 31)));
await Assert.ThrowsAsync<InvalidOperationException>(() =>
svc.ReplaceAsync(id, BuildRequest(catId, new DateOnly(2026, 5, 31))));
}
[Fact]
public async Task ReopenThenReplace_SwapsLinesAndResubmits()
{
using var db = BuildDb();
var catId = await SeedCategoryAsync(db);
var svc = new OfferingSessionService(db, BuildAccessor());
var id = await svc.CreateAsync(BuildRequest(catId, new DateOnly(2026, 5, 31)));
await svc.ReopenAsync(id);
var reopened = await db.OfferingSessions.FindAsync(id);
Assert.Equal("Draft", reopened!.Status);
var newReq = new CreateOfferingSessionRequest
{
SessionDate = new DateOnly(2026,5,31), CashTotal = 200m, CheckTotal = 0m,
Givings = [ new() { GivingCategoryId = catId, Amount = 200m, PaymentMethod = "Cash" } ],
};
await svc.ReplaceAsync(id, newReq);
var after = await db.OfferingSessions.FindAsync(id);
Assert.Equal("Submitted", after!.Status);
Assert.Equal(200m, after.SystemTotal);
Assert.Equal(1, await db.Givings.CountAsync(g => g.OfferingSessionId == id));
}
}