Testing & contract tests
In-process test client
Every App exposes a request() method that round-trips a fetch Request through the same pipeline real traffic uses — no socket, no port:
import test from "node:test";
import assert from "node:assert/strict";
import { app } from "../src/server.js";
test("GET /books/1 returns 200", async () => {
const res = await app.request("/books/1");
assert.equal(res.status, 200);
assert.equal((await res.json()).title, "Foundation");
});
test("POST /books rejects unauthorized", async () => {
const res = await app.request("/books", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ title: "Dune" }),
});
assert.equal(res.status, 401);
});Mock mode
For pure-contract testing (no DB, no side effects), enable mockMode. DaloyJS will return the first declared examples entry from your response schema without ever invoking your handler:
const app = new App({ mockMode: true });
app.route({
method: "GET",
path: "/users/:id",
operationId: "getUser",
responses: {
200: {
description: "ok",
body: z.object({ id: z.string(), name: z.string() }),
examples: { default: { id: "u_1", name: "Alice" } },
},
},
handler: async () => { throw new Error("not called in mock mode"); },
});Contract test runner
runContractTests walks your registered routes and verifies that every declared example validates against its schema, every operationId is unique, and there are no obvious anti-patterns:
import { runContractTests } from "@daloyjs/core/contract";
const report = await runContractTests(app, {
requireOperationId: true,
allowBodyOnSafeMethods: false,
});
if (!report.ok) {
console.error(report.issues);
process.exit(1);
}
console.log(`${report.checked} routes — all clean`);The report flags:
- Routes missing
operationId. - Duplicate operationIds.
- Examples that don't match their schemas.
- Body schemas declared on safe methods (
GET,HEAD,DELETE). - Routes with no declared
responses.
Wire into CI
{
"scripts": {
"test": "node --import tsx/esm --test tests/**/*.test.ts",
"test:contract": "node --import tsx/esm scripts/contract.ts"
}
}