feat(giving): giving-category service with CRUD + soft-disable

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Chris Chen
2026-05-28 16:29:31 -07:00
parent 8b52572fad
commit 798dfa3fe0
7 changed files with 192 additions and 0 deletions
@@ -0,0 +1,85 @@
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.Services;
using Xunit;
namespace ROLAC.API.Tests.Services;
public class GivingCategoryServiceTests
{
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);
}
[Fact]
public async Task CreateAsync_ReturnsId_AndDefaultsActive()
{
using var db = BuildDb();
var svc = new GivingCategoryService(db, BuildAccessor());
var id = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Tithe", Name_zh = "什一" });
var saved = await db.GivingCategories.FindAsync(id);
Assert.NotNull(saved);
Assert.True(saved!.IsActive);
Assert.Equal("Tithe", saved.Name_en);
}
[Fact]
public async Task GetAllAsync_ExcludesInactive_ByDefault()
{
using var db = BuildDb();
var svc = new GivingCategoryService(db, BuildAccessor());
var id1 = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Active" });
var id2 = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Gone" });
await svc.DeactivateAsync(id2);
var active = await svc.GetAllAsync(includeInactive: false);
var all = await svc.GetAllAsync(includeInactive: true);
Assert.Single(active);
Assert.Equal(2, all.Count);
}
[Fact]
public async Task DeactivateAsync_SetsIsActiveFalse()
{
using var db = BuildDb();
var svc = new GivingCategoryService(db, BuildAccessor());
var id = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Temp" });
await svc.DeactivateAsync(id);
var saved = await db.GivingCategories.FindAsync(id);
Assert.False(saved!.IsActive);
}
[Fact]
public async Task UpdateAsync_Throws_WhenMissing()
{
using var db = BuildDb();
var svc = new GivingCategoryService(db, BuildAccessor());
await Assert.ThrowsAsync<KeyNotFoundException>(() =>
svc.UpdateAsync(999, new UpdateGivingCategoryRequest { Name_en = "X" }));
}
}