Files
2026-05-27 23:29:53 +01:00

5.5 KiB

ADDED Requirements

Requirement: Layer 0 dep-graph isolation

The @suaveplan/testing-integration-oidc package's dependencies MUST be exactly @suaveplan/testing-integration (workspace:^) and testcontainers (catalog:integration). No other @suaveplan/* runtime dep is permitted. devDependencies MAY include @suaveplan/testing for the package's own tests.

Scenario: Runtime closure clean

  • WHEN bunx turbo ls --filter=@suaveplan/testing-integration-oidc... is run
  • THEN the only @suaveplan/* package in the runtime closure is @suaveplan/testing-integration

Requirement: Pluggable provider selection

The fixture SHALL accept a provider: 'keycloak' | 'oauth2-mock-server' config field. Both providers MUST satisfy the same ServiceFixture<OidcConfig> contract — only the underlying image, init sequence, and admin-API endpoints differ.

Scenario: Keycloak provider selected

  • WHEN start({ provider: 'keycloak', realm: 'test' }) is called
  • THEN the fixture pulls quay.io/keycloak/keycloak:26.0 AND seeds the test realm via the Keycloak admin REST API before resolving

Scenario: oauth2-mock-server provider selected

  • WHEN start({ provider: 'oauth2-mock-server' }) is called
  • THEN the fixture pulls ghcr.io/navikt/mock-oauth2-server:2 AND seeds clients/users via its JSON config-file mount before resolving

Requirement: Image-pinning policy

Default images MUST be pinned to a minor tag (quay.io/keycloak/keycloak:26.0, ghcr.io/navikt/mock-oauth2-server:2). The constants/index.ts file MUST carry a # TODO: replace with sha256:<digest> comment for each image until the digest is recorded.

Scenario: Pinned tag

  • WHEN the constants file is read
  • THEN both image strings include an explicit minor tag AND a sha256 TODO line is present

Requirement: Env-bridge passthrough

The fixture MUST consult OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI, and optional OIDC_USERS_FILE. When OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, and OIDC_REDIRECT_URI are all present and non-empty, start() MUST skip container creation and return a handle whose config points at the live issuer.

Scenario: Live issuer present

  • WHEN all four required env vars are set
  • THEN start() returns without invoking containerFactory AND the returned config's issuerUrl equals OIDC_ISSUER_URL

Scenario: Partial env triggers container

  • WHEN OIDC_ISSUER_URL is set but OIDC_CLIENT_SECRET is empty
  • THEN start() falls back to container mode

Requirement: Healthcheck

The fixture MUST poll HTTP GET <issuerUrl>/.well-known/openid-configuration and treat a 200 JSON response containing issuer, authorization_endpoint, token_endpoint, jwks_uri as ready. The default healthTimeoutMs MUST be 90000 (Keycloak slow-start). Callers MAY override.

Scenario: Healthcheck succeeds

  • WHEN /.well-known/openid-configuration returns 200 with the four required fields
  • THEN start() resolves

Scenario: Healthcheck times out

  • WHEN the discovery endpoint never returns within healthTimeoutMs
  • THEN start() rejects with code INTEGRATION.OIDC.HEALTHCHECK_TIMEOUT

Requirement: Config shape

The exported OidcConfig interface MUST include provider, optional realm, optional users: Array<{ username: string; password: string; email: string }>, optional clients: Array<{ id: string; secret: string; redirectUris: string[]; grantTypes: string[] }>, and the resolved issuerUrl. A sibling oidc-fixture.schemas.ts MUST export a Zod schema (OidcConfigSchema) that validates the same shape.

Scenario: Schema parity

  • WHEN OidcConfigSchema.safeParse is run against an OidcConfig value
  • THEN the parse succeeds for valid configs AND fails with a ZodError for missing required fields

Requirement: Error-code prefix

All errors thrown from the fixture MUST be subclasses of TestingIntegrationError carrying { code, cause?, context? } where code matches ^INTEGRATION\.OIDC\.[A-Z_]+$.

Scenario: Coded error

  • WHEN any fixture method throws
  • THEN the thrown value's code starts with INTEGRATION.OIDC.

Requirement: Injectable fakes

The fixture constructor MUST accept containerFactory?: () => GenericContainer and fetchFn?: typeof fetch. Unit tests MUST be able to drive 100% Istanbul branch coverage without invoking Docker or the network.

Scenario: Fake injection

  • WHEN the fixture is constructed with both fakes
  • THEN real Docker / real fetch are never called during the test

Requirement: Plugin scenarios

The fixture MUST support, end-to-end against a real container, the following flows used by @suaveplan/oauth-client and exercised in the plugin's smoke test:

Scenario: Authorization Code + PKCE returns valid tokens

  • WHEN @suaveplan/oauth-client runs an Authorization Code + PKCE flow against the fixture's seeded client
  • THEN the issuer returns an id_token AND access_token whose JWT signature verifies against the jwks_uri

Scenario: Client Credentials grant succeeds

  • WHEN a grant_type=client_credentials request is made with the seeded client id and secret
  • THEN the response is 200 OK with an access_token JWT

Scenario: Revoked user introspection

  • WHEN a seeded user is removed (Keycloak admin REST or oauth2-mock-server config reload) and a previously issued token is introspected
  • THEN the introspection response has active: false