feat: H3 — reusable auth System + the testing seam (stubLoad, bootstrapService) - #39
Merged
Merged
Conversation
…spec design: docs/design/10-domains/testing.md — the two-altitude testing model. Every dependency flows through one seam, service.load(); stubLoad replaces its output (unit), bootstrapService feeds its input (integration), both without touching the code under test. drive: the H3 slice spec — reusable auth System (owns its db), the two testing utilities (stubLoad in core, bootstrapService via a runForTest runnable seam), the fake shipped from the auth package, and the three proofs (unit, integration, live e2e). Two open items flagged for an operator ruling before dispatch. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
Operator rulings: teardown via bun-test process isolation (no close(), server.ts untouched); both the unit (stubLoad) and integration (bootstrapService) paths are deliverables, integration boots the full Next storefront against a loopback fake auth with a minimal-RPC-consumer fallback. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
auth's default export is now a System (src/system.ts) that provisions its own Postgres and the compute service, wiring db in and exposing only the rpc contract — no db input on the boundary. The root system.ts no longer provisions the database; it provisions the auth System and wires its exposed rpc into storefront. The package declares @prisma/* as peer dependencies, standing in for how a published reusable System would ship. Part of the H3 slice (reusable-system-testing). Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
@prisma/app/testing exports stubLoad(service, overrides): a service node whose load() returns overrides merged with the service's own param defaults, typed against the service's declared deps so a double of the wrong shape fails to compile. Target-agnostic, no module mocking of its own — that stays in the test. New ./testing tsdown entry regenerates the package manifest. Part of the H3 slice (reusable-system-testing). Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
compute()'s runnable node gets runForTest(config, boot): stash the given
Config address-free (the same writer run() uses, minus the address ->
Config deserialize step) and call boot(). @prisma/app/testing wraps it
generically as bootstrapService(service, config): dynamically imports the
service's real entry (build.entry resolved against build.module, exactly
as the deploy bootstrap does) and hands back { url, fetch }. No close() —
teardown rides bun-test's per-file process isolation. server.ts is
untouched.
Part of the H3 slice (reusable-system-testing).
Signed-off-by: willbot <w.a.madden+machine@gmail.com>
Signed-off-by: Will Madden <madden@prisma.io>
Auth ships a /fake export: an in-memory verify with no Postgres, sharing the real authContract so the handler map is typed against the same contract the real service exposes. Storefront gets both proof tests: a unit test renders page.tsx with load() stubbed to the fake via stubLoad (vitest, no server, no environment), and an integration test drives the real Next.js standalone entry through bootstrapService against the fake served on a loopback port (bun test — needs Bun.serve, and the resolved no-close() teardown decision rests on bun-test's per-file process isolation). bootstrapService gains an optional third `boot` argument: its default derivation (build.entry resolved against build.module) fits a `node` build adapter, but nextjs's entry is a bare filename inside the Next standalone output, not module-relative, so the integration test supplies its own boot thunk built from @prisma/app-nextjs/control's nextStandaloneDir — the same path math the deploy assembly step uses. This makes the integration test boot the *real* Next.js storefront in-process rather than falling back to a minimal RPC consumer. Part of the H3 slice (reusable-system-testing). Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
@prisma/alchemy
@prisma/app
@prisma/app-assemble
@prisma/app-cli
@prisma/app-cloud
@prisma/app-nextjs
@prisma/app-node
@prisma/app-rpc
prisma-app
commit: |
wmadden
requested changes
Jul 11, 2026
Collaborator
There was a problem hiding this comment.
You need to clarify the purpose of this file. Its content is so unrealistic it's impossible to infer what it's for, which IIUC is: use this fake auth service as a dummy service to test a system which depends on it. Is that right?
It probably doesn't belong in package src/; you wouldn't want it to reach production code. It ought to be a separate entrypoint under, eg, test.
| vi.mock('../src/service.ts', async () => { | ||
| const actual = await vi.importActual<{ default: typeof Service }>('../src/service.ts'); | ||
| return { | ||
| default: stubLoad(actual.default, { |
Collaborator
There was a problem hiding this comment.
stubLoad() is spectacularly hard to read. How about mockService(service, overrides)?
| blindCast< | ||
| RunnableServiceNode<D, typeof computeParams, E>, | ||
| "the spread copies node's own enumerable data (including the Symbol.for brand) and adds run/load — exactly RunnableServiceNode's shape" | ||
| RunnableServiceNode<D, typeof computeParams, E> & Testable, |
Collaborator
There was a problem hiding this comment.
Wtf is this? Why does our production code have testing code in it????
Comment on lines
+64
to
+67
| async runForTest<T>(config: Config, boot: () => Promise<T>): Promise<T> { | ||
| stash(configOf(node), config); | ||
| return boot(); | ||
| }, |
…oduction, mockService, doc truth Your review (CHANGES_REQUESTED) + the Opus pass: - Move the integration seam OUT of production code. `compute()` no longer carries runForTest/Testable — it ships only run/load. `bootstrapService` moves to a new target-specific `@prisma/app-cloud/testing`, doing stash+boot directly via the internal `stash` + `configOf`. `mockService` (the unit seam, renamed from the hard-to-read `stubLoad`) stays in core `@prisma/app/testing`. - The fake moves out of production `src/` to a test-only entrypoint (`auth/testing/fake.ts`, still exported as `./fake`) with its purpose spelled out; storefronts production `next build` no longer type-checks test files. - CodeQL: drop the regex comment-strip in the integration test; assert the exact render instead. - Opus: rewrite `testing.md`s `bootstrapService` example to the shipped API (real Config shape, no `close()`, an explicit Next boot thunk, target-specific); single-source the Next standalone entry path (`standaloneEntryPath`, shared by `assemble` + the test); wire `test:types` into CI. Gates green (typecheck, test, test:types, lint, build, casts delta 0); both proof tests run live (unit + Next-standalone integration). Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
…0.22 bumps Integrates main: dep bumps (next 15->16, tsdown 0.15->0.22, @types/node 25), ADR-0018/0019/0020 (params/cron), the forcing-function-apps project. Conflicts resolved: - storefront tsconfig: main include (+.next/dev/types) with our test-file excludes (production next build must not type-check tests). - storefront package.json: main @types/node/@types/react bumps, keeping our @types/bun (the bun integration test needs it). Re-verified on the merged tree: gates green (typecheck, test, test:types, lint, build, casts delta 0); both proof tests run live — the integration test boots the real Next 16 standalone server against the fake auth. (A stale local rolldown binary from the incremental install broke the build; a clean reinstall fixed it — CI installs fresh.) Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
The merge auto-merged a broken lockfile (missing the vitest peer-hash entry after the @types/node bump); it was committed before the clean reinstall regenerated it. Regenerate so --frozen-lockfile passes in CI. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
wmadden
approved these changes
Jul 11, 2026
…isma-apps skill - Rewrite docs/design/10-domains/testing.md: lead with the decision, open with a worked example, build the topic up plainly, alternatives-considered at the end. Drops the contrived "replace/feed the seam" phrasing. - Add skills/testing-prisma-apps: a user-facing how-to for mockService (unit) and bootstrapService (integration). Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
wmadden-electric
force-pushed
the
claude/h3-reusable-system-testing
branch
from
July 11, 2026 17:12
67b1cee to
0f84d56
Compare
wmadden-electric
added a commit
that referenced
this pull request
Jul 11, 2026
Reconcile H3's testing seam (#39: mockService, bootstrapService) with S1's config()/load() split and schema-typed params: - mockService now sets load() (deps) and config() (params) separately, splitting its flat override by dependency vs param key; drops the removed Loaded type. - bootstrapService calls stash(node, config) (the node-based signature), not the old shape argument. - testing.test.ts / testing.test-d.ts migrate off { type: 'string' } to string()/number() and read params through config(). - Commit the ./testing export both packages' tsdown config already emits. Signed-off-by: willbot <w.a.madden+machine@gmail.com> Signed-off-by: Will Madden <madden@prisma.io>
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.
The last slice of the system-composition project (H3): a reusable System and the testing seam that makes apps built on the framework testable, both proven.
What lands
system('auth', { expose: { rpc } }, …)provisions its own Postgres and the auth service internally and exposes only the RPC contract;@prisma/*are peer dependencies (publish-shaped). The app root composes it and wiresauth.rpcinto storefront — it no longer touches Postgres.@prisma/app/testing → stubLoad— the unit seam. Mocksservice.load()to return doubles typed against the service's own deps (a wrong-shaped double is a compile error). Test any code that pulls deps throughload()— a page, an action — with no server and no environment.bootstrapService— the integration seam. The in-process counterpart of the deploy bootstrap (runForTest(config, boot)reuses the samestashwriter): boots the real entry with a config you choose and hands back a driveable server.Both rest on one fact — every dependency flows through
service.load()— documented in testing.md.Proof
page.tsxwithload()stubbed to a fake auth (vitest).All repo gates green (typecheck, test, lint, build, casts delta 0).