Files
suaveplan/openspec/archive/2026-05-02-testing-integration-step-ca/tasks.md
2026-05-27 23:29:53 +01:00

67 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks — testing-integration-step-ca
## 1. Package scaffolding
- [x] 1.1 Create `packages/dev/testing-integration-step-ca/` with the standard layout (`package.json`, `tsconfig.json`, `vite.config.ts`, `README.md`, `src/index.ts`, `src/fixture/`).
- [x] 1.2 `package.json`: name `@suaveplan/testing-integration-step-ca`, version `0.1.0`, `peerDependencies` for `@suaveplan/testing-integration` and `testcontainers` (NOT `dependencies`), `dependencies` for `@suaveplan/error` and `@suaveplan/types` only.
- [x] 1.3 Subpath exports: `.`, `./fixture`, `./constants`.
- [x] 1.4 Add the package to `CLAUDE.md` `Packages` section, then run `bun scripts/folder-reorg/generate-manifest.ts`.
## 2. Image + constants
- [x] 2.1 Pin `smallstep/step-ca` to a specific tag + digest in `src/constants/index.ts` as `STEPCA_IMAGE`.
- [x] 2.2 Define `STEPCA_HEALTH_TIMEOUT_MS = 60_000`, `STEPCA_HEALTH_POLL_INTERVAL_MS = 1000`.
- [x] 2.3 Define `STEPCA_ENV_VARS = ['STEPCA_URL','STEPCA_ROOT_CERT_PEM','STEPCA_ROOT_CERT_PATH','STEPCA_FINGERPRINT','STEPCA_PROVISIONER_PASSWORD'] as const`.
- [x] 2.4 Define `STEPCA_DEFAULT_PROVISIONER = 'api'`, `STEPCA_DEFAULT_DNS = 'localhost,step-ca'`, `STEPCA_INTERNAL_PORT = 8443`.
## 3. Error codes
- [x] 3.1 Register codes via `@suaveplan/error`'s `defineCodes` with prefix `INTEGRATION.STEP_CA.*`: `INIT_FAILED`, `JWK_PROVISIONER_FAILED`, `ROOT_READ_FAILED`, `FINGERPRINT_FAILED`, `ISSUE_CERT_FAILED`, `PARSE_PEM_FAILED`, `HEALTH_TIMEOUT`.
## 4. Fixture core (`src/fixture/step-ca-fixture.ts`)
- [x] 4.1 Implement `StepCaFixture implements ServiceFixture<StepCaConfig>` with `name === 'step-ca'`.
- [x] 4.2 Constructor options: `{ image?; caName?; dnsNames?; provisionerName?; healthTimeoutMs?; healthPollIntervalMs?; containerFactory?; execFn?; fetchFn? }`.
- [x] 4.3 `start()` env-bridge: when ALL FIVE `STEPCA_ENV_VARS` are set+non-empty → return passthrough config; partial (4 of 5) → fall through to container mode.
- [x] 4.4 Container mode: start container via factory (default: dynamic `GenericContainer` import), expose internal `8443` → host port, set environment variables required by step-ca.
- [x] 4.5 Init sequence (steps 1–6 from proposal §What Changes/Container mode); each step wrapped in a try/catch that throws the matching `INTEGRATION.STEP_CA.*` error with the underlying stderr attached.
- [x] 4.6 Materialize `rootCertPem` to a host-side tmpfile (`fs.mkdtempSync` + `tmp/<name>.crt`), set `rootCertPath` accordingly.
- [x] 4.7 If `waitForHealth()` rejects, stop the container exactly once and clear the reference before re-throwing.
## 5. Health probe
- [x] 5.1 `healthCheck()` returns `false` when no container started.
- [x] 5.2 Container mode: `GET https://localhost:<caPort>/health` via injected `fetchFn` (default `globalThis.fetch`) with TLS verification disabled (use a per-call `https.Agent` or `Bun`-equivalent that the injected fetch can apply). Return `res.ok`. Network errors → `false`.
- [x] 5.3 `waitForHealth()` delegates to `new HealthPoller({ timeoutMs, intervalMs }).poll(this)` with `STEPCA_HEALTH_TIMEOUT_MS` / `STEPCA_HEALTH_POLL_INTERVAL_MS` defaults.
- [x] 5.4 Timeout rejection MUST be a `TestEnvironmentError` whose message contains `Health check timed out for fixture "step-ca"`.
## 6. `issueCert()` helper
- [x] 6.1 Signature `issueCert({ commonName, sans?, ttl? }) → Promise<{ certPem; keyPem; chainPem; serial }>`.
- [x] 6.2 Shells `step ca certificate <commonName> <crt> <key> --provisioner <provName> --provisioner-password-file <pwFile> --not-after=<ttl> --san <san1> --san <san2> ...` inside the container via injected `execFn`.
- [x] 6.3 Reads cert + key from container, splits leaf + chain via PEM block parser (own implementation; no external dep), extracts serial via `step certificate inspect --format json`.
- [x] 6.4 Throws `INTEGRATION.STEP_CA.ISSUE_CERT_FAILED` on non-zero exit; `INTEGRATION.STEP_CA.PARSE_PEM_FAILED` on malformed PEM.
- [x] 6.5 Verifies `commonName` is non-empty before shelling — empty → `INTEGRATION.STEP_CA.ISSUE_CERT_FAILED` (no shell invocation).
## 7. Tests (100% Istanbul, per AGENTS.md §7)
- [x] 7.1 `src/fixture/step-ca-fixture.test.ts` — passthrough mode (full + partial), container mode happy path, init sequence command order, image override, `containerFactory` injection, `execFn` injection, `fetchFn` injection, health-check truth table, `waitForHealth` timeout + cleanup, `stop()` idempotency.
- [x] 7.2 `src/fixture/step-ca-fixture-issue-cert.test.ts` — issueCert happy path (with mocked `execFn`), SAN passthrough, TTL passthrough, malformed PEM rejection, empty commonName rejection, non-zero exit rejection.
- [x] 7.3 `src/fixture/step-ca-fixture-parse.test.ts` — PEM block splitter (leaf + chain), serial extraction, edge cases (empty input, missing END line, multiple chain certs).
- [x] 7.4 Integration-level smoke test `src/fixture/step-ca-fixture.integration.test.ts` (gated by `INTEGRATION_DOCKER=1`) that actually boots the container, issues a cert, and verifies the leaf chains to the root using Node's `tls.createSecureContext({ ca: rootCertPem })`.
## 8. Pre-publish gate
- [x] 8.1 `bunx turbo lint typecheck test --filter=@suaveplan/testing-integration-step-ca` green.
- [x] 8.2 `bunx turbo test:coverage --filter=@suaveplan/testing-integration-step-ca` reports 100% on touched modules.
- [x] 8.3 Spec-conformance test file (`src/fixture/step-ca-fixture.spec-conformance.test.ts`) asserts every "Verified by" anchor in `specs/testing-integration-step-ca/spec.md` resolves to a real test.
## 9. Documentation
- [x] 9.1 Root `README.md` for the package: purpose, basic example (`new StepCaFixture()` + `issueCert`), advanced example (using the root cert with `NODE_EXTRA_CA_CERTS` and Playwright `--ignore-certificate-errors-spki-list`), API reference, implementation notes, ≥200 words.
- [x] 9.2 Co-located `.md` for each implementation file per `CLAUDE.md` Documentation rules.
## 10. Archive
- [x] 10.1 On ship: fold the spec delta into `openspec/specs/testing-integration-step-ca/spec.md` and move this folder to `openspec/archive/<yyyy-mm-dd>-testing-integration-step-ca/`.