207 lines
7.7 KiB
C#
207 lines
7.7 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.Members;
|
|
using ROLAC.API.Entities;
|
|
using ROLAC.API.Services;
|
|
using Xunit;
|
|
|
|
namespace ROLAC.API.Tests.Services;
|
|
|
|
public class MemberServiceTests
|
|
{
|
|
private static IHttpContextAccessor BuildAccessor(string userId = "test-user")
|
|
{
|
|
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 mock.Object;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Builds an InMemory AppDbContext that includes the AuditSaveChangesInterceptor
|
|
/// so that CreatedBy/UpdatedBy are stamped on save (required by InMemory null checks).
|
|
/// </summary>
|
|
private static AppDbContext BuildDb(string userId = "test-user")
|
|
{
|
|
var accessor = BuildAccessor(userId);
|
|
var interceptor = new AuditSaveChangesInterceptor(new ROLAC.API.Services.Logging.CurrentUserAccessor(accessor));
|
|
return new AppDbContext(
|
|
new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.AddInterceptors(interceptor)
|
|
.Options);
|
|
}
|
|
|
|
// ── Create ───────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_ReturnsNewId()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
var request = new CreateMemberRequest { FirstName_en = "Chris", LastName_en = "Chen" };
|
|
|
|
var id = await svc.CreateAsync(request);
|
|
|
|
Assert.True(id > 0);
|
|
var saved = await db.Members.FindAsync(id);
|
|
Assert.NotNull(saved);
|
|
Assert.Equal("Chris", saved.FirstName_en);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_SavesNickName()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
var request = new CreateMemberRequest
|
|
{ FirstName_en = "Yuan", LastName_en = "Chen", NickName = "Chris" };
|
|
|
|
var id = await svc.CreateAsync(request);
|
|
var saved = await db.Members.FindAsync(id);
|
|
|
|
Assert.Equal("Chris", saved!.NickName);
|
|
}
|
|
|
|
// ── GetById ──────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task GetByIdAsync_ReturnsDto_WhenExists()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
var id = await svc.CreateAsync(
|
|
new CreateMemberRequest { FirstName_en = "A", LastName_en = "B" });
|
|
|
|
var dto = await svc.GetByIdAsync(id);
|
|
|
|
Assert.NotNull(dto);
|
|
Assert.Equal(id, dto.Id);
|
|
Assert.Equal("A", dto.FirstName_en);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetByIdAsync_ReturnsNull_WhenNotFound()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
|
|
var dto = await svc.GetByIdAsync(9999);
|
|
|
|
Assert.Null(dto);
|
|
}
|
|
|
|
// ── GetPaged ─────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task GetPagedAsync_FiltersOnSearch()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
await svc.CreateAsync(new CreateMemberRequest { FirstName_en = "Chris", LastName_en = "Chen" });
|
|
await svc.CreateAsync(new CreateMemberRequest { FirstName_en = "Alice", LastName_en = "Wang" });
|
|
|
|
var result = await svc.GetPagedAsync(1, 20, "Chris", null, null);
|
|
|
|
Assert.Equal(1, result.TotalCount);
|
|
Assert.Equal("Chris", result.Items[0].FirstName_en);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetPagedAsync_FiltersOnStatus()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
await svc.CreateAsync(new CreateMemberRequest
|
|
{ FirstName_en = "A", LastName_en = "A", Status = "Member" });
|
|
await svc.CreateAsync(new CreateMemberRequest
|
|
{ FirstName_en = "B", LastName_en = "B", Status = "Visitor" });
|
|
|
|
var result = await svc.GetPagedAsync(1, 20, null, "Visitor", null);
|
|
|
|
Assert.Equal(1, result.TotalCount);
|
|
Assert.Equal("Visitor", result.Items[0].Status);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetPagedAsync_SearchesNickName()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
await svc.CreateAsync(new CreateMemberRequest
|
|
{ FirstName_en = "Yuan", LastName_en = "Chen", NickName = "Chris" });
|
|
|
|
var result = await svc.GetPagedAsync(1, 20, "Chris", null, null);
|
|
|
|
Assert.Equal(1, result.TotalCount);
|
|
}
|
|
|
|
// ── Update ───────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_PersistsChanges()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
var id = await svc.CreateAsync(
|
|
new CreateMemberRequest { FirstName_en = "Old", LastName_en = "Name" });
|
|
|
|
await svc.UpdateAsync(id, new UpdateMemberRequest
|
|
{ FirstName_en = "New", LastName_en = "Name", Country = "USA",
|
|
Status = "Member", LanguagePreference = "en" });
|
|
|
|
var saved = await db.Members.FindAsync(id);
|
|
Assert.Equal("New", saved!.FirstName_en);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateAsync_ThrowsKeyNotFound_WhenMissing()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
|
|
await Assert.ThrowsAsync<KeyNotFoundException>(() =>
|
|
svc.UpdateAsync(9999, new UpdateMemberRequest
|
|
{ FirstName_en = "X", LastName_en = "Y", Country = "USA",
|
|
Status = "Member", LanguagePreference = "en" }));
|
|
}
|
|
|
|
// ── Delete (soft) ────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_SoftDeletesMember()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor("deleter-id"));
|
|
var id = await svc.CreateAsync(
|
|
new CreateMemberRequest { FirstName_en = "A", LastName_en = "B" });
|
|
|
|
await svc.DeleteAsync(id);
|
|
|
|
// Query-filtered view returns null
|
|
var filtered = await db.Members.FindAsync(id);
|
|
Assert.Null(filtered);
|
|
|
|
// Raw view shows IsDeleted = true
|
|
var raw = await db.Members.IgnoreQueryFilters()
|
|
.FirstAsync(m => m.Id == id);
|
|
Assert.True(raw.IsDeleted);
|
|
Assert.Equal("deleter-id", raw.DeletedBy);
|
|
Assert.NotNull(raw.DeletedAt);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteAsync_ThrowsKeyNotFound_WhenMissing()
|
|
{
|
|
using var db = BuildDb();
|
|
var svc = new MemberService(db, BuildAccessor());
|
|
|
|
await Assert.ThrowsAsync<KeyNotFoundException>(() => svc.DeleteAsync(9999));
|
|
}
|
|
}
|