What is tested at which level in each module, what CI enforces, and why the shape is a pyramid: most of the confidence comes from fast tests on the domain, a smaller number of tests go through real infrastructure, and a few check the system end to end.
flowchart TB
e2e["End to end: few<br/>local stack demo, deploy smoke test"]
integ["Integration: some<br/>API + real SQL Server (Testcontainers), adapters through real DI and HTTP pipelines"]
handler["Use case: many<br/>application layer with fakes for ports"]
unit["Unit: most<br/>aggregates, value objects, decision tables, KPIs"]
arch["Architecture: always on<br/>layer direction, immutability, feature independence"]
e2e --- integ --- handler --- unit
arch -.- unit
Level
Speed
Runs
Proves
Unit
ms
every PR that touches the module
business rules are correct, including edge cases
Use case
ms
same
orchestration: right ports called, right events raised, right errors
Integration
seconds
same
mapping, SQL, migrations, HTTP contract, serialization against real dependencies
Architecture
ms
same
dependency direction and boundaries hold
Contract fixtures
ms
same
real payloads parse and map: Alertmanager and Azure Monitor webhooks in services/api/tests/IncidentOps.Api.Tests/Fixtures, CloudEvent samples in the Escalation anti-corruption tests, the committed incident sample and response snapshots in Insights
End to end
minutes
after deploy (smoke test), on demand locally (scripts/demo.sh)
value object invariants, aggregate behaviors and the events they raise, state machine, SlaClock state and compliance, escalation cap at level 3, rotation across week boundaries and daylight saving time, alert rules
Use case
tests/IncidentOps.Application.Tests
xUnit, in-memory fakes of the ports wired through the real DI registration
every use case and query, domain event translation, outbox message handling and retries, metrics report
Integration
tests/IncidentOps.Api.Tests
WebApplicationFactory, Testcontainers SQL Server 2022
lifecycle over HTTP, problem details, API key, alert ingestion with real payloads, outbox retries, seeded history, SignalR broadcast, rate limiting, chaos, Prometheus output, CloudEvent format
Architecture
tests/IncidentOps.Architecture.Tests
NetArchTest, reflection
layer dependencies, aggregates and owned entities, immutable value objects and events, no public setters, repositories only for roots, sealed single-method handlers (code structure)
value object invariants; AcknowledgementWatch.Open, Schedule, Evaluate and PagingDecision.Decide as decision tables
Application
each use case against hand-written port fakes, time through FakeTimeProvider, log fields through FakeLogger
AntiCorruption
CloudEvent and SLA check translation, upstream status mapping, rejection of payloads that break the contract or the model
Adapters
typed HttpClients through the real DI and resilience pipeline with a recording handler (routes, X-Api-Key, 404/409 mapping, no retry on POST); Service Bus message shape; Logic App payload; telemetry redaction
Host
settlement (complete, abandon and rethrow, dead-letter) and each trigger end to end from a ServiceBusReceivedMessage
Architecture
dependency rule, upstream DTOs confined to the anti-corruption layer, domain immutability (code structure)
az bicep build, build-params and lint (every rule at error), Logic App JSON, ShellCheck on infra/scripts, Azure DevOps YAML parses, validate and what-if with OIDC
platform.yml
promtool test rules (each alert fires and stays quiet where it should), promtool check config, amtool check-config, docker compose config (default and functions profile), Service Bus emulator JSON, Markdown link check over every .md, ShellCheck, actionlint
Coverage is a floor that stops erosion, not a goal. Generated code, migrations, composition roots and Program.cs are excluded. A test that only executes code without asserting behavior does not count in review.