Context
Every package and service needs the same test plumbing: a vitest preset, factories, fakes for the clock and ids, and matchers. Before this decision each package wrote its own, and the domain helpers (a module under test, a dispatch runner, actors, refusal and event matchers, a permission matcher) had no agreed home. 0022 decision 4 asked for “an explicittesting entry of the package that owns the contract”, but did not say what a shared
package holds. 0182 fixed the test tiers by
filename infix, so the entries below line up with those tiers. The owner settled the placement on 2026-10-21. That answer
reverses an earlier answer of the same day, which put every helper in one package.
Decision
-
@stackra/testingis atier:libpackage in thedevelopercategory (packages/developer/testing, 0127). It has six entries:.,./node,./react,./native,./e2eand./contract. Each entry is declared inpackage.jsononly when it holds real code, as 0126 decision 2 says. -
The
.entry holds only generic code. It re-exports vitest, and it provides generic factories, fakes for the clock and ids, and generic matchers. It has no workspace dependency and no domain type. Nothing in.knows what a module, an action, a permission or an event is. -
Domain helpers live in the owning package’s
./testingentry, built on@stackra/testing. The helpers of this class arecreateTestingModule,run, the actor helpers,toRefuseWith,toEmit,toHavePermissionandtoMatchManifest. Each one lives in the./testingentry of the package that owns the thing it tests. A package never reaches into another package’s./testingentry. -
Mock hoisting is configured in the preset. The shared vitest preset (
packages/tooling/vitest/src/preset.ts) is set so thatvi.mockcalls in a test file hoist when the file imports from@stackra/testing. A test in@stackra/testingproves this. If that test fails, the work stops and returns to the owner. There is no fallback to globalvi(globals: true). -
The entry names are fixed here, and the tier they serve is not.
./contractand./e2eare named for the.contractand.e2etiers of 0182. Which kind of test uses which entry is set in the test-by-kind table (standards/testing-by-kind), and the contents of each entry are set by the package build. -
Named exception to 0126 decision 2. Decision 2 of 0126 lists the entries a
package may have:
.,./contracts,./node,./react,./native,./testingand./devtools. For@stackra/testingalone, the entries./e2eand./contractare added to that list, with these names. Nothing else in the entry list changes, and no other package gets these entries.@stackra/testing/contractis a subpath of the testing package; it is unrelated to a member’s./contractsentry (the vocabulary entry of 0117), and the two names do not mean the same thing.
Consequences
- A package’s tests import its domain helpers from its own
./testingentry, so a helper is never copied between packages. @stackra/testingstays free of domain dependencies, so it can sit at the bottom of the dependency graph for tests.- Each package that owns a contract grows a
./testingentry when it first needs one. Until then it has none. - The mock-hoisting test is a gate: if the preset changes and hoisting breaks, the change fails before any package relies on it.
Enforcement
planned — the hoisting test inpackages/developer/testing/src/__tests__/ (built in Phase T, item T1) proves decision 4. The
exports of each package and the ./testing placement are checked by checkEntriesMatchExports in
tools/scripts/validate-repository.mjs once the structure work (STA-220) lands. Nothing checks decision 3 today.