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

31 lines
2.4 KiB
Markdown

<!-- markdownlint-disable MD024 -->
# Spec delta: api-host
## MODIFIED Requirements
### Requirement: `./testing` subpath ships the PipelineRouter conformance harness
The package SHALL declare `./testing` as a subpath export AND ship `runPipelineRouterConformance(factory)` plus the supporting `PipelineRouterConformanceFactory` and `PipelineRouteRegistration` types. The harness SHALL exercise the host's `PipelineRouter` contract against any adapter via a factory-supplied `(router, register, reload?)` triple, covering: register → match round-trip, `null` on miss, `statusMap` propagation, plugin ownership across reload, `:param` parsing, and collision detection.
The placeholder `STAGE_2_RESERVED` sentinel SHALL remain exported for back-compat so existing detection logic (`Object.keys(mod).length > 0` style) continues to discriminate "is the harness wired?" from "is the subpath reserved?".
The package's vite build SHALL emit `dist/testing/testing.js` AND `dist/testing/pipeline-router-conformance/pipeline-router-conformance.js` so the subpath actually resolves at runtime — these files were previously absent because the testing module was tree-shaken out of the single-entry build.
#### Scenario: harness drives a reference PipelineRouter through every required case
- **WHEN** a test calls `runPipelineRouterConformance({ label, build: () => ({ router, register, reload? }) })`
- **THEN** `describe("PipelineRouter conformance — ${label}", ...)` registers six `it()` cases AND all of them pass against any adapter whose `register` populates a backing table consulted by `router.match`
#### Scenario: harness asserts collision detection throws
- **WHEN** the harness invokes `register({ plugin: "p", method: "GET", path: "/x" })` followed by `register({ plugin: "q", method: "GET", path: "/x" })`
- **THEN** the second call SHALL throw — the harness uses `expect(...).toThrow()` without prescribing the error class so any adapter-specific typed error satisfies the contract
#### Scenario: harness asserts plugin ownership survives reload
- **WHEN** the factory exposes a `reload()` hook, the harness registers a route, calls `reload()`, then calls `router.match(...)`
- **THEN** the matched route's `plugin` AND `params` survive the reload cycle
**Verified by:** `src/testing/pipeline-router-conformance/pipeline-router-conformance.test.ts` AND (downstream) `@suaveplan/api-router-hono`'s `src/pipeline/pipeline.conformance.test.ts`