Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Latest commit

 

History

History
98 lines (65 loc) · 3.52 KB

File metadata and controls

98 lines (65 loc) · 3.52 KB

Testing Strategy — Loopless Backend

Overview

Two test layers: unit tests for business logic in isolation, integration tests for HTTP endpoints against real infrastructure (PostgreSQL + Redis + RabbitMQ via Testcontainers).


Unit Tests (backend/tests/Loopless.UnitTests/)

Target: Application + Infrastructure logic with no external dependencies.

Infrastructure: EF Core InMemory provider. No Docker required.

Run:

dotnet test backend/tests/Loopless.UnitTests/

Coverage targets

Area Tests Notes
Email job Email/UnreadMessageEmailJobTests.cs 4 tests — threshold, idempotency, opt-out, read guard
DLQ behavior Email/DlqBehaviorTests.cs 4 tests — naming, invalid JSON, missing user, send failure
Standup features Standups/ Handler + validator tests
Messaging Messaging/ Handler + validator tests

Constraints:

  • Use TestAppDbContext.CreateInMemory() for all DB access.
  • ExecuteUpdateAsync / ExecuteDeleteAsync not supported by InMemory — use change-tracking pattern instead.
  • All RabbitMQ/email interactions go through IEventPublisher + IEmailSender — inject fakes via constructor.

Integration Tests (backend/tests/Loopless.IntegrationTests/)

Target: Full HTTP request/response cycle against a real PostgreSQL database (pgvector enabled).

Infrastructure: Testcontainers (pgvector/pgvector:pg16). Requires Docker Desktop or Docker Engine running locally and in CI.

Run:

dotnet test backend/tests/Loopless.IntegrationTests/

CI: Docker socket is available in the GitHub Actions ubuntu-latest runner — no extra setup needed.

Factory: LooplessWebAppFactory

WebApplicationFactory<Program> + IAsyncLifetime. Spins up a real Postgres container, runs EF migrations, and replaces Keycloak JWT validation with TestAuthHandler.

Authenticated clients: factory.CreateAuthenticatedClient(keycloakId, role) — sets sub claim to keycloakId, role claim to role.

Test suites

File Scenarios
MatchingEndpointTests.cs auth-required, enterprise user discovers freelancers (real pgvector embeddings), semantic search returns 200
StandupAnalyticsTests.cs auth-required, analytics aggregates seeded standups, empty project returns empty memberCompletions
MessagingFlowTests.cs auth-required, send message persists and appears in GET history, pagination returns paged result

Seeding pattern

Seed data directly via AppDbContext obtained from factory.Services.CreateAsyncScope(). Do not seed through the API unless the test specifically covers the creation endpoint.

await using var scope = factory.Services.CreateAsyncScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
db.Users.Add(user);
await db.SaveChangesAsync();

CI Integration (.github/workflows/ci.yml)

Both test suites run in the quality job:

- name: Run backend unit tests
  run: dotnet test backend/tests/Loopless.UnitTests/ --no-build

- name: Run backend integration tests
  run: dotnet test backend/tests/Loopless.IntegrationTests/ --no-build

The integration test job needs Docker — the ubuntu-latest runner has it available by default.


Coverage Targets (Sprint 4)

Layer Target
Application handlers ≥ 80 % line coverage via unit tests
API endpoints (HTTP) 100 % of endpoint files covered by at least one integration test
Email pipeline 100 % of job + consumer paths (happy + DLQ paths)