183 lines
7.5 KiB
C#
183 lines
7.5 KiB
C#
using Microsoft.AspNetCore.Identity;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Moq;
|
|
using ROLAC.API.Data;
|
|
using ROLAC.API.DTOs.Users;
|
|
using ROLAC.API.Entities;
|
|
using ROLAC.API.Services;
|
|
using Xunit;
|
|
|
|
namespace ROLAC.API.Tests.Services;
|
|
|
|
public class UserManagementServiceTests
|
|
{
|
|
private static AppDbContext BuildDb() =>
|
|
new(new DbContextOptionsBuilder<AppDbContext>()
|
|
.UseInMemoryDatabase(Guid.NewGuid().ToString())
|
|
.Options);
|
|
|
|
private static Mock<UserManager<AppUser>> BuildUserManager(
|
|
AppUser? findResult = null,
|
|
bool createOk = true,
|
|
IList<string>? roles = null)
|
|
{
|
|
var store = new Mock<IUserStore<AppUser>>();
|
|
#pragma warning disable CS8625
|
|
var mgr = new Mock<UserManager<AppUser>>(
|
|
store.Object, null, null, null, null, null, null, null, null);
|
|
#pragma warning restore CS8625
|
|
mgr.Setup(m => m.FindByIdAsync(It.IsAny<string>()))
|
|
.ReturnsAsync(findResult);
|
|
mgr.Setup(m => m.FindByEmailAsync(It.IsAny<string>()))
|
|
.ReturnsAsync((AppUser?)null);
|
|
mgr.Setup(m => m.CreateAsync(It.IsAny<AppUser>(), It.IsAny<string>()))
|
|
.ReturnsAsync(createOk ? IdentityResult.Success
|
|
: IdentityResult.Failed(new IdentityError { Description = "fail" }));
|
|
mgr.Setup(m => m.AddToRolesAsync(It.IsAny<AppUser>(), It.IsAny<IEnumerable<string>>()))
|
|
.ReturnsAsync(IdentityResult.Success);
|
|
mgr.Setup(m => m.GetRolesAsync(It.IsAny<AppUser>()))
|
|
.ReturnsAsync(roles ?? new List<string> { "member" });
|
|
mgr.Setup(m => m.UpdateAsync(It.IsAny<AppUser>()))
|
|
.ReturnsAsync(IdentityResult.Success);
|
|
mgr.Setup(m => m.RemoveFromRolesAsync(It.IsAny<AppUser>(), It.IsAny<IEnumerable<string>>()))
|
|
.ReturnsAsync(IdentityResult.Success);
|
|
mgr.Setup(m => m.GeneratePasswordResetTokenAsync(It.IsAny<AppUser>()))
|
|
.ReturnsAsync("reset-token");
|
|
mgr.Setup(m => m.ResetPasswordAsync(It.IsAny<AppUser>(), It.IsAny<string>(), It.IsAny<string>()))
|
|
.ReturnsAsync(IdentityResult.Success);
|
|
return mgr;
|
|
}
|
|
|
|
// ── CreateAsync ──────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_ReturnsTempPassword()
|
|
{
|
|
using var db = BuildDb();
|
|
// Seed a Member so MemberId validation passes
|
|
// Note: InMemory DB requires audit fields — we set them directly
|
|
var member = new Member
|
|
{
|
|
FirstName_en = "A", LastName_en = "B",
|
|
CreatedBy = "system", UpdatedBy = "system",
|
|
CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow,
|
|
};
|
|
db.Members.Add(member);
|
|
await db.SaveChangesAsync();
|
|
|
|
var mgr = BuildUserManager();
|
|
// Capture the AppUser passed to CreateAsync
|
|
AppUser? created = null;
|
|
mgr.Setup(m => m.CreateAsync(It.IsAny<AppUser>(), It.IsAny<string>()))
|
|
.Callback<AppUser, string>((u, _) => { created = u; u.Id = Guid.NewGuid().ToString(); })
|
|
.ReturnsAsync(IdentityResult.Success);
|
|
|
|
// Mock Users queryable to return empty (no existing user for this member)
|
|
mgr.Setup(m => m.Users)
|
|
.Returns(new List<AppUser>().AsQueryable());
|
|
|
|
var svc = new UserManagementService(mgr.Object, db, ROLAC.API.Tests.TestSupport.NullAuditLogger.Instance);
|
|
var result = await svc.CreateAsync(new CreateUserRequest
|
|
{
|
|
MemberId = member.Id,
|
|
Email = "test@rolac.org",
|
|
Roles = ["member"],
|
|
});
|
|
|
|
Assert.False(string.IsNullOrEmpty(result.TempPassword));
|
|
Assert.Equal(12, result.TempPassword.Length);
|
|
Assert.NotNull(created);
|
|
Assert.Equal(member.Id, created!.MemberId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_Throws_WhenMemberNotFound()
|
|
{
|
|
using var db = BuildDb();
|
|
var mgr = BuildUserManager();
|
|
mgr.Setup(m => m.Users)
|
|
.Returns(new List<AppUser>().AsQueryable());
|
|
var svc = new UserManagementService(mgr.Object, db, ROLAC.API.Tests.TestSupport.NullAuditLogger.Instance);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
svc.CreateAsync(new CreateUserRequest
|
|
{ MemberId = 9999, Email = "x@y.com", Roles = ["member"] }));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateAsync_Throws_WhenMemberAlreadyHasUser()
|
|
{
|
|
using var db = BuildDb();
|
|
var member = new Member
|
|
{
|
|
FirstName_en = "A", LastName_en = "B",
|
|
CreatedBy = "system", UpdatedBy = "system",
|
|
CreatedAt = DateTimeOffset.UtcNow, UpdatedAt = DateTimeOffset.UtcNow,
|
|
};
|
|
db.Members.Add(member);
|
|
await db.SaveChangesAsync();
|
|
|
|
var existingUser = new AppUser
|
|
{
|
|
Id = Guid.NewGuid().ToString(),
|
|
UserName = "existing@test.com",
|
|
Email = "existing@test.com",
|
|
MemberId = member.Id,
|
|
};
|
|
db.Users.Add(existingUser);
|
|
await db.SaveChangesAsync();
|
|
|
|
var mgr = BuildUserManager();
|
|
// The service checks _userManager.Users — we need to return the existing user
|
|
mgr.Setup(m => m.Users)
|
|
.Returns(new List<AppUser> { existingUser }.AsQueryable());
|
|
var svc = new UserManagementService(mgr.Object, db, ROLAC.API.Tests.TestSupport.NullAuditLogger.Instance);
|
|
|
|
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
|
svc.CreateAsync(new CreateUserRequest
|
|
{ MemberId = member.Id, Email = "new@test.com", Roles = ["member"] }));
|
|
}
|
|
|
|
// ── DeactivateAsync ──────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task DeactivateAsync_SetsIsActiveFalse()
|
|
{
|
|
using var db = BuildDb();
|
|
var user = new AppUser
|
|
{ Id = "u1", UserName = "a@b.com", Email = "a@b.com", IsActive = true };
|
|
var mgr = BuildUserManager(findResult: user);
|
|
var svc = new UserManagementService(mgr.Object, db, ROLAC.API.Tests.TestSupport.NullAuditLogger.Instance);
|
|
|
|
await svc.DeactivateAsync("u1");
|
|
|
|
Assert.False(user.IsActive);
|
|
Assert.Equal(DateTimeOffset.MaxValue, user.LockoutEnd);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeactivateAsync_ThrowsKeyNotFound_WhenUserMissing()
|
|
{
|
|
using var db = BuildDb();
|
|
var mgr = BuildUserManager(findResult: null);
|
|
var svc = new UserManagementService(mgr.Object, db, ROLAC.API.Tests.TestSupport.NullAuditLogger.Instance);
|
|
|
|
await Assert.ThrowsAsync<KeyNotFoundException>(() => svc.DeactivateAsync("missing"));
|
|
}
|
|
|
|
// ── ResetPasswordAsync ───────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task ResetPasswordAsync_ReturnsNewTempPassword()
|
|
{
|
|
using var db = BuildDb();
|
|
var user = new AppUser { Id = "u1", UserName = "a@b.com", Email = "a@b.com" };
|
|
var mgr = BuildUserManager(findResult: user);
|
|
var svc = new UserManagementService(mgr.Object, db, ROLAC.API.Tests.TestSupport.NullAuditLogger.Instance);
|
|
|
|
var pwd = await svc.ResetPasswordAsync("u1");
|
|
|
|
Assert.Equal(12, pwd.Length);
|
|
}
|
|
}
|