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,32 +1,33 @@
using MicCheck.Api.Common;
using MicCheck.Api.Data;
using Microsoft.EntityFrameworkCore;
namespace MicCheck.Api.Audit;
public class AuditLogQueryService(MicCheckDbContext db)
public class AuditLogQueryService(IMicCheckDbContext db)
{
public async Task<(int Total, IReadOnlyList<AuditLogResponse> Items)> ListByOrganizationAsync(
public async Task<PagedResult<AuditLogResponse>> ListByOrganizationAsync(
int organizationId, AuditLogFilter filter, CancellationToken ct = default)
{
var query = db.AuditLogs.Where(l => l.OrganizationId == organizationId);
return await ApplyFilterAndPageAsync(query, filter, ct);
}
public async Task<(int Total, IReadOnlyList<AuditLogResponse> Items)> ListByProjectAsync(
public async Task<PagedResult<AuditLogResponse>> ListByProjectAsync(
int projectId, AuditLogFilter filter, CancellationToken ct = default)
{
var query = db.AuditLogs.Where(l => l.ProjectId == projectId);
return await ApplyFilterAndPageAsync(query, filter, ct);
}
public async Task<(int Total, IReadOnlyList<AuditLogResponse> Items)> ListByEnvironmentAsync(
public async Task<PagedResult<AuditLogResponse>> ListByEnvironmentAsync(
int environmentId, AuditLogFilter filter, CancellationToken ct = default)
{
var query = db.AuditLogs.Where(l => l.EnvironmentId == environmentId);
return await ApplyFilterAndPageAsync(query, filter, ct);
}
private async Task<(int Total, IReadOnlyList<AuditLogResponse> Items)> ApplyFilterAndPageAsync(
private async Task<PagedResult<AuditLogResponse>> ApplyFilterAndPageAsync(
IQueryable<AuditLog> query, AuditLogFilter filter, CancellationToken ct)
{
if (filter.From.HasValue)
@@ -63,6 +64,6 @@ public class AuditLogQueryService(MicCheckDbContext db)
user != null ? user.FirstName + " " + user.LastName : null))
.ToListAsync(ct);
return (total, items);
return new PagedResult<AuditLogResponse>(total, items);
}
}