-
Notifications
You must be signed in to change notification settings - Fork 7
Contracts and verification
Summary: This page describes the StartER verification strategy, based on Contract-Driven Development for the API and React Testing Library for the interface.
In StartER, tests are not just for catching bugs; they serve as AI guardrails and living documentation. By defining the API behavior declaratively, you provide a clear specification that AI agents can use to generate and verify code.
Tip
Run the full suite with:
npm run testInstead of writing imperative test files, StartER uses a contract-based approach.
This file is the single source of truth for your API. Because it is a structured TypeScript object, AI agents can parse it to understand exactly what they need to build.
// Example contract for reading an item
const contracts = {
items: {
read: {
method: "get",
path: "/api/items/1",
cases: {
success: {
request: {},
response: { status: 200, body: { id: 1, title: "My Item", user_id: 1 } },
},
not_found: {
specialPath: "/api/items/NaN",
request: {},
response: { status: 404, body: {} },
},
}
}
}
};- Context-rich: an AI agent reading this file knows all handled error cases (401, 404, 400) at a glance.
- Self-correction: if the AI generates an action that returns a 404 when it should return a 200, the contract test will fail. You can then feed the failure back to the AI for instant correction.
-
Consistency: the test runner (
tests/api/contracts.test.ts) ensures that every contract is tested the same way, preventing AI from introducing "test drift."
A major strength of StartER lies in using in-memory SQLite for verification:
-
Perfect isolation: each API test starts with a fresh, independent database in memory (
:memory:). One test can never corrupt another. -
Schema compliance: the schema (
schema.sql) is applied before each scenario. You are always testing against the real structure. - Speed: near-instant execution because no disk access is required.
React tests are located in tests/react/ and focus on user behavior.
-
React Testing Library: interact with the DOM semantically (
getByRole,userEvent.click). - Data mocks: the cache system is simulated using contract data, allowing the UI to be tested in isolation from the network.
Example component test:
it("should display an item", async () => {
await renderWithStub(
"/items/:id",
ItemShow,
[`/items/${allItems[0].id}`],
{ me: fooUser },
);
await screen.findByRole("heading", { level: 1, name: allItems[0].title });
// Verifies the component "called" the contract
expectContractCall("items", "read", "success");
});-
Contract first: always update
contracts.tsbefore asking the AI to implement the logic. - Realistic data: use data close to reality in your mocks so the AI has good examples of what the data looks like.
-
Snake case: name your scenarios in snake_case (e.g.,
bad_request,unauthorized) for consistency.
AI co-creation
Getting started
Explanations
How-To Guides
Reference
Digging deeper