Skip to main content

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 tierTargetEnforced by
Unit≥ 80% statements + linesVitest --coverage in CI
IntegrationMandatory scenarios listed belowCI gate
Contract (Pact)All published events; all consumed eventsPact broker green
E2ECritical auth flowsPlaywright; staging only

2. Test pyramid

3. Unit tests

Target: domain layer and application use-cases. Infrastructure adapters are mocked.

ScenarioFile 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 / verificationdomain/credential.spec.ts
MFA factor enrollment / verificationdomain/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

ScenarioAssertion
User A in tenant X cannot read users from tenant Y403 / RLS blocks row
Session created in tenant X is not visible to tenant YRLS policy verified
License assignments scoped to tenant X not returned for tenant YRLS policy verified

4.2 Outbox reliability

test/integration/outbox.spec.ts

ScenarioAssertion
RegisterUser use-case writes user row and outbox row atomicallyDB transaction
Outbox relay publishes identity.user.registered.v1 to NATSEvent received
Failed NATS publish does not discard outbox rowRow remains unpublished
Outbox relay deduplicates on republishIdempotent delivery

4.3 Inbox deduplication

test/integration/inbox.spec.ts

ScenarioAssertion
tenant.suspended.v1 consumed once even with NATS redeliverySingle processing
tenant.activated.v1 creates shadow user exactly onceNo duplicate users

4.4 Security-specific integration

ScenarioFile
Refresh token replay detection triggers session revocationrefresh-replay.integration.spec.ts
Suspended user cannot loginuser-suspension.integration.spec.ts
Concurrent session cap enforcedsession-cap.integration.spec.ts
Device offline binding requires trusted statedevice-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 subjectSchema spec
identity.user.registered.v1test/contract/identity.user.registered.schema.spec.ts
identity.user.suspended.v1test/contract/identity.user.suspended.schema.spec.ts
identity.device.bound_for_offline.v1test/contract/identity.device.bound_for_offline.schema.spec.ts
identity.license.assignment.status_changed.v1test/contract/identity.license.assignment.schema.spec.ts

5.2 API consumer contracts (Pact)

ConsumerContract file
tenant-servicetest/contract/tenant-service.pact.spec.ts
communication-servicetest/contract/communication-service.pact.spec.ts

6. E2E tests (staging)

test/e2e/

ScenarioFile
Full login → JWT → access-context flowlogin-access-context.e2e.spec.ts
TOTP enrollment and login with MFAmfa-totp-login.e2e.spec.ts
OIDC login (Keycloak broker) → JIT provisionoidc-login.e2e.spec.ts
Device offline binding certificate issuancedevice-offline-bind.e2e.spec.ts
License assignment and effective resolverlicense-lifecycle.e2e.spec.ts

7. Test data strategy

ConcernApproach
Tenant isolationEach test suite creates isolated tenant ID via identity_rls_bypass role
Password hashingUse IDENTITY_ARGON2_MEMORY_KIB=4096 in test env for speed; don't skip hashing entirely
KMSUse LocalStack fake-KMS in integration; skip in unit tests (mocked)
KeycloakWiremock stub for broker tests; real Keycloak in E2E
Time-dependent testsInject clock via ClockPort (hexagonal); freeze time in domain unit tests