142 lines
5.1 KiB
C#
142 lines
5.1 KiB
C#
using System.Security.Claims;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Moq;
|
|
using ROLAC.API.Data;
|
|
using ROLAC.API.Data.Interceptors;
|
|
using ROLAC.API.DTOs.Expense;
|
|
using ROLAC.API.Entities;
|
|
using ROLAC.API.Services;
|
|
using ROLAC.API.Services.Logging;
|
|
using Xunit;
|
|
|
|
namespace ROLAC.API.Tests.Services;
|
|
|
|
public class ExpenseSnapshotServiceTests
|
|
{
|
|
private static AppDbContext BuildDb(string userId)
|
|
{
|
|
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 new AppDbContext(new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.AddInterceptors(new AuditSaveChangesInterceptor(new CurrentUserAccessor(mock.Object))).Options);
|
|
}
|
|
|
|
private static (ExpenseSnapshotService svc, AppDbContext db) Build(string userId = "u1")
|
|
{
|
|
var db = BuildDb(userId);
|
|
db.Ministries.Add(new Ministry { Id = 1, Name_en = "Worship", Name_zh = "敬拜" });
|
|
db.ExpenseCategoryGroups.Add(new ExpenseCategoryGroup { Id = 1, Name_en = "Facilities" });
|
|
db.ExpenseSubCategories.Add(new ExpenseSubCategory { Id = 1, GroupId = 1, Name_en = "Rent" });
|
|
db.SaveChanges();
|
|
return (new ExpenseSnapshotService(db), db);
|
|
}
|
|
|
|
private static CreateExpenseSnapshotRequest Rent() => new()
|
|
{
|
|
Name = "Monthly Rent", MinistryId = 1, Description = "Office rent", VendorName = "Landlord X",
|
|
CheckNumber = "1001",
|
|
Lines = { new ExpenseLineInput { CategoryGroupId = 1, SubCategoryId = 1, Amount = 1200m } },
|
|
};
|
|
|
|
[Fact]
|
|
public async Task Create_PersistsHeaderAndLines_StampsCreator()
|
|
{
|
|
var (svc, db) = Build("creator-1");
|
|
var id = await svc.CreateAsync(Rent());
|
|
|
|
var saved = await db.ExpenseSnapshots.FindAsync(id);
|
|
Assert.Equal("Monthly Rent", saved!.Name);
|
|
Assert.Equal("creator-1", saved.CreatedBy);
|
|
Assert.Equal(1, await db.ExpenseSnapshotLines.CountAsync(l => l.SnapshotId == id));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_WithNoLines_Throws()
|
|
{
|
|
var (svc, _) = Build();
|
|
var r = Rent(); r.Lines.Clear();
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() => svc.CreateAsync(r));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetById_ReturnsLines_TotalsAndCreatorName()
|
|
{
|
|
var (svc, db) = Build("creator-1");
|
|
db.Members.Add(new Member { Id = 5, FirstName_en = "Joy", LastName_en = "Wong" });
|
|
db.Users.Add(new AppUser { Id = "creator-1", MemberId = 5 });
|
|
await db.SaveChangesAsync();
|
|
|
|
var id = await svc.CreateAsync(Rent());
|
|
var dto = await svc.GetByIdAsync(id);
|
|
|
|
Assert.NotNull(dto);
|
|
Assert.Equal(1200m, dto!.TotalAmount);
|
|
Assert.Equal(1, dto.LineCount);
|
|
Assert.Equal("Rent", dto.Lines.Single().SubCategoryName);
|
|
Assert.Equal("Joy Wong", dto.CreatedByName);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetAll_ReturnsNewestFirst()
|
|
{
|
|
var (svc, _) = Build();
|
|
var first = await svc.CreateAsync(Rent());
|
|
var second = await svc.CreateAsync(Rent());
|
|
|
|
var all = await svc.GetAllAsync();
|
|
|
|
Assert.Equal(2, all.Count);
|
|
Assert.Equal(second, all[0].Id);
|
|
Assert.Equal(first, all[1].Id);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Update_RenamesAndReplacesLines()
|
|
{
|
|
var (svc, db) = Build();
|
|
db.ExpenseCategoryGroups.Add(new ExpenseCategoryGroup { Id = 2, Name_en = "Utilities" });
|
|
db.ExpenseSubCategories.Add(new ExpenseSubCategory { Id = 2, GroupId = 2, Name_en = "Internet" });
|
|
await db.SaveChangesAsync();
|
|
|
|
var id = await svc.CreateAsync(Rent());
|
|
await svc.UpdateAsync(id, new UpdateExpenseSnapshotRequest
|
|
{
|
|
Name = "Monthly Internet", MinistryId = 1, Description = "ISP",
|
|
Lines = { new ExpenseLineInput { CategoryGroupId = 2, SubCategoryId = 2, Amount = 80m } },
|
|
});
|
|
|
|
var dto = await svc.GetByIdAsync(id);
|
|
Assert.Equal("Monthly Internet", dto!.Name);
|
|
Assert.Equal(80m, dto.TotalAmount);
|
|
Assert.Equal("Internet", dto.Lines.Single().SubCategoryName);
|
|
Assert.Equal(1, await db.ExpenseSnapshotLines.CountAsync(l => l.SnapshotId == id));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Update_MissingId_Throws()
|
|
{
|
|
var (svc, _) = Build();
|
|
await Assert.ThrowsAsync<KeyNotFoundException>(() => svc.UpdateAsync(999, new UpdateExpenseSnapshotRequest
|
|
{
|
|
Name = "x", MinistryId = 1, Description = "x",
|
|
Lines = { new ExpenseLineInput { CategoryGroupId = 1, SubCategoryId = 1, Amount = 1m } },
|
|
}));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Delete_SoftDeletes_HidesFromQueries()
|
|
{
|
|
var (svc, db) = Build();
|
|
var id = await svc.CreateAsync(Rent());
|
|
|
|
await svc.DeleteAsync(id);
|
|
|
|
Assert.Empty(await svc.GetAllAsync());
|
|
Assert.Null(await db.ExpenseSnapshots.FirstOrDefaultAsync(s => s.Id == id));
|
|
}
|
|
}
|