api_tests/README.md
A fully‑featured API test harness that spins up Typesense in both single‑ and multi‑node modes, executes phase‑scoped test suites, and tears everything down again. Each logical phase of testing (fresh start, restart, snapshot restore, etc.) is completely isolated and reproducible.
| Path | Phase | Description |
|---|---|---|
| Single-node | SINGLE_FRESH | A brand-new Typesense server is started. |
SINGLE_RESTARTED | Same server is stopped and started again using the same data directory. | |
SINGLE_SNAPSHOT | A snapshot is taken, the server is restarted, and the snapshot is restored. | |
| Multi-node (3-node RAFT) | MULTI_FRESH | A fresh 3-node cluster is started. |
MULTI_RESTARTED | Every node is gracefully stopped and restarted. | |
MULTI_SNAPSHOT | A snapshot is taken on the leader, the cluster is restarted, and the snapshot is restored on all nodes. |
The phases within a given path run sequentially because each later phase re‑uses the data produced by the previous one. The single‑node and multi‑node paths run in parallel to reduce build time.
[!NOTE] Single-node server is always run on 8108 when running api_tests
[!NOTE] Multi-node server is always run on 5108, 6108, 7108 when running api_tests
Create a *.test.ts file under api_tests/tests/ and declare only the describe blocks you need. For instance, if your feature must persist across a single‑node restart but does not require snapshots or clustering, you might only target SINGLE_FRESH + SINGLE_RESTARTED.
import { describe, it, expect } from "bun:test";
import { Phases } from "../src/constants";
import { fetchSingleNode } from "../src/request";
// 1️⃣ Seed data in the fresh phase
describe(Phases.SINGLE_FRESH, () => {
it("create collection", async () => {
const res = await fetchSingleNode("/collections", {
method: "POST",
body: JSON.stringify({
name: "companies",
fields: [{ name: "company_name", type: "string" }],
}),
});
expect(res.ok).toBe(true);
});
});
// 2️⃣ Assert persistence after restart
describe(Phases.SINGLE_RESTARTED, () => {
it("collection should persist", async () => {
const res = await fetchSingleNode("/collections/companies");
expect(res.status).toBe(200);
});
An template file containing all the phases,
describe(Phases.SINGLE_FRESH, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
describe(Phases.SINGLE_RESTARTED, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
describe(Phases.SINGLE_SNAPSHOT, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
describe(Phases.MULTI_FRESH, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
describe(Phases.MULTI_RESTARTED, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
describe(Phases.MULTI_SNAPSHOT, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
Also, you can use a describe with NO_PHASE when you can entirely manage the lifecyle of the typesense-server using TypesenseProcessManager
describe(Phases.NO_PHASE, () => {
it("test 1", async () => {
...
});
it("test 2", async () => {
...
});
});
Please refer to collections.test.ts and health.test.ts for reference tests. These examples are the quickest way to copy‑paste a skeleton for new features.
All cluster‑level operations (snapshot creation, server restart, cluster restart) are performed between phases by TypesenseProcessManager. That means any state you need in a later phase must be created in an earlier one under the same path:
SINGLE_RESTARTED? Create it during SINGLE_FRESH.MULTI_SNAPSHOT? Index them during MULTI_FRESH or MULTI_RESTARTED.Phases are therefore best thought of as a progressive timeline rather than independent test shards.
fetchSingleNode(path: string, init?: RequestInit): Promise<Response>http://localhost:8108.X-TYPESENSE-API-KEY: xyz.const res = await fetchSingleNode("/health");
fetchMultiNode(node: 1 | 2 | 3, path: string, init?: RequestInit): Promise<Response>// Query the leader (node 1)
await fetchMultiNode(1, "/collections/companies");
Happy testing! 🎉