What to boot, and what to substitute.
A booted test client.
import { createTestApp } from '@stone-js/testing'
// Subpath on purpose: the event factories are platform-specific, and an agnostic application
// should not carry the HTTP one. `makeIncomingEvent` (from the main entry) is NOT a substitute:
// it builds the generic event, with no URL and no HTTP methods.
import { makeIncomingHttpEvent } from '@stone-js/testing/http'
const app = await createTestApp() // discovers app/**
const app = await createTestApp({ appDir: 'src' }) // another layout
const app = await createTestApp({ modules: [Application, Tasks] }) // exactly these
const app = await createTestApp({ bindings: { clock: fixedClock } })
const response = await app.send(makeIncomingHttpEvent({ method: 'GET', url: '/tasks' }))
expect(response.statusCode).toBe(200)
expect(response.json()).toEqual([{ id: 1 }])
Boots a Stone.js application in-memory for testing.
It runs the real bootstrap (blueprint introspection, providers, hooks) via
StoneFactory, but swaps in the TestAdapter so nothing binds a port. You get a TestClient whosesend(event)dispatches through the full kernel — the same path production uses.Called with no modules, it discovers them, from the same files the CLI builds. That is not a convenience: a hand-written list drifts, and it drifts silently. A forgotten handler answers 404 and reads as a routing bug; a forgotten
@Configurationmakes the whole suite validate behaviour production does not have. Listing modules explicitly stays possible, as an override.