86d9879a6d
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
81 lines
3.7 KiB
C#
81 lines
3.7 KiB
C#
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.Entities;
|
|
using ROLAC.API.Services;
|
|
using Xunit;
|
|
|
|
namespace ROLAC.API.Tests.Services;
|
|
|
|
public class MonthlyStatementServiceTests
|
|
{
|
|
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<IHttpContextAccessor>();
|
|
mock.Setup(x => x.HttpContext).Returns(ctx);
|
|
return new AppDbContext(new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.AddInterceptors(new AuditSaveChangesInterceptor(mock.Object)).Options);
|
|
}
|
|
|
|
private static MonthlyStatementService Build(AppDbContext db)
|
|
{
|
|
var mock = new Mock<IHttpContextAccessor>();
|
|
var ctx = new DefaultHttpContext { User = new(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "test-user") })) };
|
|
mock.Setup(x => x.HttpContext).Returns(ctx);
|
|
return new MonthlyStatementService(db, mock.Object);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_ComputesGivingAndPaidExpenses_ForMonthOnly()
|
|
{
|
|
using var db = BuildDb();
|
|
db.GivingCategories.Add(new GivingCategory { Id = 1, Name_en = "Tithe" });
|
|
db.Ministries.Add(new Ministry { Id = 1, Name_en = "Admin" });
|
|
db.ExpenseCategoryGroups.Add(new ExpenseCategoryGroup { Id = 1, Name_en = "Other" });
|
|
db.ExpenseSubCategories.Add(new ExpenseSubCategory { Id = 1, GroupId = 1, Name_en = "Misc" });
|
|
db.Givings.Add(new Giving { GivingCategoryId = 1, Amount = 1000m, PaymentMethod = "Cash", GivingDate = new DateOnly(2026, 5, 10) });
|
|
db.Givings.Add(new Giving { GivingCategoryId = 1, Amount = 500m, PaymentMethod = "Cash", GivingDate = new DateOnly(2026, 6, 1) });
|
|
db.Expenses.Add(new Expense { MinistryId = 1, CategoryGroupId = 1, SubCategoryId = 1, Type = "VendorPayment", Status = "Paid", Amount = 300m, Description = "x", ExpenseDate = new DateOnly(2026, 5, 20) });
|
|
db.Expenses.Add(new Expense { MinistryId = 1, CategoryGroupId = 1, SubCategoryId = 1, Type = "StaffReimbursement", Status = "Approved", Amount = 999m, Description = "not paid", ExpenseDate = new DateOnly(2026, 5, 21) });
|
|
await db.SaveChangesAsync();
|
|
var svc = Build(db);
|
|
|
|
var id = await svc.CreateAsync(new CreateMonthlyStatementRequest
|
|
{ Year = 2026, Month = 5, OpeningBalance = 2000m, TotalOtherIncome = 100m, BankStatementBalance = 2800m });
|
|
|
|
var dto = await svc.GetByIdAsync(id);
|
|
Assert.Equal(1000m, dto!.TotalGiving);
|
|
Assert.Equal(300m, dto.TotalExpenses);
|
|
Assert.Equal(2800m, dto.CalculatedClosingBalance);
|
|
Assert.Equal(0m, dto.Difference);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Create_Duplicate_Throws()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = Build(db);
|
|
await svc.CreateAsync(new CreateMonthlyStatementRequest { Year = 2026, Month = 5 });
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
svc.CreateAsync(new CreateMonthlyStatementRequest { Year = 2026, Month = 5 }));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Update_AfterFinalize_Throws()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = Build(db);
|
|
var id = await svc.CreateAsync(new CreateMonthlyStatementRequest { Year = 2026, Month = 5 });
|
|
await svc.FinalizeAsync(id);
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
svc.UpdateAsync(id, new UpdateMonthlyStatementRequest { OpeningBalance = 1m }));
|
|
}
|
|
}
|