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(); 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() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .AddInterceptors(interceptor) .Options); } private static async Task 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(() => 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(() => 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(() => svc.DeleteAsync(giving.Id)); } }