Files
mic-check/tests/api/MicCheck.Api.Tests.Unit/Common/Security/Authorization/AuthServiceTests.cs
James Wampler b4a666ebf0 Refactor nunit tests off EF InMemory provider and web server
Extract IMicCheckDbContext so DB access can be mocked with Moq instead
of the InMemory provider, per project testing guidelines. Add shared
async DbSet test doubles (TestSupport/) to back the mocks. Rewrite
HTTP-pipeline tests (WebApplicationFactory) to exercise controllers
and auth handlers directly instead of running the web server. Drop
DbContext/seeder tests that only made sense against a real provider,
and remove stale duplicate test files left over from a namespace move.
2026-07-04 19:54:22 -07:00

220 lines
7.1 KiB
C#

using MicCheck.Api.Common.Security.Authorization;
using MicCheck.Api.Data;
using MicCheck.Api.Organizations;
using MicCheck.Api.Tests.Unit.TestSupport;
using MicCheck.Api.Users;
using Microsoft.AspNetCore.Identity;
using Moq;
using NUnit.Framework;
namespace MicCheck.Api.Tests.Unit.Common.Security.Authorization;
[TestFixture]
public class AuthServiceTests
{
private Mock<IMicCheckDbContext> _db = null!;
private List<User> _users = null!;
private List<Organization> _organizations = null!;
private List<OrganizationUser> _organizationUsers = null!;
private List<RefreshToken> _refreshTokens = null!;
private PasswordHasher<User> _passwordHasher = null!;
private AuthService _service = null!;
[SetUp]
public void SetUp()
{
_db = new Mock<IMicCheckDbContext>();
_users = [];
_db.SetupDbSetWithGeneratedIds(c => c.Users, _users);
_organizations = [];
_db.SetupDbSetWithGeneratedIds(c => c.Organizations, _organizations);
_organizationUsers = [];
_db.SetupDbSet(c => c.OrganizationUsers, _organizationUsers);
_refreshTokens = [];
_db.SetupDbSetWithGeneratedIds(c => c.RefreshTokens, _refreshTokens);
_passwordHasher = new PasswordHasher<User>();
var tokenService = new Mock<ITokenService>();
tokenService.Setup(t => t.GenerateToken(It.IsAny<User>()))
.Returns(new TokenResponse("access-token", DateTime.UtcNow.AddHours(1)));
_service = new AuthService(_db.Object, tokenService.Object, _passwordHasher);
}
private User AddUser(string email, string password, bool isActive = true)
{
var user = new User
{
Email = email,
FirstName = "First",
LastName = "Last",
IsActive = isActive,
CreatedAt = DateTimeOffset.UtcNow,
PasswordHash = string.Empty
};
user.PasswordHash = _passwordHasher.HashPassword(user, password);
_db.Object.Users.Add(user);
return user;
}
[Test]
public async Task WhenRegisteringWithValidDetails_ThenTokensAreReturned()
{
var response = await _service.RegisterAsync(
"alice@example.com", "SecurePass1!", "Alice", "Smith", "Acme Corp");
Assert.That(response, Is.Not.Null);
Assert.That(response!.AccessToken, Is.Not.Null.And.Not.Empty);
Assert.That(response.RefreshToken, Is.Not.Null.And.Not.Empty);
Assert.That(_users, Has.Count.EqualTo(1));
Assert.That(_organizations, Has.Count.EqualTo(1));
Assert.That(_organizationUsers.Single().Role, Is.EqualTo(OrganizationRole.Admin));
}
[Test]
public async Task WhenRegisteringWithDuplicateEmail_ThenNullIsReturned()
{
AddUser("alice@example.com", "SecurePass1!");
var response = await _service.RegisterAsync(
"alice@example.com", "AnotherPass1!", "Alice", "Jones", "Other Corp");
Assert.That(response, Is.Null);
}
[Test]
public async Task WhenLoginWithValidCredentials_ThenTokensAreReturned()
{
AddUser("bob@example.com", "SecurePass1!");
var response = await _service.LoginAsync("bob@example.com", "SecurePass1!");
Assert.That(response, Is.Not.Null);
Assert.That(response!.AccessToken, Is.Not.Null.And.Not.Empty);
Assert.That(response.RefreshToken, Is.Not.Null.And.Not.Empty);
}
[Test]
public async Task WhenLoginWithWrongPassword_ThenNullIsReturned()
{
AddUser("carol@example.com", "CorrectPass1!");
var response = await _service.LoginAsync("carol@example.com", "WrongPassword");
Assert.That(response, Is.Null);
}
[Test]
public async Task WhenLoginWithUnknownEmail_ThenNullIsReturned()
{
var response = await _service.LoginAsync("nobody@example.com", "SomePass1!");
Assert.That(response, Is.Null);
}
[Test]
public async Task WhenRefreshingWithValidToken_ThenNewTokensAreReturnedAndOldTokenIsRevoked()
{
var user = AddUser("dave@example.com", "SecurePass1!");
var refreshToken = new RefreshToken
{
Token = "valid-refresh-token",
UserId = user.Id,
User = user,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(30),
CreatedAt = DateTimeOffset.UtcNow
};
_db.Object.RefreshTokens.Add(refreshToken);
var response = await _service.RefreshAsync("valid-refresh-token");
Assert.That(response, Is.Not.Null);
Assert.That(response!.RefreshToken, Is.Not.EqualTo("valid-refresh-token"));
Assert.That(refreshToken.IsRevoked, Is.True);
}
[Test]
public async Task WhenRefreshingWithInvalidToken_ThenNullIsReturned()
{
var response = await _service.RefreshAsync("not-a-valid-refresh-token");
Assert.That(response, Is.Null);
}
[Test]
public async Task WhenRefreshingWithRevokedToken_ThenNullIsReturned()
{
var user = AddUser("eve@example.com", "SecurePass1!");
_db.Object.RefreshTokens.Add(new RefreshToken
{
Token = "revoked-token",
UserId = user.Id,
User = user,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(30),
IsRevoked = true,
CreatedAt = DateTimeOffset.UtcNow
});
var response = await _service.RefreshAsync("revoked-token");
Assert.That(response, Is.Null);
}
[Test]
public async Task WhenRefreshingWithExpiredToken_ThenNullIsReturned()
{
var user = AddUser("frank@example.com", "SecurePass1!");
_db.Object.RefreshTokens.Add(new RefreshToken
{
Token = "expired-token",
UserId = user.Id,
User = user,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(-1),
CreatedAt = DateTimeOffset.UtcNow.AddDays(-31)
});
var response = await _service.RefreshAsync("expired-token");
Assert.That(response, Is.Null);
}
[Test]
public async Task WhenLoggingOut_ThenTokenIsRevoked()
{
var user = AddUser("grace@example.com", "SecurePass1!");
var refreshToken = new RefreshToken
{
Token = "logout-token",
UserId = user.Id,
User = user,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(30),
CreatedAt = DateTimeOffset.UtcNow
};
_db.Object.RefreshTokens.Add(refreshToken);
await _service.LogoutAsync("logout-token");
Assert.That(refreshToken.IsRevoked, Is.True);
}
[Test]
public async Task WhenLoggingOutAndRefreshing_ThenNullIsReturned()
{
var user = AddUser("henry@example.com", "SecurePass1!");
_db.Object.RefreshTokens.Add(new RefreshToken
{
Token = "logout-then-refresh-token",
UserId = user.Id,
User = user,
ExpiresAt = DateTimeOffset.UtcNow.AddDays(30),
CreatedAt = DateTimeOffset.UtcNow
});
await _service.LogoutAsync("logout-then-refresh-token");
var response = await _service.RefreshAsync("logout-then-refresh-token");
Assert.That(response, Is.Null);
}
}