Initial Commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
## Selection rationale — why these five first
|
||||
|
||||
The 2026-04-25 audit (`/mnt/s/web/genesis/docs/superpowers/specs/2026-04-25-test-suite-audit.md`, §1) lists ten missing fixtures. This wave picks the five with **direct existing-package consumers in the genesis tree** (per the `CLAUDE.md` package list) so each new fixture immediately unblocks integration testing for shipped code:
|
||||
|
||||
| Plugin | Direct consumer on genesis | Audit severity |
|
||||
|---|---|---|
|
||||
| `testing-integration-oidc` | `@suaveplan/oauth-client` (Authorization Code + PKCE, Client Credentials, OIDC discovery) | Critical |
|
||||
| `testing-integration-mongodb` | data-tier parity with `@suaveplan/db-sqlite`; future `db-mongodb` | Critical |
|
||||
| `testing-integration-mysql` | data-tier parity with `@suaveplan/db-sqlite`; future `db-mysql` | Critical |
|
||||
| `testing-integration-minio` | AWS-S3 surface parity vs `testing-integration-garage-s3` quirks | High |
|
||||
| `testing-integration-nats` | `@suaveplan/events`; future stream-processing | High |
|
||||
|
||||
## Deferred (next waves)
|
||||
|
||||
- **OpenTelemetry collector** — required to verify exported spans/metrics end-to-end across every package. Held back because it needs a co-designed assertion helper (`assertSpanExported(name, attributes)`) that does not yet exist; spec lands with the OTel-collector change.
|
||||
- **Elasticsearch / OpenSearch** — Typesense covers our search surface today; no shipped consumer.
|
||||
- **Vault** — no shipped secrets consumer; `@suaveplan/env` and `@suaveplan/config` cover env/file layers.
|
||||
- **LocalStack** — overlaps with MinIO + future SQS/SNS work; revisit once an AWS-API consumer ships.
|
||||
- **Caddy / nginx reverse-proxy** — useful for routing tests once `apps/testbed` formalises a reverse-proxy contract.
|
||||
- **Kafka / RabbitMQ** — NATS goes first as the lightest broker; Kafka follows when a Kafka-specific consumer is on deck.
|
||||
|
||||
## `dependsOn` interaction
|
||||
|
||||
Audit §3 flags that `ServiceFixture` lacks declared inter-fixture dependencies. The `testing-integration` core package is expected to gain a `dependsOn?: string[]` field on `ServiceFixture` and topo-sort start order in `TestEnvironment`. That field is specified by `testing-integration` itself — **out of scope** for this change — but every plugin in this wave MUST tolerate its presence:
|
||||
|
||||
- `testing-integration-oidc` is the canonical upstream: an app fixture that authenticates against the issuer would declare `dependsOn: ['oidc']`. The OIDC fixture itself declares no dependencies.
|
||||
- `testing-integration-mongodb` / `mysql` / `minio` / `nats` likewise declare no dependencies; downstream app fixtures depend on them.
|
||||
|
||||
Plugins MUST NOT introspect the `dependsOn` field; resolution is `TestEnvironment`'s responsibility.
|
||||
|
||||
## Image-pinning policy
|
||||
|
||||
Mirroring the existing six-plugin precedent: each plugin pins a **minor tag** (`mongo:7.0`, `mysql:8.4`, `nats:2.10-alpine`, `quay.io/keycloak/keycloak:26.0`) AND records a `# TODO: replace with sha256:<digest>` comment in the constants file. Digest substitution happens during the implementation chunk, not in the spec — the spec only requires that a digest placeholder exists.
|
||||
|
||||
## Env-bridge contract (uniform across all five)
|
||||
|
||||
When **all** documented env vars for a plugin are present and non-empty, the fixture's `start()` MUST skip container creation entirely and return a `ServiceFixtureHandle` whose config points at the live service. This is the same contract the existing six plugins implement. Each plugin spec below enumerates its env-var set explicitly.
|
||||
|
||||
## Injectable fakes — coverage strategy
|
||||
|
||||
Each fixture exposes a constructor option object with at least:
|
||||
|
||||
- `containerFactory?: () => GenericContainer` — swap in a fake container for unit tests.
|
||||
- One of: `fetchFn?: typeof fetch` (HTTP-driven seeding — OIDC, MinIO), `execFn?: (cmd: string[]) => Promise<{ stdout, stderr, exitCode }>` (in-container CLI seeding — MongoDB, MySQL, NATS), `tcpPingFn?: (host, port) => Promise<boolean>` (TCP healthchecks — NATS).
|
||||
|
||||
This pattern is exactly what `testing-integration-redis` (tcp ping), `testing-integration-postgres` (exec), and `testing-integration-garage-s3` (fetch + exec) already use; consistency lets the same Vitest fake helpers be re-used across plugins.
|
||||
|
||||
## Error-code namespacing
|
||||
|
||||
Each plugin reserves a top-level prefix under `INTEGRATION.*`: `INTEGRATION.OIDC.*`, `INTEGRATION.MONGODB.*`, `INTEGRATION.MYSQL.*`, `INTEGRATION.MINIO.*`, `INTEGRATION.NATS.*`. Codes match `^INTEGRATION\.[A-Z0-9_]+\.[A-Z_]+$` (same regex the existing plugins use).
|
||||
@@ -0,0 +1,54 @@
|
||||
## Why
|
||||
|
||||
The genesis monorepo already ships six `@suaveplan/testing-integration-*` plugin packages (SurrealDB, Redis, Postgres, Mailpit, Typesense, Garage S3) implementing the `ServiceFixture<TConfig>` interface from `@suaveplan/testing-integration`. The 2026-04-25 test-suite documentation audit at `/mnt/s/web/genesis/docs/superpowers/specs/2026-04-25-test-suite-audit.md` (§1, "Coverage gaps in service fixtures") flags the existing plugin set as too narrow for the published package list:
|
||||
|
||||
- `@suaveplan/oauth-client` ships an Authorization Code + PKCE / Client Credentials / OIDC discovery client, but there is no OIDC issuer fixture, so its integration suite cannot exercise a real flow against a real issuer.
|
||||
- `@suaveplan/db-sqlite` exists, but no fixture covers MongoDB or MySQL — both are in the standard data-tier surface for downstream apps.
|
||||
- `@suaveplan/garage-s3` is the only object-store fixture; Garage's quirks (admin-port health, layout assignment, custom init sequence) mask AWS-S3 surface differences that show up against MinIO.
|
||||
- `@suaveplan/events` plus future stream-processing work has no broker fixture; NATS is the lightest option for a first wave.
|
||||
|
||||
Per `CLAUDE.md` rule 5 ("preserve the full public surface") and rule 2 ("complete all implementations — no stubs"), each of these plugins must be specified normatively before code is written, mirroring the existing six-plugin pattern (image policy, env-bridge, healthcheck, config shape, error-code prefix, injectable fakes, plugin-specific scenarios).
|
||||
|
||||
## What Changes
|
||||
|
||||
This change adds **five new capabilities**, one per plugin package, using the same shape as the existing `testing-integration-*` capabilities:
|
||||
|
||||
- `testing-integration-oidc` — Keycloak (preferred) or `oauth2-mock-server` (lighter alt), exercised by `@suaveplan/oauth-client` integration tests.
|
||||
- `testing-integration-mongodb` — `mongo:7.0`, with optional replica-set initialisation for transactions/change-streams.
|
||||
- `testing-integration-mysql` — `mysql:8.4`, with charset/collation override and `mysqladmin ping` health.
|
||||
- `testing-integration-minio` — `minio/minio` with random keys and bucket pre-seeding via the admin API; lives alongside the Garage S3 fixture to expose AWS-S3 surface differences.
|
||||
- `testing-integration-nats` — `nats:2.10-alpine`, optional JetStream + cluster + auth modes, NATS-CLI seeding via injectable `execFn`.
|
||||
|
||||
Each plugin spec mandates:
|
||||
|
||||
1. **Pinned image** with minor tag plus a `TODO` placeholder for the sha256 digest (preserves the existing six-plugin policy).
|
||||
2. **Env-bridge passthrough** — when documented env vars are present and non-empty, no container is started; the fixture returns a config pointing at the live service.
|
||||
3. **Healthcheck** — a deterministic readiness probe with a documented default timeout.
|
||||
4. **Config shape** — TypeScript interface plus a sibling `*.schemas.ts` Zod schema reference.
|
||||
5. **Error-code prefix** — `INTEGRATION.<SERVICE>.*` (matching the `INTEGRATION.SURREALDB.*`, `INTEGRATION.GARAGE_S3.*` precedent).
|
||||
6. **Injectable fakes** — `containerFactory`, plus per-plugin `fetchFn`, `execFn`, or `tcpPingFn` so unit tests reach 100% Istanbul coverage without Docker.
|
||||
7. **Plugin-specific scenarios** — at least three behavioural scenarios per fixture, expressed as `WHEN`/`THEN` pairs.
|
||||
|
||||
`dependsOn?: string[]` (the wave-1 `ServiceFixture` extension flagged by audit §3) is referenced where relevant, e.g. an OIDC-protected app fixture would `dependsOn: ['oidc']`. The field itself is specified by `testing-integration` (out of scope for this change); plugins MUST tolerate its presence in `TestEnvironmentOptions`.
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `testing-integration-oidc`
|
||||
- `testing-integration-mongodb`
|
||||
- `testing-integration-mysql`
|
||||
- `testing-integration-minio`
|
||||
- `testing-integration-nats`
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
(none — additive; existing plugin specs are untouched)
|
||||
|
||||
## Impact
|
||||
|
||||
- **Affected code:** five new packages under `packages/testing/testing-integration-<name>/` (implementation in a follow-up plan).
|
||||
- **Affected consumers:** none yet on genesis. Once shipped, `@suaveplan/oauth-client`, `@suaveplan/events`, future `@suaveplan/db-mongodb` / `db-mysql`, and any S3-API consumer become eligible for real-service integration coverage.
|
||||
- **Catalogs:** `testcontainers` already lives in `catalog:integration` (added by the original six-plugin wave); no new external deps.
|
||||
- **Pre-publish gate:** unchanged — `bunx turbo lint typecheck test --filter=@suaveplan/testing-integration-<name>` plus 100% Istanbul coverage per `openspec/AGENTS.md` §7.
|
||||
- **Deferred (audit §1, not in this change):** Elasticsearch/OpenSearch, Vault, LocalStack, Caddy/nginx reverse-proxy, Kafka, OpenTelemetry collector. Each is a separate future change to keep this proposal scoped.
|
||||
@@ -0,0 +1,125 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Layer 0 dep-graph isolation
|
||||
|
||||
The `@suaveplan/testing-integration-minio` 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-minio...` is run
|
||||
- **THEN** the only `@suaveplan/*` package in the runtime closure is `@suaveplan/testing-integration`
|
||||
|
||||
### Requirement: Coexistence with Garage S3 fixture
|
||||
|
||||
The MinIO fixture MUST exist alongside `@suaveplan/testing-integration-garage-s3` rather than replacing it. The two fixtures expose deliberately different surfaces: Garage masks several AWS-S3 quirks (admin-port health, layout-assignment init), while MinIO mirrors AWS-S3 surface much more closely. Tests that need AWS-API parity MUST target the MinIO fixture; tests covering the Garage init dance MUST target the Garage fixture.
|
||||
|
||||
#### Scenario: Both fixtures coexist
|
||||
|
||||
- **WHEN** a test environment registers both `MinioFixture` and `GarageS3Fixture`
|
||||
- **THEN** both fixtures start in parallel with no shared port or env-var conflicts
|
||||
|
||||
### Requirement: Image-pinning policy
|
||||
|
||||
The default image MUST be a pinned `minio/minio:RELEASE.<date>` tag (latest stable RELEASE at implementation time). The `constants/index.ts` file MUST carry a `# TODO: replace with sha256:<digest>` comment until the digest is recorded.
|
||||
|
||||
#### Scenario: Pinned RELEASE tag
|
||||
|
||||
- **WHEN** the constants file is read
|
||||
- **THEN** the image string starts with `minio/minio:RELEASE.` AND a sha256 TODO line is present
|
||||
|
||||
### Requirement: Env-bridge passthrough
|
||||
|
||||
The fixture MUST consult `MINIO_S3_ENDPOINT`, `MINIO_S3_ACCESS_KEY_ID`, `MINIO_S3_SECRET_ACCESS_KEY`, `MINIO_S3_REGION`, and `MINIO_S3_BUCKET`. When the first four are present and non-empty, `start()` MUST skip container creation and return a handle whose config points at the live MinIO. `MINIO_S3_BUCKET` is optional in passthrough mode.
|
||||
|
||||
#### Scenario: Live MinIO present
|
||||
|
||||
- **WHEN** the four required env vars are set
|
||||
- **THEN** `start()` returns without invoking `containerFactory` AND the returned config's `endpoint` equals `MINIO_S3_ENDPOINT`
|
||||
|
||||
#### Scenario: Partial env triggers container
|
||||
|
||||
- **WHEN** `MINIO_S3_ENDPOINT` is set but `MINIO_S3_SECRET_ACCESS_KEY` is empty
|
||||
- **THEN** `start()` falls back to container mode
|
||||
|
||||
### Requirement: Healthcheck
|
||||
|
||||
The fixture MUST poll `HTTP GET <endpoint>/minio/health/live` and treat a `200` response as ready. The default `healthTimeoutMs` MUST be `60000`. Callers MAY override.
|
||||
|
||||
#### Scenario: Healthcheck succeeds
|
||||
|
||||
- **WHEN** `/minio/health/live` returns 200
|
||||
- **THEN** `start()` resolves
|
||||
|
||||
#### Scenario: Healthcheck times out
|
||||
|
||||
- **WHEN** the endpoint never returns 200 within `healthTimeoutMs`
|
||||
- **THEN** `start()` rejects with code `INTEGRATION.MINIO.HEALTHCHECK_TIMEOUT`
|
||||
|
||||
### Requirement: Random keys per run
|
||||
|
||||
In container mode, when `accessKeyId`/`secretAccessKey` are not supplied, the fixture MUST generate cryptographically random keys for each run (≥ 20 chars, alphanumeric) and pass them as `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` env vars to the container.
|
||||
|
||||
#### Scenario: Random keys
|
||||
|
||||
- **WHEN** the fixture starts twice without explicit keys
|
||||
- **THEN** the two runs use different `accessKeyId` values
|
||||
|
||||
### Requirement: Bucket pre-seeding
|
||||
|
||||
When `bucket` is supplied, the fixture MUST create the bucket via the MinIO admin API after the healthcheck passes, using the injectable `fetchFn`. The fixture MUST NOT resolve `start()` until the bucket exists (verified via `HEAD <endpoint>/<bucket>` returning `200`).
|
||||
|
||||
#### Scenario: Bucket created
|
||||
|
||||
- **WHEN** `start({ bucket: 'app-uploads' })` resolves
|
||||
- **THEN** `HEAD /app-uploads` returns 200 against the returned endpoint
|
||||
|
||||
### Requirement: Config shape
|
||||
|
||||
The exported `MinioConfig` interface MUST include `accessKeyId`, `secretAccessKey`, `region`, optional `bucket?: string`, optional `console?: boolean`, and the resolved `endpoint`. A sibling `minio-fixture.schemas.ts` MUST export a Zod `MinioConfigSchema` that validates the same shape.
|
||||
|
||||
#### Scenario: Schema parity
|
||||
|
||||
- **WHEN** `MinioConfigSchema.safeParse` runs against a `MinioConfig` value
|
||||
- **THEN** valid configs parse OK AND missing required fields fail with `ZodError`
|
||||
|
||||
### Requirement: Error-code prefix
|
||||
|
||||
All errors thrown from the fixture MUST carry a `code` matching `^INTEGRATION\.MINIO\.[A-Z_]+$`.
|
||||
|
||||
#### Scenario: Coded error
|
||||
|
||||
- **WHEN** any fixture method throws
|
||||
- **THEN** `code` starts with `INTEGRATION.MINIO.`
|
||||
|
||||
### Requirement: Injectable fakes
|
||||
|
||||
The fixture constructor MUST accept `containerFactory?: () => GenericContainer` and `fetchFn?: typeof fetch`. Unit tests MUST drive 100% Istanbul branch coverage without Docker or 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 — AWS-S3 parity
|
||||
|
||||
The fixture MUST support, end-to-end against a real container with an AWS S3 SDK client (`@aws-sdk/client-s3`), the following flows in its smoke test:
|
||||
|
||||
#### Scenario: PutObject and GetObject round-trip
|
||||
|
||||
- **WHEN** an SDK client puts an object then gets it back
|
||||
- **THEN** the retrieved body bytes equal the put body bytes AND the `ETag` header is present
|
||||
|
||||
#### Scenario: Presigned GET URL works
|
||||
|
||||
- **WHEN** a presigned GET URL is created via `@aws-sdk/s3-request-presigner` and fetched directly with no SDK signing
|
||||
- **THEN** the response is `200` with the object body
|
||||
|
||||
#### Scenario: Multipart upload works
|
||||
|
||||
- **WHEN** an object larger than the part-size threshold (≥ 5 MB) is uploaded via `CreateMultipartUpload` + `UploadPart` + `CompleteMultipartUpload`
|
||||
- **THEN** the assembled object's body equals the concatenated source AND `HeadObject` reports the full content length
|
||||
|
||||
#### Scenario: Bucket policy attach
|
||||
|
||||
- **WHEN** a `PutBucketPolicy` request attaches a public-read policy
|
||||
- **THEN** `GetBucketPolicy` returns the same JSON document
|
||||
@@ -0,0 +1,106 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Layer 0 dep-graph isolation
|
||||
|
||||
The `@suaveplan/testing-integration-mongodb` 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-mongodb...` is run
|
||||
- **THEN** the only `@suaveplan/*` package in the runtime closure is `@suaveplan/testing-integration`
|
||||
|
||||
### Requirement: Image-pinning policy
|
||||
|
||||
The default image MUST be `mongo:7.0`. The `constants/index.ts` file MUST carry a `# TODO: replace with sha256:<digest>` comment until the digest is recorded.
|
||||
|
||||
#### Scenario: Pinned tag
|
||||
|
||||
- **WHEN** the constants file is read
|
||||
- **THEN** the image string equals `mongo:7.0` AND a sha256 TODO line is present
|
||||
|
||||
### Requirement: Env-bridge passthrough
|
||||
|
||||
The fixture MUST consult `MONGODB_URL` and `MONGODB_DATABASE`. When both are present and non-empty, `start()` MUST skip container creation and return a handle whose config points at the live MongoDB.
|
||||
|
||||
#### Scenario: Live MongoDB present
|
||||
|
||||
- **WHEN** both env vars are set
|
||||
- **THEN** `start()` returns without invoking `containerFactory` AND the returned config's `url` equals `MONGODB_URL`
|
||||
|
||||
#### Scenario: Partial env triggers container
|
||||
|
||||
- **WHEN** `MONGODB_URL` is set but `MONGODB_DATABASE` is empty
|
||||
- **THEN** `start()` falls back to container mode
|
||||
|
||||
### Requirement: Healthcheck
|
||||
|
||||
The fixture MUST run `mongosh --eval "db.runCommand('ping')"` in-container via the injectable `execFn` and treat exit code `0` with stdout containing `ok` as ready. The default `healthTimeoutMs` MUST be `60000`. Callers MAY override.
|
||||
|
||||
#### Scenario: Healthcheck succeeds
|
||||
|
||||
- **WHEN** the in-container `mongosh` ping returns exit code 0 with `{ ok: 1 }` stdout
|
||||
- **THEN** `start()` resolves
|
||||
|
||||
#### Scenario: Healthcheck times out
|
||||
|
||||
- **WHEN** the ping never succeeds within `healthTimeoutMs`
|
||||
- **THEN** `start()` rejects with code `INTEGRATION.MONGODB.HEALTHCHECK_TIMEOUT`
|
||||
|
||||
### Requirement: Replica-set initialisation
|
||||
|
||||
When `replicaSet: true` is supplied, the fixture MUST start `mongod` with `--replSet rs0` and run `rs.initiate()` via `execFn` after the ping healthcheck passes. The fixture MUST NOT resolve `start()` until `rs.status().ok === 1`.
|
||||
|
||||
#### Scenario: Replica-set ready
|
||||
|
||||
- **WHEN** `start({ database: 'app', replicaSet: true })` resolves
|
||||
- **THEN** a subsequent `rs.status()` call reports `ok: 1` AND a multi-document transaction succeeds against the returned `url`
|
||||
|
||||
### Requirement: Authentication mode
|
||||
|
||||
When `authentication: { username, password }` is supplied, the fixture MUST start `mongod` with `--auth`, create the supplied user via `execFn` against the `admin` database, and embed the credentials in the returned `url` (`mongodb://user:pass@host:port/database`).
|
||||
|
||||
#### Scenario: Auth rejects bad credentials
|
||||
|
||||
- **WHEN** the fixture starts in auth mode and a client connects with an incorrect password
|
||||
- **THEN** the connection fails with a MongoDB authentication error
|
||||
|
||||
### Requirement: Config shape
|
||||
|
||||
The exported `MongoDbConfig` interface MUST include `database`, optional `replicaSet?: boolean`, optional `authentication?: { username: string; password: string }`, and the resolved `url`. A sibling `mongodb-fixture.schemas.ts` MUST export a Zod `MongoDbConfigSchema` that validates the same shape.
|
||||
|
||||
#### Scenario: Schema parity
|
||||
|
||||
- **WHEN** `MongoDbConfigSchema.safeParse` is run against a `MongoDbConfig` value
|
||||
- **THEN** valid configs parse OK AND missing required fields fail with `ZodError`
|
||||
|
||||
### Requirement: Error-code prefix
|
||||
|
||||
All errors thrown from the fixture MUST carry a `code` matching `^INTEGRATION\.MONGODB\.[A-Z_]+$`.
|
||||
|
||||
#### Scenario: Coded error
|
||||
|
||||
- **WHEN** any fixture method throws
|
||||
- **THEN** `code` starts with `INTEGRATION.MONGODB.`
|
||||
|
||||
### Requirement: Injectable fakes
|
||||
|
||||
The fixture constructor MUST accept `containerFactory?: () => GenericContainer` and `execFn?: (cmd: string[]) => Promise<{ stdout: string; stderr: string; exitCode: number }>`. Unit tests MUST be able to drive 100% Istanbul branch coverage without Docker.
|
||||
|
||||
#### Scenario: Fake injection
|
||||
|
||||
- **WHEN** the fixture is constructed with both fakes
|
||||
- **THEN** real Docker is never invoked during the test
|
||||
|
||||
### Requirement: Plugin scenarios
|
||||
|
||||
The fixture MUST support, end-to-end against a real container, the following flows in its smoke test:
|
||||
|
||||
#### Scenario: Plain mode write/read round-trip
|
||||
|
||||
- **WHEN** the fixture is started in plain mode and an `insertOne` followed by a `findOne` is performed
|
||||
- **THEN** the inserted document is read back unchanged
|
||||
|
||||
#### Scenario: Replica-set transaction round-trip
|
||||
|
||||
- **WHEN** the fixture is started with `replicaSet: true` and a multi-document transaction is committed
|
||||
- **THEN** all inserts are visible after `commitTransaction()` AND none are visible if `abortTransaction()` is called instead
|
||||
@@ -0,0 +1,102 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Layer 0 dep-graph isolation
|
||||
|
||||
The `@suaveplan/testing-integration-mysql` 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-mysql...` is run
|
||||
- **THEN** the only `@suaveplan/*` package in the runtime closure is `@suaveplan/testing-integration`
|
||||
|
||||
### Requirement: Image-pinning policy
|
||||
|
||||
The default image MUST be `mysql:8.4`. The `constants/index.ts` file MUST carry a `# TODO: replace with sha256:<digest>` comment until the digest is recorded.
|
||||
|
||||
#### Scenario: Pinned tag
|
||||
|
||||
- **WHEN** the constants file is read
|
||||
- **THEN** the image string equals `mysql:8.4` AND a sha256 TODO line is present
|
||||
|
||||
### Requirement: Env-bridge passthrough
|
||||
|
||||
The fixture MUST consult `MYSQL_URL`, `MYSQL_DATABASE`, `MYSQL_USER`, `MYSQL_PASSWORD`, and `MYSQL_ROOT_PASSWORD`. When all five are present and non-empty, `start()` MUST skip container creation and return a handle whose config points at the live MySQL.
|
||||
|
||||
#### Scenario: Live MySQL present
|
||||
|
||||
- **WHEN** all five env vars are set
|
||||
- **THEN** `start()` returns without invoking `containerFactory` AND the returned config's `url` equals `MYSQL_URL`
|
||||
|
||||
#### Scenario: Partial env triggers container
|
||||
|
||||
- **WHEN** `MYSQL_URL` is set but `MYSQL_ROOT_PASSWORD` is empty
|
||||
- **THEN** `start()` falls back to container mode
|
||||
|
||||
### Requirement: Healthcheck
|
||||
|
||||
The fixture MUST run `mysqladmin ping -h localhost -p$MYSQL_ROOT_PASSWORD` in-container via the injectable `execFn` and treat exit code `0` with stdout containing `mysqld is alive` as ready. The default `healthTimeoutMs` MUST be `90000` (initial DB init can be slow on cold image pulls). Callers MAY override.
|
||||
|
||||
#### Scenario: Healthcheck succeeds
|
||||
|
||||
- **WHEN** in-container `mysqladmin ping` returns exit code 0 with `mysqld is alive`
|
||||
- **THEN** `start()` resolves
|
||||
|
||||
#### Scenario: Healthcheck times out
|
||||
|
||||
- **WHEN** the ping never succeeds within `healthTimeoutMs`
|
||||
- **THEN** `start()` rejects with code `INTEGRATION.MYSQL.HEALTHCHECK_TIMEOUT`
|
||||
|
||||
### Requirement: Charset and collation overrides
|
||||
|
||||
The fixture MUST default to `charset: 'utf8mb4'` and `collation: 'utf8mb4_0900_ai_ci'` and MUST pass `--character-set-server=<charset>` and `--collation-server=<collation>` to `mysqld` when overrides are supplied via config.
|
||||
|
||||
#### Scenario: UTF-8mb4 default
|
||||
|
||||
- **WHEN** the fixture is started with no charset override
|
||||
- **THEN** `SHOW VARIABLES LIKE 'character_set_server'` returns `utf8mb4`
|
||||
|
||||
#### Scenario: Charset override applied
|
||||
|
||||
- **WHEN** the fixture is started with `charset: 'utf8'`
|
||||
- **THEN** `SHOW VARIABLES LIKE 'character_set_server'` returns `utf8`
|
||||
|
||||
### Requirement: Config shape
|
||||
|
||||
The exported `MySqlConfig` interface MUST include `database`, `user`, `password`, `rootPassword`, optional `charset?: string`, optional `collation?: string`, and the resolved `url`. A sibling `mysql-fixture.schemas.ts` MUST export a Zod `MySqlConfigSchema` that validates the same shape.
|
||||
|
||||
#### Scenario: Schema parity
|
||||
|
||||
- **WHEN** `MySqlConfigSchema.safeParse` is run against a `MySqlConfig` value
|
||||
- **THEN** valid configs parse OK AND missing required fields fail with `ZodError`
|
||||
|
||||
### Requirement: Error-code prefix
|
||||
|
||||
All errors thrown from the fixture MUST carry a `code` matching `^INTEGRATION\.MYSQL\.[A-Z_]+$`.
|
||||
|
||||
#### Scenario: Coded error
|
||||
|
||||
- **WHEN** any fixture method throws
|
||||
- **THEN** `code` starts with `INTEGRATION.MYSQL.`
|
||||
|
||||
### Requirement: Injectable fakes
|
||||
|
||||
The fixture constructor MUST accept `containerFactory?: () => GenericContainer` and `execFn?: (cmd: string[]) => Promise<{ stdout: string; stderr: string; exitCode: number }>`. Unit tests MUST drive 100% Istanbul branch coverage without Docker.
|
||||
|
||||
#### Scenario: Fake injection
|
||||
|
||||
- **WHEN** the fixture is constructed with both fakes
|
||||
- **THEN** real Docker is never invoked during the test
|
||||
|
||||
### Requirement: Plugin scenarios
|
||||
|
||||
The fixture MUST support, end-to-end against a real container, the following flows in its smoke test:
|
||||
|
||||
#### Scenario: Transaction round-trip
|
||||
|
||||
- **WHEN** a transaction inserts rows and commits
|
||||
- **THEN** the rows are visible to a subsequent connection AND a rolled-back transaction's rows are not visible
|
||||
|
||||
#### Scenario: DDL post-start
|
||||
|
||||
- **WHEN** `CREATE TABLE`, `ALTER TABLE ADD COLUMN`, and `DROP TABLE` statements run after `start()` resolves
|
||||
- **THEN** each statement succeeds AND `INFORMATION_SCHEMA.TABLES` reflects the change
|
||||
@@ -0,0 +1,120 @@
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: Layer 0 dep-graph isolation
|
||||
|
||||
The `@suaveplan/testing-integration-nats` 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-nats...` is run
|
||||
- **THEN** the only `@suaveplan/*` package in the runtime closure is `@suaveplan/testing-integration`
|
||||
|
||||
### Requirement: Image-pinning policy
|
||||
|
||||
The default image MUST be `nats:2.10-alpine`. The `constants/index.ts` file MUST carry a `# TODO: replace with sha256:<digest>` comment until the digest is recorded.
|
||||
|
||||
#### Scenario: Pinned tag
|
||||
|
||||
- **WHEN** the constants file is read
|
||||
- **THEN** the image string equals `nats:2.10-alpine` AND a sha256 TODO line is present
|
||||
|
||||
### Requirement: Env-bridge passthrough
|
||||
|
||||
The fixture MUST consult `NATS_URL`, optional `NATS_CLUSTER_NAME`, and optional `NATS_JETSTREAM_DOMAIN`. When `NATS_URL` is present and non-empty, `start()` MUST skip container creation and return a handle whose config points at the live NATS server. Optional vars influence the returned config but are not required.
|
||||
|
||||
#### Scenario: Live NATS present
|
||||
|
||||
- **WHEN** `NATS_URL` is set
|
||||
- **THEN** `start()` returns without invoking `containerFactory` AND the returned config's `url` equals `NATS_URL`
|
||||
|
||||
#### Scenario: No env triggers container
|
||||
|
||||
- **WHEN** `NATS_URL` is unset or empty
|
||||
- **THEN** `start()` falls back to container mode
|
||||
|
||||
### Requirement: Healthcheck
|
||||
|
||||
The fixture MUST perform a TCP PING on the mapped client port (`4222` inside the container) via the injectable `tcpPingFn`. When the container exposes the monitoring endpoint (`-m 8222`), the fixture MUST also poll `HTTP GET /healthz` and treat a `200` response as ready. The default `healthTimeoutMs` MUST be `60000`. Callers MAY override.
|
||||
|
||||
#### Scenario: TCP ping succeeds
|
||||
|
||||
- **WHEN** `tcpPingFn(host, mappedClientPort)` returns true
|
||||
- **THEN** `start()` resolves once any pre-seeding has completed
|
||||
|
||||
#### Scenario: Healthcheck times out
|
||||
|
||||
- **WHEN** the TCP ping never succeeds within `healthTimeoutMs`
|
||||
- **THEN** `start()` rejects with code `INTEGRATION.NATS.HEALTHCHECK_TIMEOUT`
|
||||
|
||||
### Requirement: JetStream initialisation
|
||||
|
||||
When `jetstream: true` is supplied, the fixture MUST start `nats-server` with `-js` and pre-seed any supplied streams and durable consumers via `nats stream add` / `nats consumer add` invoked through the injectable `execFn` after the TCP-ping healthcheck passes. The fixture MUST NOT resolve `start()` until each declared stream reports `state.messages >= 0` via `nats stream info`.
|
||||
|
||||
#### Scenario: JetStream stream seeded
|
||||
|
||||
- **WHEN** `start({ jetstream: true, streams: [{ name: 'EVENTS', subjects: ['events.>'] }] })` resolves
|
||||
- **THEN** `nats stream info EVENTS` reports the stream exists with the declared subjects
|
||||
|
||||
### Requirement: Cluster mode
|
||||
|
||||
When `cluster: { name, routes }` is supplied, the fixture MUST start `nats-server` with `--cluster_name <name>` and the supplied `--routes` list. The fixture MUST verify cluster formation by calling `nats server list` via `execFn` and checking the cluster name matches.
|
||||
|
||||
#### Scenario: Cluster formed
|
||||
|
||||
- **WHEN** the fixture is started with a cluster config
|
||||
- **THEN** `nats server list` reports `cluster: <name>`
|
||||
|
||||
### Requirement: Authentication mode
|
||||
|
||||
The fixture MUST accept `auth: { user: string; password: string }` OR `auth: { token: string }` (mutually exclusive). When supplied, the fixture MUST start `nats-server` with the corresponding `--user`/`--pass` or `--auth` flags AND embed credentials in the returned `url` (`nats://user:pass@host:port` or `nats://token@host:port`).
|
||||
|
||||
#### Scenario: Token auth rejects bad token
|
||||
|
||||
- **WHEN** the fixture starts with `auth: { token: 'good' }` and a client connects with token `bad`
|
||||
- **THEN** the client connection is refused with a NATS authorization error
|
||||
|
||||
### Requirement: Config shape
|
||||
|
||||
The exported `NatsConfig` interface MUST include optional `jetstream?: boolean`, optional `cluster?: { name: string; routes: string[] }`, optional `auth?: { user: string; password: string } | { token: string }`, optional `streams?: Array<{ name: string; subjects: string[] }>`, optional `consumers?: Array<{ stream: string; durable: string }>`, and the resolved `url`. A sibling `nats-fixture.schemas.ts` MUST export a Zod `NatsConfigSchema` that validates the same shape including the `auth` discriminated union.
|
||||
|
||||
#### Scenario: Schema parity
|
||||
|
||||
- **WHEN** `NatsConfigSchema.safeParse` runs against a `NatsConfig` value
|
||||
- **THEN** valid configs parse OK AND mixing `user`/`password` with `token` in the same `auth` object fails with `ZodError`
|
||||
|
||||
### Requirement: Error-code prefix
|
||||
|
||||
All errors thrown from the fixture MUST carry a `code` matching `^INTEGRATION\.NATS\.[A-Z_]+$`.
|
||||
|
||||
#### Scenario: Coded error
|
||||
|
||||
- **WHEN** any fixture method throws
|
||||
- **THEN** `code` starts with `INTEGRATION.NATS.`
|
||||
|
||||
### Requirement: Injectable fakes
|
||||
|
||||
The fixture constructor MUST accept `containerFactory?: () => GenericContainer`, `tcpPingFn?: (host: string, port: number) => Promise<boolean>`, and `execFn?: (cmd: string[]) => Promise<{ stdout: string; stderr: string; exitCode: number }>`. Unit tests MUST drive 100% Istanbul branch coverage without Docker.
|
||||
|
||||
#### Scenario: Fake injection
|
||||
|
||||
- **WHEN** the fixture is constructed with all three fakes
|
||||
- **THEN** real Docker / real TCP / real exec are never invoked during the test
|
||||
|
||||
### Requirement: Plugin scenarios
|
||||
|
||||
The fixture MUST support, end-to-end against a real container, the following flows in its smoke test:
|
||||
|
||||
#### Scenario: Core pub/sub round-trip
|
||||
|
||||
- **WHEN** a subscriber subscribes to `test.>` and a publisher publishes to `test.hello`
|
||||
- **THEN** the subscriber receives the message within 1s
|
||||
|
||||
#### Scenario: JetStream durable consumer
|
||||
|
||||
- **WHEN** the fixture seeds `EVENTS` stream + `worker` durable consumer, a publisher publishes 10 messages, and the consumer pulls
|
||||
- **THEN** the consumer acks all 10 messages AND `nats consumer info EVENTS worker` reports `num_pending: 0`
|
||||
|
||||
#### Scenario: Request/reply
|
||||
|
||||
- **WHEN** a responder subscribes to `svc.echo` and replies with the request payload, and a client issues a request
|
||||
- **THEN** the client receives the original payload within the request timeout
|
||||
@@ -0,0 +1,107 @@
|
||||
## 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`
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
|
||||
## 0. Pre-flight (non-negotiable)
|
||||
|
||||
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
||||
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
||||
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
||||
|
||||
## 1. Pre-conditions
|
||||
|
||||
- [ ] 1.1 Confirm `@suaveplan/testing-integration` is published to Verdaccio at `https://npmjs.fq.io/` and resolves as `workspace:^`.
|
||||
- [ ] 1.2 Confirm `testcontainers` exists in the root `package.json` `catalog.integration` block.
|
||||
- [ ] 1.3 Confirm the existing six `testing-integration-*` plugins still pass `bunx turbo lint typecheck test --filter=@suaveplan/testing-integration-*`.
|
||||
- [ ] 1.4 Confirm the audit doc at `/mnt/s/web/genesis/docs/superpowers/specs/2026-04-25-test-suite-audit.md` is current and §1 still tracks these five plugins.
|
||||
|
||||
## 2. Spec deltas (this change)
|
||||
|
||||
- [ ] 2.1 Land `specs/testing-integration-oidc/spec.md` with image policy, env-bridge, healthcheck, config, error prefix, fakes, scenarios.
|
||||
- [ ] 2.2 Land `specs/testing-integration-mongodb/spec.md` with replica-set + auth modes.
|
||||
- [ ] 2.3 Land `specs/testing-integration-mysql/spec.md` with charset/collation overrides.
|
||||
- [ ] 2.4 Land `specs/testing-integration-minio/spec.md` with AWS-S3 parity scenarios.
|
||||
- [ ] 2.5 Land `specs/testing-integration-nats/spec.md` with core + JetStream + cluster + auth modes.
|
||||
|
||||
## 3. Capability registration
|
||||
|
||||
- [ ] 3.1 On archive, fold each delta into `openspec/specs/<capability>/spec.md` (five new capability folders).
|
||||
- [ ] 3.2 Move this change folder to `openspec/archive/<yyyy-mm-dd>-enterprise-testing-fixtures-expansion/`.
|
||||
|
||||
## 4. Implementation hand-off (separate plans, not part of this change)
|
||||
|
||||
- [ ] 4.1 Author `docs/superpowers/plans/<date>-testing-integration-oidc.md` mirroring the six-plugin plan structure.
|
||||
- [ ] 4.2 Author corresponding plans for mongodb, mysql, minio, nats.
|
||||
- [ ] 4.3 Each plan must include: package skeleton, fixture impl, fakes, integration smoke test guarded by `skipIfUnavailable()`, README, sibling `.md` per implementation file, 100% Istanbul coverage, root `README.md` table update, `CLAUDE.md` package-list entry.
|
||||
|
||||
## 5. Pre-publish gate (per plugin, executed inside the implementation plan)
|
||||
|
||||
- [ ] 5.1 `bunx turbo lint typecheck test --filter=@suaveplan/testing-integration-<name>` green.
|
||||
- [ ] 5.2 `bunx turbo test:coverage --filter=@suaveplan/testing-integration-<name>` at 100/100/100/100 (Istanbul).
|
||||
- [ ] 5.3 `bunx turbo build --filter=@suaveplan/testing-integration-<name>` green.
|
||||
- [ ] 5.4 Banned-pattern scan zero (per `openspec/AGENTS.md` §2).
|
||||
- [ ] 5.5 Sibling-`.md` scan zero misses (per `AGENTS.md` §4).
|
||||
- [ ] 5.6 `bun scripts/publish.ts --package @suaveplan/testing-integration-<name>` after gates green.
|
||||
|
||||
## 6. Documentation propagation
|
||||
|
||||
- [ ] 6.1 Add each new package to the `CLAUDE.md` package list with one-paragraph blurb (image, env vars, healthcheck, error prefix, deps).
|
||||
- [ ] 6.2 Add each new package row to the root `README.md` package table.
|
||||
- [ ] 6.3 Update `docs/superpowers/specs/2026-04-24-enterprise-testbed-design.md` §4 fixture inventory.
|
||||
- [ ] 6.4 Cross-reference each new plugin from the relevant consumer package's README (`oauth-client`, `events`, future `db-mongodb`/`db-mysql`).
|
||||
Reference in New Issue
Block a user