Skip to main content
The backend test suite is built on Vitest and uses a hybrid layout: tests that belong to a specific module live beside the source in backend/src/**/*.test.ts, while shared support utilities, repository contracts, and cross-boundary integration workflows live centrally under backend/test/.

Test Layout

backend/
├── src/
│   └── **/*.test.ts          # source-adjacent unit tests
└── test/
    ├── contracts/
    │   ├── repository-contract.ts          # shared contract definition
    │   ├── in-memory-repositories.test.ts  # contract run against in-memory adapters
    │   └── sql-repositories.test.ts        # contract run against D1/SQLite via Miniflare
    ├── integration/
    │   └── llm-mcp-client.test.ts          # end-to-end MCP workflow
    └── support/
        ├── D1Miniflare.ts      # SQL test harness (Miniflare D1)
        ├── HttpApiTest.ts      # HTTP API test helpers
        └── McpMiniflare.ts     # MCP Miniflare client factory
Vitest is configured in backend/vitest.config.ts to include both trees:
export default defineConfig({
  test: {
    environment: "node",
    include: ["src/**/*.test.ts", "test/**/*.test.ts"],
    setupFiles: ["./vitest.setup.ts"],
  },
});

In-Memory vs SQL Contract Tests

The repository contract is defined once in test/contracts/repository-contract.ts and then run against two different adapter implementations, making it straightforward to verify that both adapters satisfy the same behavioral guarantees.
Provides InMemoryCoffeeRepositoriesLive to the Effect runtime — no database or external process required:
// test/contracts/in-memory-repositories.test.ts
import * as Effect from "effect/Effect";
import { InMemoryCoffeeRepositoriesLive } from "#external/live";
import { defineRepositoryContract } from "./repository-contract.ts";

defineRepositoryContract("in-memory repositories", (effect) =>
  Effect.runPromise(effect.pipe(Effect.provide(InMemoryCoffeeRepositoriesLive))),
);
The shared contract verifies list, find-by-id, round-trip save, upsert, and status-filtered listing — the same assertions run against both adapters.

Miniflare Worker Test for MCP HTTP

The file backend/src/presentation/mcp/miniflare.worker.test.ts exercises the full MCP HTTP surface end-to-end inside a Cloudflare Worker environment using Miniflare. It validates tools, resources, prompts, and protocol headers without deploying to Cloudflare. Run it directly:
bun run --cwd backend test src/presentation/mcp/miniflare.worker.test.ts
The suite covers three scenarios:
  • Catalog surface — asserts the expected tool names, resource URIs, and prompt names returned by tools/list, resources/list, and prompts/list
  • Order resources — places a latte order via tools/call and reads it back through resources/read
  • Prompts and protocol headers — fetches the recommend-drink prompt and checks the Mcp-Protocol-Version response header
// snapshot of the expected MCP surface from the test
assert.deepStrictEqual(toolNames, [
  "cancel_order",
  "get_order",
  "list_menu",
  "list_orders",
  "mark_ready",
  "pick_up_order",
  "place_order",
  "start_brewing",
]);
assert.deepStrictEqual(resourceUris, ["coffee://menu", "coffee://orders/open"]);
assert.deepStrictEqual(promptNames, ["recommend-drink", "summarize-open-orders"]);

Running Tests

All tests

bun run test
Runs the full Vitest suite across all workspaces via Turborepo.

Affected tests only

bun run test:affected
Uses Turborepo’s --affected flag to run tests only in workspaces changed since the last build.

Frontend and Storybook Tests

The ui/ workspace uses Storybook for component-level testing. Run the Storybook test suite from the repo root:
bun run test:storybook
This invokes turbo run test --filter=ui.

Full Quality Check Suite

The check script runs typechecking, linting (standard and custom), format verification, and tests in a single command:
bun run check
This expands to:
turbo run typecheck lint lint:custom fmt:check test
To run the same suite but skip workspaces unaffected by your current branch:
bun run check:affected
bun run check:affected is the fastest way to validate your changes before pushing. Turborepo hashes task inputs and skips any workspace whose source files have not changed.

Build docs developers (and LLMs) love