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.Entities; using Xunit; namespace ROLAC.API.Tests.Services; public class DbSeederForm990Tests { private static AppDbContext BuildDb() { var ctx = new DefaultHttpContext { User = new(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "seed") })) }; 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 SeedExpenseCategories_AddsNewGroups_RenamesDuplicates_AndIsIdempotent() { using var db = BuildDb(); var fnb = new ExpenseCategoryGroup { Name_en = "Food & Beverage", Name_zh = "餐飲", SortOrder = 3 }; db.ExpenseCategoryGroups.Add(fnb); await db.SaveChangesAsync(); db.ExpenseSubCategories.Add(new ExpenseSubCategory { GroupId = fnb.Id, Name_en = "Consumables", Name_zh = "消耗品" }); await db.SaveChangesAsync(); await DbSeeder.SeedExpenseCategoriesAsync(db); await DbSeeder.SeedExpenseCategoriesAsync(db); // idempotent second run var groups = await db.ExpenseCategoryGroups.ToListAsync(); Assert.Contains(groups, g => g.Name_en == "Professional Services"); Assert.Contains(groups, g => g.Name_en == "Information Technology"); Assert.Contains(groups, g => g.Name_en == "Finance & Banking"); var fnbSubs = await db.ExpenseSubCategories.Where(s => s.GroupId == fnb.Id).ToListAsync(); Assert.DoesNotContain(fnbSubs, s => s.Name_en == "Consumables"); Assert.Contains(fnbSubs, s => s.Name_en == "Disposable Tableware"); Assert.Single(groups, g => g.Name_en == "Professional Services"); } }