pi-mono/packages/durable/test-workers/storage.test.ts
Mario Zechner ea7c648430 feat(durable): workerd tests for Durable Object hosting
Runs the storage conformance suite, the schema migration, an alarm-woken sleep across an eviction, and a
turn recovered through the recovery alarm in workerd via @cloudflare/vitest-plugin (npm run test:workers).

fix(durable): decline background timers in Cloudflare Workers. With nodejs_compat, timers have an unref(),
so the idle-memory timers were armed and a pending one kept a Durable Object from hibernating or being evicted.
2026-10-17 23:02:48 +00:00

54 lines
2.6 KiB
TypeScript

import { runInDurableObject } from "cloudflare:test";
import { env } from "cloudflare:workers";
import { DurableObjectSqliteDatabase } from "@earendil-works/chord/sqlite/cloudflare";
import { registerStorageConformance } from "@earendil-works/pi-durable/testing";
import { describe, expect, it } from "vitest";
import { openDurableObjectSqliteStorage } from "../src/storage/sqlite/cloudflare.ts";
import { applySqliteMigrationsForSchema, createSqliteMigrations } from "../src/storage/sqlite/migrations.ts";
import { createSqliteSchema } from "../src/storage/sqlite/schema.ts";
const fresh = () => env.SESSIONS.get(env.SESSIONS.newUniqueId());
let suite = 0;
registerStorageConformance({ describe, expect, it }, "SqliteStorage on Durable Object SQLite in workerd", (use) =>
runInDurableObject(fresh(), async (_instance, state) => {
// Tables of their own, next to the ones the object's Harness opened.
await use(await openDurableObjectSqliteStorage(state.storage, { prefix: `conformance${suite++}_` }));
}),
);
describe("Durable Object SQLite schema", () => {
it("migrates a version 1 schema to admit sleeping tasks, keeping tasks and indexes", async () => {
await runInDurableObject(fresh(), async (_instance, state) => {
const database = new DurableObjectSqliteDatabase(state.storage);
const schema = createSqliteSchema("legacy_");
const tasks = schema.tables.tasks;
const insert = (id: number, status: string) =>
database.run(
`INSERT INTO ${tasks} (id, conversation_id, kind, status, abort_requested, background, record) VALUES (?, 1, 'test', ?, 0, 0, '{}')`,
id,
status,
);
// A database written by a version that knew schema version 1 only.
await applySqliteMigrationsForSchema(database, schema, createSqliteMigrations(schema).slice(0, 1));
await insert(5, "waiting");
await expect(insert(6, "sleeping")).rejects.toThrow();
await openDurableObjectSqliteStorage(state.storage, { prefix: "legacy_" });
expect(await database.get(`SELECT version FROM ${schema.tables.durableSchema}`)).toEqual({ version: 2 });
expect(await database.get(`SELECT status FROM ${tasks} WHERE id = 5`)).toEqual({ status: "waiting" });
await insert(6, "sleeping");
const indexes = await database.all<{ name: string }>(
`SELECT name FROM sqlite_master WHERE type = 'index' AND tbl_name = ? ORDER BY name`,
tasks,
);
expect(indexes.map(({ name }) => name)).toEqual([
schema.indexes.tasksByAbortRequested,
schema.indexes.tasksByBackground,
schema.indexes.tasksByConversation,
schema.indexes.tasksByKind,
schema.indexes.tasksByStatus,
]);
});
});
});