Move docs/ package mirror into document-navigator plugin
Relocate the ~6k-file docs tree from the repo root into plugins/document-navigator/docs/ so the plugin is self-contained and portable. docs/superpowers/ (specs and plans) stays at the repo root. Moved via cp+rm rather than git mv because directory rename() fails with EPERM on this WSL /mnt DrvFs mount; git rename-detection pairs the identical-content files at commit time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
# `testing/conformance` — `runSecretsConformance` harness
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
`runSecretsConformance` is the reusable Vitest suite every concrete `SecretsLike` implementation in the api-secrets family MUST pass. Stage-4 adapters (`api-secrets-env`, `api-secrets-vault`) and The `api-secrets-bun` adapter all import it, hand it a factory that returns a fresh fixture per scenario, and inherit the cross-package shape guarantees — `get` returns the value, `get` returns `undefined` on miss, `require` returns the value, `require` throws `SECRETS.SECRET_NOT_FOUND` on miss, and `list()` enumerates seeded names. Centralising the suite means an adapter cannot accidentally drift its semantics from peer adapters without a test failure showing up in its own CI.
|
||||
|
||||
The harness is intentionally factory-based so sources that own real resources (testcontainers-backed vault, OS keystore) can seed and tear down per scenario rather than reusing a single instance across the suite.
|
||||
|
||||
## Features
|
||||
|
||||
- Five canonical scenarios covering the four documented behaviours plus `list()` enumeration.
|
||||
- Per-scenario fixture factory with optional teardown hook.
|
||||
- Customisable `isNotFound` predicate so sources representing missing names differently (network adapter wrapping a transport error) can still pass.
|
||||
- All `it(...)` calls carry `{ timeout: 5000 }`.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import { runSecretsConformance } from "@suaveplan/api-secrets/testing";
|
||||
import { createEnvSecrets } from "./env-secrets.js";
|
||||
|
||||
runSecretsConformance({
|
||||
label: "env-secrets",
|
||||
factory: (seed) => {
|
||||
const restore = applyEnv(seed);
|
||||
const secrets = createEnvSecrets({ allowList: Object.keys(seed) });
|
||||
return { secrets, teardown: () => restore() };
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
Adapt the harness to a source whose missing-name representation differs from the canonical code:
|
||||
|
||||
```ts
|
||||
import { runSecretsConformance } from "@suaveplan/api-secrets/testing";
|
||||
import { VaultTransportError } from "./vault-transport.js";
|
||||
|
||||
runSecretsConformance({
|
||||
label: "vault-secrets",
|
||||
factory: async (seed) => {
|
||||
const container = await startVaultContainer({ seed });
|
||||
return {
|
||||
secrets: container.secrets,
|
||||
teardown: () => container.stop(),
|
||||
};
|
||||
},
|
||||
// vault wraps "not found" in a transport-level 404 — recognise it as
|
||||
// the canonical SECRET_NOT_FOUND for the purposes of the suite.
|
||||
isNotFound: (err) =>
|
||||
err instanceof VaultTransportError && err.status === 404,
|
||||
});
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `function runSecretsConformance(options: RunSecretsConformanceOptions): void` — registers a Vitest `describe` block; call once at module top level.
|
||||
- `interface RunSecretsConformanceOptions` — `{ label, factory, isNotFound? }`.
|
||||
- `interface SecretsConformanceFixture` — `{ secrets, teardown? }`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The harness deliberately uses `describe`/`it` from `@suaveplan/testing/runner` rather than importing directly from `vitest` — that runner wrapper routes to `bun:test` under Bun's runner condition, so a package whose runtime is browser-tier can still drive its conformance check from a `bun test` invocation. The default `isNotFound` matches on the canonical `SECRETS.SECRET_NOT_FOUND` code string; adapters not extending `SecretsError` directly must supply their own predicate. Note the harness does not check `dispose()` — that's intentional, because the canonical api-types `SecretsLike` shape does not require `dispose`; adapters that DO own resources should pair the conformance run with their own teardown suite.
|
||||
Reference in New Issue
Block a user