a573179714
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
162 lines
5.7 KiB
C#
162 lines
5.7 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 GivingServiceTests
|
|
{
|
|
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;
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_PersistsGiving()
|
|
{
|
|
using var db = BuildDb();
|
|
var catId = await SeedCategoryAsync(db);
|
|
var svc = new GivingService(db);
|
|
|
|
var id = await svc.CreateAsync(new CreateGivingRequest
|
|
{
|
|
GivingCategoryId = catId, Amount = 100m, PaymentMethod = "Cash",
|
|
GivingDate = new DateOnly(2026, 5, 31), IsAnonymous = true,
|
|
});
|
|
|
|
var saved = await db.Givings.FindAsync(id);
|
|
Assert.NotNull(saved);
|
|
Assert.Equal(100m, saved!.Amount);
|
|
Assert.Null(saved.OfferingSessionId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetPagedAsync_FiltersByCategory()
|
|
{
|
|
using var db = BuildDb();
|
|
var catId = await SeedCategoryAsync(db);
|
|
var svc = new GivingService(db);
|
|
await svc.CreateAsync(new CreateGivingRequest { GivingCategoryId = catId, Amount = 10m, PaymentMethod = "Cash", GivingDate = new DateOnly(2026,5,31) });
|
|
|
|
var page = await svc.GetPagedAsync(1, 20, null, catId, null, null);
|
|
|
|
Assert.Equal(1, page.TotalCount);
|
|
Assert.Equal("Tithe", page.Items[0].CategoryName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_Throws_WhenGivingBelongsToSubmittedSession()
|
|
{
|
|
using var db = BuildDb();
|
|
var catId = await SeedCategoryAsync(db);
|
|
var session = new OfferingSession { SessionDate = new DateOnly(2026,5,31), Status = "Submitted" };
|
|
db.OfferingSessions.Add(session);
|
|
await db.SaveChangesAsync();
|
|
var giving = new Giving { GivingCategoryId = catId, Amount = 50m, PaymentMethod = "Cash",
|
|
GivingDate = new DateOnly(2026,5,31), OfferingSessionId = session.Id };
|
|
db.Givings.Add(giving);
|
|
await db.SaveChangesAsync();
|
|
|
|
var svc = new GivingService(db);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
svc.UpdateAsync(giving.Id, new UpdateGivingRequest
|
|
{ GivingCategoryId = catId, Amount = 999m, PaymentMethod = "Cash", GivingDate = new DateOnly(2026,5,31) }));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_Throws_WhenMissing()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new GivingService(db);
|
|
await Assert.ThrowsAsync<KeyNotFoundException>(() => svc.DeleteAsync(999));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_Anonymous_NullsProvidedMemberId()
|
|
{
|
|
using var db = BuildDb();
|
|
var catId = await SeedCategoryAsync(db);
|
|
var svc = new GivingService(db);
|
|
|
|
var id = await svc.CreateAsync(new CreateGivingRequest
|
|
{
|
|
GivingCategoryId = catId, Amount = 25m, PaymentMethod = "Cash",
|
|
GivingDate = new DateOnly(2026, 5, 31),
|
|
IsAnonymous = true, MemberId = 12345, // provided, but must be stripped
|
|
});
|
|
|
|
var saved = await db.Givings.FindAsync(id);
|
|
Assert.True(saved!.IsAnonymous);
|
|
Assert.Null(saved.MemberId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_Throws_WhenGivingBelongsToSubmittedSession()
|
|
{
|
|
using var db = BuildDb();
|
|
var catId = await SeedCategoryAsync(db);
|
|
var session = new OfferingSession { SessionDate = new DateOnly(2026, 5, 31), Status = "Submitted" };
|
|
db.OfferingSessions.Add(session);
|
|
await db.SaveChangesAsync();
|
|
var giving = new Giving { GivingCategoryId = catId, Amount = 50m, PaymentMethod = "Cash",
|
|
GivingDate = new DateOnly(2026, 5, 31), OfferingSessionId = session.Id };
|
|
db.Givings.Add(giving);
|
|
await db.SaveChangesAsync();
|
|
|
|
var svc = new GivingService(db);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() => svc.DeleteAsync(giving.Id));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetPagedAsync_MatchesByMemberName()
|
|
{
|
|
using var db = BuildDb();
|
|
var catId = await SeedCategoryAsync(db);
|
|
var member = new Member { FirstName_en = "Grace", LastName_en = "Lee" };
|
|
db.Members.Add(member);
|
|
await db.SaveChangesAsync();
|
|
var svc = new GivingService(db);
|
|
await svc.CreateAsync(new CreateGivingRequest
|
|
{
|
|
GivingCategoryId = catId, Amount = 75m, PaymentMethod = "Cash",
|
|
GivingDate = new DateOnly(2026, 5, 31), MemberId = member.Id,
|
|
});
|
|
|
|
var page = await svc.GetPagedAsync(1, 20, "grace", null, null, null);
|
|
|
|
Assert.Equal(1, page.TotalCount);
|
|
Assert.Equal(member.Id, page.Items[0].MemberId);
|
|
}
|
|
}
|