feat(testing)!: the harness is a package, and the examples dogfood it - #43
Merged
Conversation
Closes #21. `@btravstack/core/testing` is gone; `@btravstack/testing` ships what it did — testRuntime, TestRuntimePort, createFakeClock, withApp — plus the two things every suite had been hand-rolling: - bootFixture(defaults?): a test.extend fixture handing the test boot(module, options?) with a test's defaults (signals off always, probes off unless asked, preDrainDelayMs 0, silent onEvent) and stopping every application it started when the test ends. Teardown mirrors withApp: a Defect on exited fails the test, a modeled Err passes through. - tapped(module, ports): read services out of a booted application, since start hands the context to the runtime alone. The gate refuses a port the module does not export; services() is loud before the graph is built. Why the examples never used withApp (the issue's first question): the test conventions mandate test.extend fixtures with teardown in the fixture, and a callback harness cannot be handed to use(). The eight fixture files now boot through bootFixture, and order-api / order-temporal-worker / order-amqp-worker drop their hand-rolled LoggerTap / ServicesTap for tapped. core's specs reach the package without a package-graph cycle: a tsconfig paths entry to its built declarations, a vitest alias to its source (with @btravstack/core aliased back to src), a turbo edge, a knip ignore, and tsconfig.build.json so the published dist never sees any of it.
There was a problem hiding this comment.
Pull request overview
This PR extracts the test harness into a new published package (@btravstack/testing) and migrates the starters + example deployments to dogfood it, replacing the former @btravstack/core/testing entry point. It also updates the monorepo toolchain (turbo, vitest aliasing, knip) and documentation/TypeDoc to reflect the new package layout.
Changes:
- Add
@btravstack/testingpackage (bootFixture/boot types, tapped, plus moved testRuntime/createFakeClock/withApp) and its test/TypeDoc setup. - Remove
@btravstack/core/testingexport surface and adjust core build/typecheck to avoid a turbo cycle while still letting core specs consume@btravstack/testing. - Migrate starter + example test fixtures to use
bootFixturefor lifecycle teardown andtappedto access services from a running graph; update docs accordingly.
Reviewed changes
Copilot reviewed 82 out of 85 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| turbo.json | Orders @btravstack/testing#build before core typecheck; adds testing build to docs build deps. |
| README.md | Adds @btravstack/testing to the package list. |
| pnpm-lock.yaml | Adds workspace links for @btravstack/testing across affected workspaces. |
| packages/testing/vitest.config.ts | New vitest config + 100% coverage thresholds for the testing package. |
| packages/testing/tsconfig.json | New TS config for the testing package output/typecheck. |
| packages/testing/src/with-app.ts | Switches withApp to import core APIs from @btravstack/core (now that core/testing is gone). |
| packages/testing/src/with-app.spec.ts | Updates spec imports to the new core entry point. |
| packages/testing/src/vitest.d.ts | Adds matcher type augmentation via @unthrown/vitest. |
| packages/testing/src/test-runtime.ts | Moves runtime types/imports to @btravstack/core; updates started-tracking implementation. |
| packages/testing/src/test-runtime.spec.ts | New/ported specs for testRuntime, including abort forwarding. |
| packages/testing/src/test-fixtures.ts | Adds local package test fixtures (runtimeModule, greetingApp, extended it). |
| packages/testing/src/tapped.ts | Introduces tapped() helper to capture exported services from a booted graph. |
| packages/testing/src/tapped.spec.ts | Adds specs for tapped() capture + misuse guard. |
| packages/testing/src/index.ts | Exports the new bootFixture and tapped surface from the package entrypoint. |
| packages/testing/src/fake-clock.ts | Switches Clock import to @btravstack/core. |
| packages/testing/src/fake-clock.spec.ts | Adds specs for fake clock behavior. |
| packages/testing/src/boot-fixture.ts | Implements bootFixture() vitest-fixture body with Defect-only teardown semantics. |
| packages/testing/src/boot-fixture.spec.ts | Adds specs covering fixture defaults + teardown behavior. |
| packages/testing/README.md | New package README describing usage and exported surface. |
| packages/testing/package.json | New package manifest (dual build, peers, scripts). |
| packages/testing/LICENSE | Adds license file for the new package. |
| packages/testing/CLAUDE.md | Adds package-local spec for the testing surface + its invariants/tests/tooling arrangement. |
| packages/temporal/src/test-fixtures.ts | Migrates temporal starter fixtures to use bootFixture and remove hand-rolled teardown. |
| packages/temporal/package.json | Adds @btravstack/testing devDependency for fixtures. |
| packages/temporal/CLAUDE.md | Updates temporal package spec to reference new fixture composition/boot behavior. |
| packages/http/src/test-fixtures.ts | Migrates HTTP starter fixtures to bootFixture; removes manual stop/expect teardown. |
| packages/http/package.json | Adds @btravstack/testing devDependency for fixtures. |
| packages/http/CLAUDE.md | Updates HTTP package spec to reflect bootFixture-driven teardown. |
| packages/core/vitest.config.ts | Adds vitest aliases so core specs can import @btravstack/testing without a dependency cycle. |
| packages/core/tsconfig.json | Adds paths mapping to built @btravstack/testing d.ts for typecheck. |
| packages/core/tsconfig.build.json | New build tsconfig to clear paths and exclude tests from published output. |
| packages/core/src/test-runtime.spec.ts | Removes core-local testRuntime specs (moved to @btravstack/testing). |
| packages/core/src/test-fixtures.ts | Switches core fixtures to import testRuntime types from @btravstack/testing. |
| packages/core/src/start.test-d.ts | Updates type tests to use testRuntime from @btravstack/testing. |
| packages/core/src/start.spec.ts | Updates core tests to use createFakeClock/testRuntime from @btravstack/testing. |
| packages/core/src/run-main.spec.ts | Updates core tests to use createFakeClock/testRuntime from @btravstack/testing. |
| packages/core/src/invariants.spec.ts | Updates core invariant tests to use withApp/clock/runtime from @btravstack/testing. |
| packages/core/src/docs-examples.test-d.ts | Updates compiled docs samples to reference @btravstack/testing. |
| packages/core/README.md | Removes references to core/testing; points to @btravstack/testing instead. |
| packages/core/package.json | Removes ./testing export; updates tsdown invocation to use tsconfig.build.json. |
| packages/core/CLAUDE.md | Documents the new “core specs consume testing package” arrangement and points to new spec locations. |
| packages/amqp/src/test-fixtures.ts | Migrates AMQP starter fixtures to bootFixture; removes manual stop/expect teardown. |
| packages/amqp/package.json | Adds @btravstack/testing devDependency for fixtures. |
| packages/amqp/CLAUDE.md | Updates AMQP package spec to reference bootFixture-driven teardown behavior. |
| knip.json | Ignores @btravstack/testing for core workspace to accommodate non-package.json import resolution. |
| examples/README.md | Documents that deployments now test via @btravstack/testing (boot + tapped). |
| examples/order-temporal-worker/src/test-fixtures.ts | Migrates example worker fixtures to bootFixture + tapped (removes custom ServicesTap). |
| examples/order-temporal-worker/README.md | Updates fixture documentation to mention boot/tapped from testing package. |
| examples/order-temporal-worker/package.json | Adds @btravstack/testing devDependency for fixtures. |
| examples/order-api/src/test-fixtures.ts | Migrates API example fixtures to bootFixture + tapped (removes LoggerTap). |
| examples/order-api/README.md | Updates fixture documentation to mention boot/tapped from testing package. |
| examples/order-api/package.json | Adds @btravstack/testing devDependency for fixtures. |
| examples/order-amqp-worker/src/test-fixtures.ts | Migrates AMQP example fixtures to bootFixture + tapped (removes custom ServicesTap). |
| examples/order-amqp-worker/README.md | Updates fixture documentation to mention boot/tapped from testing package. |
| examples/order-amqp-worker/package.json | Adds @btravstack/testing devDependency for fixtures. |
| docs/typedoc.testing.json | Adds TypeDoc config for the new testing package API docs. |
| docs/typedoc.core.json | Removes core/testing entry point from core TypeDoc generation. |
| docs/tutorial/getting-started.md | Updates tutorial link text to reference bootFixture/@btravstack/testing. |
| docs/scripts/build-api.ts | Adds testing to the package list for API generation. |
| docs/reference/testing.md | Replaces the old core/testing reference page with the new @btravstack/testing reference. |
| docs/reference/packages.md | Updates package list, peer matrix, and install snippets to include testing package. |
| docs/reference/core/start.md | Updates gate discussion to include Boot/testing withApp. |
| docs/reference/core/runtime.md | Updates fake clock reference to point at @btravstack/testing. |
| docs/reference/core/probes.md | Updates probes note to reflect bootFixture defaults + how to opt into probes in tests. |
| docs/reference/core/events.md | Updates events note to reflect bootFixture default sink and pass-through behavior. |
| docs/index.md | Updates landing page package count/list to include testing package. |
| docs/how-to/write-a-runtime.md | Updates testing pointer to use bootFixture. |
| docs/how-to/test-an-application.md | Rewrites the how-to around bootFixture + tapped, retaining withApp for one-offs. |
| docs/how-to/swap-an-adapter.md | Updates guidance to reference @btravstack/testing boot/withApp instead of core/testing. |
| docs/how-to/open-a-per-request-scope.md | Updates guidance to include testing boot/withApp as valid unit carriers. |
| docs/how-to/embed-without-run-main.md | Updates related links to mention bootFixture and withApp. |
| docs/explanation/why-start.md | Updates “family” section to include @btravstack/testing. |
| docs/explanation/nothing-throws.md | Updates the “Promise exceptions” section to include testing package + bootFixture rationale. |
| docs/explanation/design-decisions.md | Adds explicit design decision section: test harness is a package. |
| docs/explanation/compile-time-wiring.md | Updates gate explanation to include testing Boot/withApp. |
| docs/examples/order-temporal-worker.md | Updates example docs to mention boot fixture and tapped usage. |
| docs/examples/order-api.md | Updates example docs to show bootFixture-based fixtures and tapped logger access. |
| docs/examples/order-amqp-worker.md | Updates example docs to show bootFixture-based fixtures and tapped service access. |
| docs/api/index.md | Updates API index to reflect core’s single entry point + adds testing package API page. |
| docs/.vitepress/config.ts | Updates sidebar and API nav to include @btravstack/testing. |
| CLAUDE.md | Updates repo-wide spec to add the testing package and its toolchain/test conventions integration. |
| .changeset/unit-module.md | Extends changeset to include testing package (signal availability note). |
| .changeset/testing-package.md | Adds changeset describing the new testing package + removal of core/testing. |
| .changeset/runtime-port.md | Updates changeset text to refer to @btravstack/testing. |
| .changeset/initial-kernel.md | Updates initial-kernel changeset to refer to testing package. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Port("Tap") -> Port("@btravstack/testing/Tap"): the port is invisible to
the application that would collide with it, and di keys services by the
literal id, so a bare "Tap" would shadow an application's own with a
warning naming a port its author never wrote
- test-runtime.spec.ts's two moved tests now assert once, deeply, per the
test conventions rule 4 and 5 that bind everywhere
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #21.
Why the examples never imported the harness (acceptance 1)
The repo's test conventions mandate
test.extendfixtures with teardown in the fixture;withApp(module, options, use)is a callback wrapper, so it cannot be handed touse()and give the test the app back. Every suite therefore hand-rolled the samestart(…, { signals: false, probes: false, preDrainDelayMs: 0 })+stop(); await expect(exited).toBeOk(). A second gap:starthands the application context to the runtime alone, soorder-apiand the two workers each wrote aLoggerTap/ServicesTapprovider just to reach a service of the running app.@btravstack/testingA package, the way
@nestjs/testingis —@btravstack/core/testingis gone (breaking, unreleased). It shipstestRuntime()/TestRuntimePort,createFakeClock(),withApp()(moved unchanged, with their specs) plus:bootFixture(defaults?)— atest.extendfixture giving the testboot(module, options?)with a test's defaults baked in and every application stopped when the test ends. Teardown mirrorswithApp: a Defect onexitedfails the test, a modeledErrpasses through (soserveBroken'sErrexit stays the test's to assert).tapped(module, [Port, …])—{ module, services() }; the gate refuses a port the module does not export, andservices()is loud before the graph is built.No
vitestpeer:bootFixturereturns vitest's fixture shape as a plain function, so the package stays runner-agnostic.Migrated
The kernel's own specs and the fixtures of
packages/{http,temporal,amqp}andexamples/{order-api,order-temporal-worker,order-amqp-worker}— eight files.order-api'sLoggerTapand the two workers'ServicesTapare nowtapped.The one wrinkle
@btravstack/testingpeers on@btravstack/core, and core's specs use it — a package-graph cycle turbo refuses. Broken without a cycle: a tsconfigpathsentry to the built declarations, a vitest alias to its source (with@btravstack/corealiased back tosrc, so one kernel is in play), a@btravstack/core#typecheck → @btravstack/testing#buildedge, a knipignoreDependencies, andpackages/core/tsconfig.build.jsonso the publisheddistnever sees any of it.Test plan
@btravstack/testingat 100% lines/functions, core unchanged/reference/testing, the testing how-to and the API reference rebuilt; zero dead links