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.Expense; using ROLAC.API.Services; using Xunit; namespace ROLAC.API.Tests.Services; public class ExpenseCategoryServiceTests { private static AppDbContext BuildDb() { var claims = new[] { new Claim(ClaimTypes.NameIdentifier, "test-user") }; var ctx = new DefaultHttpContext { User = new(new ClaimsIdentity(claims)) }; var mock = new Mock(); mock.Setup(x => x.HttpContext).Returns(ctx); return new AppDbContext(new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .AddInterceptors(new AuditSaveChangesInterceptor(new ROLAC.API.Services.Logging.CurrentUserAccessor(mock.Object))).Options); } [Fact] public async Task GetAll_NestsSubcategories_AndExcludesInactiveByDefault() { using var db = BuildDb(); var svc = new ExpenseCategoryService(db); var gid = await svc.CreateGroupAsync(new CreateExpenseGroupRequest { Name_en = "Equipment" }); var sid = await svc.CreateSubCategoryAsync(new CreateExpenseSubCategoryRequest { GroupId = gid, Name_en = "Purchase" }); await svc.DeactivateSubCategoryAsync(sid); var active = await svc.GetAllAsync(includeInactive: false); var all = await svc.GetAllAsync(includeInactive: true); Assert.Single(active); Assert.Empty(active[0].SubCategories); Assert.Single(all[0].SubCategories); } [Fact] public async Task DeactivateGroup_SetsInactive() { using var db = BuildDb(); var svc = new ExpenseCategoryService(db); var gid = await svc.CreateGroupAsync(new CreateExpenseGroupRequest { Name_en = "Other" }); await svc.DeactivateGroupAsync(gid); Assert.Empty(await svc.GetAllAsync(includeInactive: false)); } [Fact] public async Task UpdateGroup_Throws_WhenMissing() { using var db = BuildDb(); var svc = new ExpenseCategoryService(db); await Assert.ThrowsAsync(() => svc.UpdateGroupAsync(999, new UpdateExpenseGroupRequest { Name_en = "X" })); } [Fact] public async Task CreateAndGet_RoundTrips_Form990LineId() { using var db = BuildDb(); db.Form990ExpenseLines.Add(new ROLAC.API.Entities.Form990ExpenseLine { Id = 7, LineCode = "7", Name_en = "Salaries" }); await db.SaveChangesAsync(); var svc = new ExpenseCategoryService(db); var gid = await svc.CreateGroupAsync(new CreateExpenseGroupRequest { Name_en = "Personnel", Form990LineId = 24 }); var sid = await svc.CreateSubCategoryAsync(new CreateExpenseSubCategoryRequest { GroupId = gid, Name_en = "Salary & Wages", Form990LineId = 7 }); var all = await svc.GetAllAsync(includeInactive: true); var sub = all.Single(g => g.Id == gid).SubCategories.Single(s => s.Id == sid); Assert.Equal(7, sub.Form990LineId); Assert.Equal(24, all.Single(g => g.Id == gid).Form990LineId); } }