import { type UrbanApp } from "@nanobpm/urban/runtime"; import type { DataLayer, HttpResponse } from "@nanobpm/urban/runtime"; import { type ManualScheduler } from "./manual-scheduler.ts"; import { type ApiDriver, type ApiResponse } from "./openapi-driver.ts"; import { type TestHost } from "./test-host.ts"; import { type WasmEngineClient } from "./wasm-engine.ts"; import { SurfaceCoverage } from "./coverage.ts"; import type { MockWorkerBuilder } from "./worker-mock.ts"; /** A route invocation against the in-process router (no socket is opened). */ export interface RouteRequest { method: string; /** Path portion of the URL, e.g. "/tasks" or "/hooks/order". */ path: string; /** Query parameters, as a record or a prebuilt `URLSearchParams`. */ query?: Record | URLSearchParams; /** Request headers, as a record or a prebuilt `Headers`. */ headers?: Record | Headers; /** Raw request body text. */ body?: string; } /** Drives the app's mounted routes in-process. */ export interface UiDriver { /** Invoke a mounted route directly and return its response. Throws if the app has not * been started (no router mounted yet). */ call(req: RouteRequest): Promise; } /** Options for {@link bootTestApp}. */ export interface BootTestAppOptions { /** Manifest filename relative to `root` (default "nano.app.json"). */ manifestPath?: string; /** Environment overlaid on the real environment for the app under test. */ env?: Record; /** Capture the app's log output into {@link TestApp.logs} instead of the console. */ captureLogs?: boolean; /** Seed the app's data layer after `start()` (before the first `settle`). Receives the * provisioned {@link DataLayer}; may be async. */ seed?: (db: DataLayer) => void | Promise; /** * Enable the coverage-exhaustive gate (S4). When set, {@link TestApp.coverage} is a * {@link SurfaceCoverage} pre-declared with the app's surfaces derived from its manifest * + OpenAPI spec ("operations", "workers"), recording hits automatically as the test * drives the `api` operations and the engine runs jobs. Call * `app.coverage.assertFullCoverage()` at the end of a test to fail on any un-exercised * declared element. Off by default (zero overhead when unused). */ coverage?: boolean; } /** The booted app harness returned by {@link bootTestApp}. */ export interface TestApp { /** The underlying runtime app (manifest, inspect, lifecycle). */ readonly app: UrbanApp; /** The WASM engine backing the app — exposes `snapshot`, `now`, `advanceTime`. */ readonly engine: WasmEngineClient; /** The provisioned data layer (seed/query the app's SQLite through this). */ readonly db: DataLayer; /** Drive mounted HTTP routes in-process. */ readonly ui: UiDriver; /** * Drive the app's OpenAPI operations by `operationId`, derived from the app's own spec (ADR 0059). * Undefined when the app declares no `api` binding — guard with `if (app.api)` for a generic * harness, or assert its presence in an app-specific e2e. */ readonly api: ApiDriver | undefined; /** * Call a raw route (page action, hook, or any exact path) and get a response-parsed result — a * thin wrapper over {@link UiDriver.call}. Available whether or not the app has an `api` binding. */ callRoute(req: RouteRequest): Promise>; /** The virtual-clock scheduler driving the app's background loops. */ readonly scheduler: ManualScheduler; /** * Register (or fetch) a job-worker mock for `taskType` (epic #296, S1). The returned * {@link MockWorkerBuilder} lets a test complete/fail/error a task type — optionally * conditionally via `when(...)` — instead of running the app's real worker handler, while * un-mocked types keep running real code. Opt-in and zero-cost when unused; call the * builder's `.reset()` (or `engine.clearWorkerMock(taskType)`) to restore real behaviour. * Delegates to {@link WasmEngineClient.mockWorker}. */ mockWorker(taskType: string): MockWorkerBuilder; /** * The coverage-exhaustive gate (S4), present only when `bootTestApp` was called with * `{ coverage: true }`. Pre-declared with the app's "operations" (from its OpenAPI spec) * and "workers" (from its manifest) surfaces; hits are recorded automatically as the * test drives `api` operations and the engine runs jobs. Call * `coverage.assertFullCoverage()` to fail on any un-exercised declared element. */ readonly coverage?: SurfaceCoverage; /** Captured log lines (empty unless `captureLogs` was set). */ readonly logs: TestHost["logs"]; /** Current virtual time (ms since epoch). */ now(): number; /** The engine's parsed process snapshot. */ snapshot(): Record; /** * Drive the app to a quiescent fixpoint *at the current virtual time*: drain the engine's * workers and fire every scheduler timer already due, repeating until neither dispatches * more work. Does NOT advance the clock — time only moves via {@link advanceTime}, so a * self-rescheduling poll can never spin `settle` forever. */ settle(): Promise; /** * Advance the virtual clock by `ms`, moving the engine's timers and the scheduler's timers * in lockstep (engine BPMN timers first, then the due background-loop timers), then settle * to a fixpoint. This is the only way time moves — the explicit, deterministic replacement * for waiting on a 15s reconciler poll. */ advanceTime(ms: number): Promise; /** Tear down: stop the app (unsubscribe workers, close DB, close engine). */ stop(): Promise; } /** Join an app root and a spec path the way the runtime's `resolveAppPath` (+ `isAbsolutePath`) does: * an absolute `spec` — POSIX root (`/`), a drive-letter root (`C:\` / `C:/`), or a Windows UNC/ * drive-root backslash (`\`) — is returned as-is; otherwise it joins onto `root` using `root`'s own * separator (backslash if `root` contains one, else `/`), rewriting both segments to that separator * so the result is never mixed (e.g. no `C:\app/openapi.yaml`). Kept in lockstep with the runtime's * canonical resolver so spec loading behaves identically cross-platform (no gen/runtime drift). */ export declare function resolveSpecPath(root: string, spec: string): string; /** * Boot the Urban app rooted at `root` in-process, against the WASM engine and a virtual * clock. `root` must contain the app's manifest and its referenced files (processes, * workers, migrations) on disk — typically a temporary fixture directory. */ export declare function bootTestApp(root: string, opts?: BootTestAppOptions): Promise;