Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,66 @@
# 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/`.