Refactor nunit tests off InMemory/WebApplicationFactory, remove tuple returns (#4)
All checks were successful
CI / build-and-push (push) Successful in 48s
CI / deploy-qa (push) Successful in 12s
CI / smoke-qa (push) Successful in 20s

## Summary
- Extract `IMicCheckDbContext` and mock DB access with Moq instead of the EF InMemory provider, per CLAUDE.md testing guidelines.
- Rewrite HTTP-pipeline tests to exercise controllers/auth handlers directly instead of `WebApplicationFactory`.
- Remove tuple return types across the API in favor of named records (`PagedResult<T>`, `ProfileUpdateResult`, `ApiKeyCreationResult`).

## Test plan
- [x] `dotnet build` (full solution)
- [x] `dotnet test tests/api/MicCheck.Api.Tests.Unit` — 187/187 pass
- [x] `dotnet test tests/api/MicCheck.Api.Tests.Integration` — 8/8 pass (live API + Postgres via Aspire)
- [x] Verified Aspire AppHost/dashboard starts and API responds

Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
2026-07-04 21:40:47 -07:00
parent 1556b486d2
commit 20188c61a2
62 changed files with 1406 additions and 2458 deletions

View File

@@ -1,185 +0,0 @@
using System.Net;
using System.Net.Http.Json;
using MicCheck.Api.Common.Security.Authorization;
using NUnit.Framework;
namespace MicCheck.Api.Tests.Unit.Common.Security.Authorization;
[TestFixture]
public class AuthEndpointsTests
{
private MicCheckWebApplicationFactory _factory = null!;
[SetUp]
public void SetUp()
{
_factory = new MicCheckWebApplicationFactory();
}
[TearDown]
public void TearDown() => _factory.Dispose();
[Test]
public async Task WhenRegisteringWithValidDetails_ThenOkIsReturnedWithTokens()
{
var client = _factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"alice@example.com", "SecurePass1!", "Alice", "Smith", "Acme Corp"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var body = await response.Content.ReadFromJsonAsync<LoginResponse>();
Assert.That(body?.AccessToken, Is.Not.Null.And.Not.Empty);
Assert.That(body?.RefreshToken, Is.Not.Null.And.Not.Empty);
}
[Test]
public async Task WhenRegisteringWithDuplicateEmail_ThenConflictIsReturned()
{
var client = _factory.CreateClient();
await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"alice@example.com", "SecurePass1!", "Alice", "Smith", "Acme Corp"));
var response = await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"alice@example.com", "AnotherPass1!", "Alice", "Jones", "Other Corp"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Conflict));
}
[Test]
public async Task WhenLoginWithValidCredentials_ThenOkIsReturnedWithTokens()
{
var client = _factory.CreateClient();
await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"bob@example.com", "SecurePass1!", "Bob", "Jones", "BobCo"));
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("bob@example.com", "SecurePass1!"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var body = await response.Content.ReadFromJsonAsync<LoginResponse>();
Assert.That(body?.AccessToken, Is.Not.Null.And.Not.Empty);
Assert.That(body?.RefreshToken, Is.Not.Null.And.Not.Empty);
}
[Test]
public async Task WhenLoginWithWrongPassword_ThenUnauthorizedIsReturned()
{
var client = _factory.CreateClient();
await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"carol@example.com", "CorrectPass1!", "Carol", "White", "CarolCo"));
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("carol@example.com", "WrongPassword"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
[Test]
public async Task WhenLoginWithUnknownEmail_ThenUnauthorizedIsReturned()
{
var client = _factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("nobody@example.com", "SomePass1!"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
[Test]
public async Task WhenLoginWithEmptyEmail_ThenBadRequestIsReturned()
{
var client = _factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/login",
new LoginRequest("", "SomePass1!"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
}
[Test]
public async Task WhenRefreshingWithValidToken_ThenOkIsReturnedWithNewTokens()
{
var client = _factory.CreateClient();
var registerResponse = await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"dave@example.com", "SecurePass1!", "Dave", "Brown", "DaveCo"));
var registerBody = await registerResponse.Content.ReadFromJsonAsync<LoginResponse>();
var response = await client.PostAsJsonAsync("/api/v1/auth/refresh",
new RefreshRequest(registerBody!.RefreshToken));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.OK));
var body = await response.Content.ReadFromJsonAsync<LoginResponse>();
Assert.That(body?.AccessToken, Is.Not.Null.And.Not.Empty);
Assert.That(body?.RefreshToken, Is.Not.EqualTo(registerBody.RefreshToken));
}
[Test]
public async Task WhenRefreshingWithInvalidToken_ThenUnauthorizedIsReturned()
{
var client = _factory.CreateClient();
var response = await client.PostAsJsonAsync("/api/v1/auth/refresh",
new RefreshRequest("not-a-valid-refresh-token"));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
[Test]
public async Task WhenRefreshingWithRevokedToken_ThenUnauthorizedIsReturned()
{
var client = _factory.CreateClient();
var registerResponse = await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"eve@example.com", "SecurePass1!", "Eve", "Davis", "EveCo"));
var registerBody = await registerResponse.Content.ReadFromJsonAsync<LoginResponse>();
await client.PostAsJsonAsync("/api/v1/auth/refresh",
new RefreshRequest(registerBody!.RefreshToken));
var response = await client.PostAsJsonAsync("/api/v1/auth/refresh",
new RefreshRequest(registerBody.RefreshToken));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
[Test]
public async Task WhenLoggingOut_ThenNoContentIsReturned()
{
var client = _factory.CreateClient();
var registerResponse = await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"frank@example.com", "SecurePass1!", "Frank", "Green", "FrankCo"));
var registerBody = await registerResponse.Content.ReadFromJsonAsync<LoginResponse>();
var response = await client.PostAsJsonAsync("/api/v1/auth/logout",
new LogoutRequest(registerBody!.RefreshToken));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.NoContent));
}
[Test]
public async Task WhenLoggingOutAndRefreshing_ThenUnauthorizedIsReturned()
{
var client = _factory.CreateClient();
var registerResponse = await client.PostAsJsonAsync("/api/v1/auth/register", new RegisterRequest(
"grace@example.com", "SecurePass1!", "Grace", "Black", "GraceCo"));
var registerBody = await registerResponse.Content.ReadFromJsonAsync<LoginResponse>();
await client.PostAsJsonAsync("/api/v1/auth/logout",
new LogoutRequest(registerBody!.RefreshToken));
var response = await client.PostAsJsonAsync("/api/v1/auth/refresh",
new RefreshRequest(registerBody.RefreshToken));
Assert.That(response.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}
}

View File

@@ -0,0 +1,219 @@
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);
}
}

View File

@@ -1,180 +1,173 @@
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using MicCheck.Api.Common.Security.Authorization;
using MicCheck.Api.Data;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Moq;
using NUnit.Framework;
namespace MicCheck.Api.Tests.Unit.Common.Security.Authorization;
[TestFixture]
public class ProjectPermissionRequirementHandlerTests
{
private MicCheckDbContext _db = null!;
private Mock<IHttpContextAccessor> _httpContextAccessor = null!;
private ProjectPermissionRequirementHandler _handler = null!;
[SetUp]
public void SetUp()
{
var options = new DbContextOptionsBuilder<MicCheckDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString())
.Options;
_db = new MicCheckDbContext(options);
_httpContextAccessor = new Mock<IHttpContextAccessor>();
_handler = new ProjectPermissionRequirementHandler(_db, _httpContextAccessor.Object);
}
[TearDown]
public void TearDown() => _db.Dispose();
private static AuthorizationHandlerContext CreateContext(
ClaimsPrincipal user,
ProjectPermission permission = ProjectPermission.ViewProject)
{
var requirement = new ProjectPermissionRequirement(permission);
return new AuthorizationHandlerContext([requirement], user, null);
}
private static ClaimsPrincipal CreateUserWithClaims(params Claim[] claims)
{
return new ClaimsPrincipal(new ClaimsIdentity(claims, "Bearer"));
}
private void SetupRouteProjectId(int projectId)
{
var httpContext = new DefaultHttpContext();
httpContext.Request.RouteValues["projectId"] = projectId.ToString();
_httpContextAccessor.Setup(a => a.HttpContext).Returns(httpContext);
}
[Test]
public async Task WhenUserIsOrganizationAdmin_ThenRequirementSucceeds()
{
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "1"),
new Claim("OrganizationRole", "Admin"));
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.True);
}
[Test]
public async Task WhenUserHasRequiredPermission_ThenRequirementSucceeds()
{
_db.UserProjectPermissions.Add(new UserProjectPermission
{
UserId = 1,
ProjectId = 10,
Permissions = [ProjectPermission.ViewProject]
});
await _db.SaveChangesAsync();
SetupRouteProjectId(10);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "1"));
var context = CreateContext(user, ProjectPermission.ViewProject);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.True);
}
[Test]
public async Task WhenUserIsProjectAdmin_ThenAllPermissionsAreGranted()
{
_db.UserProjectPermissions.Add(new UserProjectPermission
{
UserId = 2,
ProjectId = 20,
IsAdmin = true,
Permissions = []
});
await _db.SaveChangesAsync();
SetupRouteProjectId(20);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "2"));
var context = CreateContext(user, ProjectPermission.DeleteFeature);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.True);
}
[Test]
public async Task WhenUserDoesNotHaveRequiredPermission_ThenRequirementFails()
{
_db.UserProjectPermissions.Add(new UserProjectPermission
{
UserId = 3,
ProjectId = 30,
Permissions = [ProjectPermission.ViewProject]
});
await _db.SaveChangesAsync();
SetupRouteProjectId(30);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "3"));
var context = CreateContext(user, ProjectPermission.DeleteFeature);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
[Test]
public async Task WhenUserHasNoProjectPermissionRecord_ThenRequirementFails()
{
SetupRouteProjectId(99);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "5"));
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
[Test]
public async Task WhenUserIdClaimIsMissing_ThenRequirementFails()
{
var user = CreateUserWithClaims();
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
[Test]
public async Task WhenProjectIdIsNotInRoute_ThenRequirementFails()
{
_httpContextAccessor.Setup(a => a.HttpContext).Returns(new DefaultHttpContext());
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "1"));
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
}
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using MicCheck.Api.Common.Security.Authorization;
using MicCheck.Api.Data;
using MicCheck.Api.Tests.Unit.TestSupport;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Moq;
using NUnit.Framework;
namespace MicCheck.Api.Tests.Unit.Common.Security.Authorization;
[TestFixture]
public class ProjectPermissionRequirementHandlerTests
{
private List<UserProjectPermission> _userProjectPermissions = null!;
private Mock<IHttpContextAccessor> _httpContextAccessor = null!;
private ProjectPermissionRequirementHandler _handler = null!;
[SetUp]
public void SetUp()
{
var db = new Mock<IMicCheckDbContext>();
_userProjectPermissions = [];
db.SetupDbSet(c => c.UserProjectPermissions, _userProjectPermissions);
_httpContextAccessor = new Mock<IHttpContextAccessor>();
_handler = new ProjectPermissionRequirementHandler(db.Object, _httpContextAccessor.Object);
}
private static AuthorizationHandlerContext CreateContext(
ClaimsPrincipal user,
ProjectPermission permission = ProjectPermission.ViewProject)
{
var requirement = new ProjectPermissionRequirement(permission);
return new AuthorizationHandlerContext([requirement], user, null);
}
private static ClaimsPrincipal CreateUserWithClaims(params Claim[] claims)
{
return new ClaimsPrincipal(new ClaimsIdentity(claims, "Bearer"));
}
private void SetupRouteProjectId(int projectId)
{
var httpContext = new DefaultHttpContext();
httpContext.Request.RouteValues["projectId"] = projectId.ToString();
_httpContextAccessor.Setup(a => a.HttpContext).Returns(httpContext);
}
[Test]
public async Task WhenUserIsOrganizationAdmin_ThenRequirementSucceeds()
{
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "1"),
new Claim("OrganizationRole", "Admin"));
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.True);
}
[Test]
public async Task WhenUserHasRequiredPermission_ThenRequirementSucceeds()
{
_userProjectPermissions.Add(new UserProjectPermission
{
UserId = 1,
ProjectId = 10,
Permissions = [ProjectPermission.ViewProject]
});
SetupRouteProjectId(10);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "1"));
var context = CreateContext(user, ProjectPermission.ViewProject);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.True);
}
[Test]
public async Task WhenUserIsProjectAdmin_ThenAllPermissionsAreGranted()
{
_userProjectPermissions.Add(new UserProjectPermission
{
UserId = 2,
ProjectId = 20,
IsAdmin = true,
Permissions = []
});
SetupRouteProjectId(20);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "2"));
var context = CreateContext(user, ProjectPermission.DeleteFeature);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.True);
}
[Test]
public async Task WhenUserDoesNotHaveRequiredPermission_ThenRequirementFails()
{
_userProjectPermissions.Add(new UserProjectPermission
{
UserId = 3,
ProjectId = 30,
Permissions = [ProjectPermission.ViewProject]
});
SetupRouteProjectId(30);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "3"));
var context = CreateContext(user, ProjectPermission.DeleteFeature);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
[Test]
public async Task WhenUserHasNoProjectPermissionRecord_ThenRequirementFails()
{
SetupRouteProjectId(99);
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "5"));
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
[Test]
public async Task WhenUserIdClaimIsMissing_ThenRequirementFails()
{
var user = CreateUserWithClaims();
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
[Test]
public async Task WhenProjectIdIsNotInRoute_ThenRequirementFails()
{
_httpContextAccessor.Setup(a => a.HttpContext).Returns(new DefaultHttpContext());
var user = CreateUserWithClaims(
new Claim(JwtRegisteredClaimNames.Sub, "1"));
var context = CreateContext(user);
await _handler.HandleAsync(context);
Assert.That(context.HasSucceeded, Is.False);
}
}