Identity Service — Testing Strategy
Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · TESTING_STANDARDS · DEFINITION_OF_DONE
1. Coverage targets
| Test tier | Target | Enforced by |
|---|---|---|
| Unit | ≥ 80% statements + lines | Vitest --coverage in CI |
| Integration | Mandatory scenarios listed below | CI gate |
| Contract (Pact) | All published events; all consumed events | Pact broker green |
| E2E | Critical auth flows | Playwright; staging only |
2. Test pyramid
3. Unit tests
Target: domain layer and application use-cases. Infrastructure adapters are mocked.
| Scenario | File pattern |
|---|---|
| User aggregate invariants (INV-01..INV-12) | domain/user.spec.ts |
| Session rotation one-time-use (INV-03) | domain/session.spec.ts |
| Credential argon2id hash creation / verification | domain/credential.spec.ts |
| MFA factor enrollment / verification | domain/mfa-factor.spec.ts |
| Device state machine (untrusted → trusted → offline_bound) | domain/device.spec.ts |
| LicenseAssignment state machine + invariants (INV-06..INV-10) | domain/license-assignment.spec.ts |
| Effective license resolver (ancestor walk, cache hit, deep-wins rule) | application/resolve-effective-licenses.use-case.spec.ts |
| Register user use-case (email duplicate, outbox emission) | application/register-user.use-case.spec.ts |
| Login use-case (password, TOTP, WebAuthn paths) | application/login.use-case.spec.ts |
| Refresh rotation (replay detection fires security incident) | application/refresh-session.use-case.spec.ts |
4. Integration tests (mandatory)
These must be green before any merge to main.
4.1 Tenant isolation
test/integration/tenant-isolation.spec.ts
| Scenario | Assertion |
|---|---|
| User A in tenant X cannot read users from tenant Y | 403 / RLS blocks row |
| Session created in tenant X is not visible to tenant Y | RLS policy verified |
| License assignments scoped to tenant X not returned for tenant Y | RLS policy verified |
4.2 Outbox reliability
test/integration/outbox.spec.ts
| Scenario | Assertion |
|---|---|
| RegisterUser use-case writes user row and outbox row atomically | DB transaction |
Outbox relay publishes identity.user.registered.v1 to NATS | Event received |
| Failed NATS publish does not discard outbox row | Row remains unpublished |
| Outbox relay deduplicates on republish | Idempotent delivery |
4.3 Inbox deduplication
test/integration/inbox.spec.ts
| Scenario | Assertion |
|---|---|
tenant.suspended.v1 consumed once even with NATS redelivery | Single processing |
tenant.activated.v1 creates shadow user exactly once | No duplicate users |
4.4 Security-specific integration
| Scenario | File |
|---|---|
| Refresh token replay detection triggers session revocation | refresh-replay.integration.spec.ts |
| Suspended user cannot login | user-suspension.integration.spec.ts |
| Concurrent session cap enforced | session-cap.integration.spec.ts |
Device offline binding requires trusted state | device-binding.integration.spec.ts |
| License dependency chain enforced (INV-06) | license-chain.integration.spec.ts |
5. Contract tests (Pact)
5.1 Events produced (schema conformance)
| Event subject | Schema spec |
|---|---|
identity.user.registered.v1 | test/contract/identity.user.registered.schema.spec.ts |
identity.user.suspended.v1 | test/contract/identity.user.suspended.schema.spec.ts |
identity.device.bound_for_offline.v1 | test/contract/identity.device.bound_for_offline.schema.spec.ts |
identity.license.assignment.status_changed.v1 | test/contract/identity.license.assignment.schema.spec.ts |
5.2 API consumer contracts (Pact)
| Consumer | Contract file |
|---|---|
| tenant-service | test/contract/tenant-service.pact.spec.ts |
| communication-service | test/contract/communication-service.pact.spec.ts |
6. E2E tests (staging)
test/e2e/
| Scenario | File |
|---|---|
| Full login → JWT → access-context flow | login-access-context.e2e.spec.ts |
| TOTP enrollment and login with MFA | mfa-totp-login.e2e.spec.ts |
| OIDC login (Keycloak broker) → JIT provision | oidc-login.e2e.spec.ts |
| Device offline binding certificate issuance | device-offline-bind.e2e.spec.ts |
| License assignment and effective resolver | license-lifecycle.e2e.spec.ts |
7. Test data strategy
| Concern | Approach |
|---|---|
| Tenant isolation | Each test suite creates isolated tenant ID via identity_rls_bypass role |
| Password hashing | Use IDENTITY_ARGON2_MEMORY_KIB=4096 in test env for speed; don't skip hashing entirely |
| KMS | Use LocalStack fake-KMS in integration; skip in unit tests (mocked) |
| Keycloak | Wiremock stub for broker tests; real Keycloak in E2E |
| Time-dependent tests | Inject clock via ClockPort (hexagonal); freeze time in domain unit tests |