Refactor nunit tests off InMemory/WebApplicationFactory, remove tuple returns (#4)
## 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:
@@ -0,0 +1,43 @@
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore.Query;
|
||||
|
||||
namespace MicCheck.Api.Tests.Unit.TestSupport;
|
||||
|
||||
public class TestAsyncQueryProvider<T>(IQueryProvider innerProvider) : IAsyncQueryProvider
|
||||
{
|
||||
public IQueryable CreateQuery(Expression expression) =>
|
||||
new TestAsyncEnumerable<T>(StripIncludes(expression));
|
||||
|
||||
public IQueryable<TElement> CreateQuery<TElement>(Expression expression) =>
|
||||
new TestAsyncEnumerable<TElement>(StripIncludes(expression));
|
||||
|
||||
public object? Execute(Expression expression) =>
|
||||
innerProvider.Execute(StripIncludes(expression));
|
||||
|
||||
public TResult Execute<TResult>(Expression expression) =>
|
||||
innerProvider.Execute<TResult>(StripIncludes(expression));
|
||||
|
||||
public TResult ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var stripped = StripIncludes(expression);
|
||||
var resultType = typeof(TResult).GetGenericArguments() is [var inner] ? inner : typeof(TResult);
|
||||
var executionResult = typeof(IQueryProvider)
|
||||
.GetMethod(nameof(IQueryProvider.Execute), 1, [typeof(Expression)])!
|
||||
.MakeGenericMethod(resultType)
|
||||
.Invoke(innerProvider, [stripped]);
|
||||
|
||||
return (TResult)typeof(Task).GetMethod(nameof(Task.FromResult))!
|
||||
.MakeGenericMethod(resultType)
|
||||
.Invoke(null, [executionResult])!;
|
||||
}
|
||||
|
||||
private static Expression StripIncludes(Expression expression) => new IncludeStripVisitor().Visit(expression);
|
||||
|
||||
private sealed class IncludeStripVisitor : ExpressionVisitor
|
||||
{
|
||||
protected override Expression VisitMethodCall(MethodCallExpression node) =>
|
||||
node.Method.Name is "Include" or "ThenInclude"
|
||||
? Visit(node.Arguments[0])
|
||||
: base.VisitMethodCall(node);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user