Initial Commit
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { SurrealAdapter } from "@suaveplan/surrealdb";
|
||||
import {
|
||||
afterAll,
|
||||
beforeAll,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
} from "@suaveplan/testing/runner";
|
||||
import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb";
|
||||
import { SurrealExportStore } from "../../src/store/surreal/surreal-store.js";
|
||||
import type { ExportJob } from "../../src/types/types.js";
|
||||
|
||||
let fixture: SurrealDBFixture | null = null;
|
||||
let adapter: SurrealAdapter | null = null;
|
||||
let migration: string | null = null;
|
||||
|
||||
const now = new Date("2026-05-19T00:00:00.000Z").toISOString();
|
||||
|
||||
function mk(id: string, overrides: Partial<ExportJob> = {}): ExportJob {
|
||||
return {
|
||||
id,
|
||||
format: "csv",
|
||||
resource: "users",
|
||||
status: "completed",
|
||||
filters: undefined,
|
||||
filename: undefined,
|
||||
totalRows: null,
|
||||
exportedRows: null,
|
||||
fileSize: null,
|
||||
error: null,
|
||||
createdAt: now,
|
||||
completedAt: null,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
beforeAll(async () => {
|
||||
fixture = new SurrealDBFixture();
|
||||
const cfg = await fixture.start();
|
||||
adapter = new SurrealAdapter({
|
||||
url: cfg.url,
|
||||
namespace: cfg.namespace,
|
||||
database: cfg.database,
|
||||
username: cfg.username,
|
||||
password: cfg.password,
|
||||
});
|
||||
await adapter.connect();
|
||||
migration = await readFile(
|
||||
join(__dirname, "..", "..", "surql", "tables", "export_job.surql"),
|
||||
"utf8"
|
||||
);
|
||||
await adapter.query(migration);
|
||||
}, 180_000);
|
||||
|
||||
afterAll(async () => {
|
||||
if (adapter !== null) {
|
||||
await adapter.disconnect();
|
||||
adapter = null;
|
||||
}
|
||||
if (fixture !== null) {
|
||||
await fixture.stop();
|
||||
fixture = null;
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
describe("SurrealExportStore — integration against real SurrealDB", () => {
|
||||
it("set + get round-trips a job through SurrealDB", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
if (adapter === null) throw new Error("fixture not ready");
|
||||
const store = new SurrealExportStore({ db: adapter });
|
||||
await store.clear();
|
||||
const job = mk("rt1", { status: "pending" });
|
||||
await store.set("rt1", job);
|
||||
const round = await store.get("rt1");
|
||||
expect(round).not.toBeNull();
|
||||
expect(round?.format).toBe("csv");
|
||||
expect(round?.resource).toBe("users");
|
||||
expect(round?.status).toBe("pending");
|
||||
expect(round?.createdAt).toBe(now);
|
||||
});
|
||||
|
||||
it("has reflects set / delete against SurrealDB", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
if (adapter === null) throw new Error("fixture not ready");
|
||||
const store = new SurrealExportStore({ db: adapter });
|
||||
await store.clear();
|
||||
await store.set("h1", mk("h1"));
|
||||
expect(await store.has("h1")).toBe(true);
|
||||
await store.delete("h1");
|
||||
expect(await store.has("h1")).toBe(false);
|
||||
});
|
||||
|
||||
it("delete on a missing row is idempotent", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
if (adapter === null) throw new Error("fixture not ready");
|
||||
const store = new SurrealExportStore({ db: adapter });
|
||||
await store.clear();
|
||||
await expect(store.delete("never-existed")).resolves.toBeUndefined();
|
||||
});
|
||||
|
||||
it("list applies status filter and pagination through SurrealDB", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
if (adapter === null) throw new Error("fixture not ready");
|
||||
const store = new SurrealExportStore({ db: adapter });
|
||||
await store.clear();
|
||||
await store.set("l1", mk("l1", { status: "completed" }));
|
||||
await store.set("l2", mk("l2", { status: "completed" }));
|
||||
await store.set("l3", mk("l3", { status: "failed" }));
|
||||
const page = await store.list({
|
||||
status: "completed",
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
});
|
||||
expect(page.total).toBe(2);
|
||||
expect(page.items).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("clear empties the SurrealDB table", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
if (adapter === null) throw new Error("fixture not ready");
|
||||
const store = new SurrealExportStore({ db: adapter });
|
||||
await store.set("c1", mk("c1"));
|
||||
await store.clear();
|
||||
const page = await store.list({ limit: 10, offset: 0 });
|
||||
expect(page.total).toBe(0);
|
||||
});
|
||||
|
||||
it("export_job migration is idempotent", {
|
||||
timeout: 60_000,
|
||||
}, async () => {
|
||||
if (adapter === null || migration === null)
|
||||
throw new Error("fixture not ready");
|
||||
// Re-apply the migration; OVERWRITE semantics make it idempotent.
|
||||
await expect(adapter.query(migration)).resolves.toBeDefined();
|
||||
await expect(adapter.query(migration)).resolves.toBeDefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user