using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Moq; using System.Security.Claims; using ROLAC.API.Data; using ROLAC.API.Data.Interceptors; using ROLAC.API.DTOs.Payee; using ROLAC.API.Entities; using ROLAC.API.Services; using ROLAC.API.Services.Logging; using ROLAC.API.Services.Security; using Xunit; namespace ROLAC.API.Tests.Services; public class Payee1099ServiceTests { private static (Payee1099Service svc, AppDbContext db) Build() { var httpContext = new DefaultHttpContext { User = new(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "test-user") })) }; var accessorMock = new Mock(); accessorMock.Setup(x => x.HttpContext).Returns(httpContext); var db = new AppDbContext(new DbContextOptionsBuilder() .UseInMemoryDatabase(Guid.NewGuid().ToString()) .AddInterceptors(new AuditSaveChangesInterceptor(new CurrentUserAccessor(accessorMock.Object))).Options); var tin = new TinProtector(DataProtectionProvider.Create("ROLAC.Tests")); return (new Payee1099Service(db, tin), db); } [Fact] public async Task Create_encrypts_tin_and_stores_last4_only_in_clear() { var (svc, db) = Build(); var id = await svc.CreateAsync(new SavePayee1099Request { LegalName = "Pat Player", TinType = "SSN", Tin = "123-45-6789", W9Status = "OnFile" }); var saved = await db.Payee1099s.FindAsync(id); Assert.NotNull(saved); Assert.Equal("6789", saved!.TinLast4); Assert.NotNull(saved.TinEncrypted); Assert.DoesNotContain("123-45-6789", saved.TinEncrypted!); Assert.Equal("123-45-6789", await svc.RevealTinAsync(id)); } [Fact] public async Task Update_with_null_tin_keeps_existing_ciphertext() { var (svc, db) = Build(); var id = await svc.CreateAsync(new SavePayee1099Request { LegalName = "X", Tin = "11-2223333" }); var before = (await db.Payee1099s.FindAsync(id))!.TinEncrypted; await svc.UpdateAsync(id, new SavePayee1099Request { LegalName = "X renamed", Tin = null }); var after = await db.Payee1099s.FindAsync(id); Assert.Equal("X renamed", after!.LegalName); Assert.Equal(before, after.TinEncrypted); Assert.Equal("3333", after.TinLast4); } [Fact] public async Task List_dto_masks_tin_to_last4() { var (svc, _) = Build(); await svc.CreateAsync(new SavePayee1099Request { LegalName = "Y", Tin = "999-88-7777" }); var list = await svc.GetAllAsync(includeInactive: true); var item = Assert.Single(list); Assert.Equal("7777", item.TinLast4); } [Fact] public async Task Delete_is_soft_and_hides_from_list() { var (svc, _) = Build(); var id = await svc.CreateAsync(new SavePayee1099Request { LegalName = "Z" }); await svc.DeleteAsync(id); Assert.Empty(await svc.GetAllAsync(includeInactive: true)); } }