mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
The Node driver takes the count from node:sqlite; the Durable Object driver reads SELECT changes() after the statement, since the cursor's rowsWritten also counts index writes (verified in workerd). run is unchanged and costs nothing more.
73 lines
3.8 KiB
TypeScript
73 lines
3.8 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,
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("Durable Object SQLite driver in workerd", () => {
|
|
// The cursor's `rowsWritten` counts index writes too (3 for this insert, 20 for the bulk update); `runCounted` must not.
|
|
it("reports the rows a statement changed, without index writes", async () => {
|
|
await runInDurableObject(fresh(), async (_instance, state) => {
|
|
const database = new DurableObjectSqliteDatabase(state.storage);
|
|
await database.exec("CREATE TABLE probe (id TEXT PRIMARY KEY, path TEXT NOT NULL, n INTEGER NOT NULL) STRICT, WITHOUT ROWID");
|
|
await database.exec("CREATE INDEX probe_path ON probe (path, id)");
|
|
await database.exec("CREATE INDEX probe_n ON probe (n, id)");
|
|
for (let i = 0; i < 10; i++) await database.run("INSERT INTO probe VALUES (?, ?, ?)", `k${i}`, `a/${i}`, i);
|
|
expect(await database.runCounted("INSERT INTO probe VALUES (?, ?, ?)", "x", "a/x", 99)).toBe(1);
|
|
expect(await database.runCounted("UPDATE probe SET n = 0 WHERE id = ?", "absent")).toBe(0);
|
|
expect(
|
|
await database.runCounted("UPDATE probe SET path = 'c/' || substr(path, 3) WHERE path >= 'a/' AND path < 'a0'"),
|
|
).toBe(10);
|
|
expect(await database.runCounted("DELETE FROM probe WHERE path >= 'c/' AND path < 'c0'")).toBe(10);
|
|
});
|
|
});
|
|
});
|