@dawn-ai/testing

Use this when

Start here for programmatic tests of Dawn agents, tools, middleware, workspaces, stores, and Agent Protocol behavior. Use @dawn-ai/sdk/testing instead when authoring route-scoped scenario files for dawn test.

Install and import

bash
pnpm add -D @dawn-ai/testing vitest
ts
import { createAgentHarness, expectFinalMessage, script } from "@dawn-ai/testing"

Compatibility and audience

SurfaceRuntimePurityAudienceStability
@dawn-ai/testingnode-onlynot-claimedtestingsupported

createAgentHarness() temporarily changes process-wide OPENAI_BASE_URL and OPENAI_API_KEY, resets runtime caches, and restores them on awaited close. Do not run concurrent harnesses in one process.

Public exports

@dawn-ai/testing

ExportResponsibility
AimockControl an aimock server.
createAimockStart an aimock server.
runCheckpointerConformanceRegister checkpointer conformance tests.
fakeEmbedderCreate deterministic test embeddings.
AimockFixtureDescribe one model fixture.
AimockResponseDescribe a fixture response.
AimockToolCallDescribe a fixture tool call.
FixtureSetCollect aimock fixtures.
ScriptBuilderBuild fixtures fluently.
scriptStart a fixture script.
loadFixturesLoad fixtures from disk.
writeFixturesWrite fixtures to disk.
AgentHarnessDrive an agent route in-process.
AgentHarnessOptionsConfigure an agent harness.
createAgentHarnessCreate an agent harness.
ThreadAccessCheckSpecDescribe one thread-access case to exercise.
ThreadAccessHarnessDrive a thread access policy in-process.
createThreadAccessHarnessCreate a thread access harness.
AgentProtocolInjectorDrive Agent Protocol requests in-process.
createAgentProtocolInjectorCreate an Agent Protocol injector.
InjectResultDescribe an injected response.
expectFinalMessageAssert final output.
expectInterruptAssert an interrupt.
expectNoInterruptAssert no interrupt.
expectNoToolErrorsAssert successful tool results.
expectOffloadedAssert result offloading.
expectPlanAssert plan state.
expectStateAssert route state.
expectStreamedTokensAssert token streaming.
expectSubagentAssert subagent behavior.
expectSystemPromptAssert the effective system prompt.
expectToolCalledAssert a tool call.
expectToolSequenceAssert tool-call order.
InterruptInfoDescribe a collected interrupt.
SubagentEventDescribe a subagent event.
SubagentRunDescribe a collected subagent run.
TodoDescribe a collected plan item.
seedMemorySeed a memory store for a test.
runMemoryStoreConformanceRegister memory-store conformance tests.
createMiddlewareHarnessCreate a middleware harness.
MiddlewareHarnessDrive middleware with real request shapes.
PermissionsStoreInitConfigure permissions conformance setup.
runPermissionsStoreConformanceRegister permissions-store conformance tests.
RecordOptionsConfigure the standalone recorder.
recordSpawn the aimock recording CLI.
AgentRunResultDescribe one collected agent run.
collectRunResultCollect a runtime stream.
deriveToolResultsCorrelate tool results with calls.
ObservedToolCallDescribe an observed tool call.
ObservedToolResultDescribe an observed tool result.
createSubprocessAppStart a real Dawn dev subprocess.
SubprocessAppControl a Dawn subprocess.
runThreadsStoreConformanceRegister threads-store conformance tests.
createToolHarnessCreate a tool harness.
ToolHarnessInvoke a tool with Dawn context.
ToolHarnessOptionsConfigure a tool harness.
createWorkspaceHarnessCreate a temporary workspace harness.
WorkspaceHarnessControl a test workspace.
WorkspaceHarnessOptionsConfigure workspace permissions.

Key contracts

AgentHarnessOptions

ts
export interface AgentHarnessOptions {
  readonly appRoot: string
  readonly route: string
  readonly fixtures?: FixtureSet
  readonly live?: boolean
  readonly record?: boolean
  readonly recordUpstream?: string
}

Fields: @dawn-ai/testing#.:AgentHarnessOptions

FieldTypeRequiredDescription
readonly appRootstringyesSelect the Dawn application.
readonly routestringyesSelect the route key.
readonly fixturesFixtureSetnoSeed mock responses.
readonly livebooleannoProxy to a live OpenAI endpoint.
readonly recordbooleannoCapture upstream traffic for replay.
readonly recordUpstreamstringnoOverride the record-mode upstream.
ts
export interface AgentHarness {
  readonly baseUrl: string
  run(opts: { input: string; fixtures?: FixtureSet | ScriptBuilder }): Promise<AgentRunResult>
  resume(opts: {
    resume: readonly DawnResumeEntry[]
    fixtures?: FixtureSet | ScriptBuilder
  }): Promise<AgentRunResult>
  reset(): void
  close(): Promise<void>
  [Symbol.asyncDispose](): Promise<void>
  getRecordedFixtures(): FixtureSet
}
ts
export declare function createAgentHarness(options: AgentHarnessOptions): Promise<AgentHarness>
ts
export interface ScriptBuilder {
  user(text: string): ScriptBuilder
  callsTool(name: string, args: Record<string, unknown>, opts?: { id?: string }): ScriptBuilder
  replies(content: string): ScriptBuilder
  build(): FixtureSet
}
ts
export declare function writeFixtures(path: string, fixtures: FixtureSet | ScriptBuilder): void
ts
export declare function loadFixtures(path: string): FixtureSet
ts
export declare function runMemoryStoreConformance(opts: {
  readonly name: string
  readonly makeStore: () => Promise<MemoryStore> | MemoryStore
  readonly describe: (name: string, fn: () => void) => void
  readonly close?: (store: MemoryStore) => Promise<void> | void
}): void
ts
export declare function runCheckpointerConformance(opts: {
  readonly name: string
  readonly makeSaver: () => Promise<BaseCheckpointSaver> | BaseCheckpointSaver
  readonly describe: (name: string, fn: () => void) => void
  readonly close?: (saver: BaseCheckpointSaver) => Promise<void> | void
  readonly supports?: {
    /** `list()` hydrates `pendingWrites` (SQLite: no). */
    readonly listPendingWrites?: boolean
    /** `list({ filter })` narrows by metadata (SQLite: no). */
    readonly listFilter?: boolean
  }
}): void
ts
export declare function runPermissionsStoreConformance(opts: {
  readonly name: string
  readonly makeStore: (init: PermissionsStoreInit) => Promise<PermissionsStore> | PermissionsStore
  readonly describe: (name: string, fn: () => void) => void
  readonly close?: (store: PermissionsStore) => Promise<void> | void
}): void
ts
export declare function runThreadsStoreConformance(opts: {
  readonly name: string
  readonly makeStore: () => Promise<ThreadsStore> | ThreadsStore
  readonly describe: (name: string, fn: () => void) => void
  readonly close?: (store: ThreadsStore) => Promise<void> | void
}): void

Behavior contract testing.fake-embedder.deterministic

With positive dimensions, inputs containing supported tokens are unit-length and deterministic. Empty or tokenless inputs produce a zero vector; fakeEmbedder({ dims: 0 }) produces an empty vector.

Embedder defaults

ts
export declare function fakeEmbedder(opts?: { readonly dims?: number }): Embedder

The default is 64 dimensions.

Behavior contract testing.harness-isolation

AgentHarness reset starts a fresh scenario and clears prior fixtures; close is idempotent and async disposal delegates to it.

Harness and fixture lifecycle

Fixtures accumulate until reset(), and the first match wins. reset() starts a new harness thread and replaces fixtures; it does not erase every application store. Live and record are separate modes. getRecordedFixtures() returns only the latest run. The standalone record() helper spawns the aimock CLI and is not harness record mode.

All four run*Conformance helpers register Vitest suites. Call them inside a test module with a fresh makeStore; they do not start Docker or Postgres. createWorkspaceHarness() is permissive by default and is not an execution sandbox. fakeEmbedder() is test data, not a production semantic model.

ts
import { createAgentHarness, expectFinalMessage, script } from "@dawn-ai/testing"
 
await using harness = await createAgentHarness({
  appRoot: process.cwd(),
  route: "/support#agent",
})
const result = await harness.run({
  input: "Say hello",
  fixtures: script().user("Say hello").replies("Hello!")
})
expectFinalMessage(result).toContain("Hello")

Continue with Agent Test Harness, Fixtures and Recording, and Scenario Testing.