import { mkdtemp, rm, writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { createPluginContext } from "@suaveplan/api-plugin"; import { EventBus } from "@suaveplan/events"; import { afterAll, beforeAll, describe, expect, it, } from "@suaveplan/testing/runner"; import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb"; import { createDatabasePlugin } from "../../src/plugin/plugin.js"; import { provisionTenant } from "../../src/tenancy/tenancy.js"; import type { DatabasePluginConfig } from "../../src/types/types.js"; let fixture: SurrealDBFixture | null = null; let cfg: { url: string; namespace: string; database: string; username: string; password: string; } | null = null; let migrationsDir: string | null = null; beforeAll(async () => { fixture = new SurrealDBFixture(); cfg = await fixture.start(); migrationsDir = await mkdtemp(join(tmpdir(), "apdb-integ-")); await writeFile( join(migrationsDir, "001_init.surql"), "DEFINE TABLE OVERWRITE thing SCHEMAFULL;", "utf8" ); }, 180_000); afterAll(async () => { if (migrationsDir !== null) { await rm(migrationsDir, { recursive: true, force: true }); migrationsDir = null; } if (fixture !== null) { await fixture.stop(); fixture = null; } }, 60_000); const noopLogger = { debug: () => undefined, info: () => undefined, warn: () => undefined, error: () => undefined, }; describe("@suaveplan/api-plugin-database integration", () => { it("full pool + migration + tenancy roundtrip against real SurrealDB", { timeout: 180_000, }, async () => { if (cfg === null || migrationsDir === null) { throw new Error("fixture not ready"); } const plugin = createDatabasePlugin({ url: cfg.url, namespace: cfg.namespace, database: cfg.database, username: cfg.username, password: cfg.password, pool: { min: 1, max: 3, idleMs: 5_000, acquireTimeoutMs: 30_000, }, migrations: { autoRun: true, directory: migrationsDir, ledgerTable: "_migrations_integ", lockTable: "_migration_lock_integ", leaseMs: 60_000, }, }); const services = new Map(); const ctx = createPluginContext({ logger: noopLogger, eventBus: new EventBus(), signal: new AbortController().signal, config: plugin.defaults as DatabasePluginConfig, services, }); await plugin.onInit?.(ctx); await plugin.onReady?.(ctx); const db = ctx.getService("db"); expect(db).toBeDefined(); // Verify the migration body actually ran — the migration creates // a SCHEMAFULL `thing` table; INFO FOR DB should now know it. const dbInfo = await db!.query("INFO FOR DB"); const dbInfoString = JSON.stringify(dbInfo[0] ?? {}); expect(dbInfoString).toContain("thing"); // Tenancy roundtrip — provision a tenant on the shared schema. await provisionTenant(db!, { strategy: "shared" }, "acme"); // Ledger table was bootstrapped — even if the row insert path is // dialect-sensitive on SurrealDB v3, the table itself exists. const ledger = await db!.query("SELECT name FROM _migrations_integ"); expect(Array.isArray(ledger)).toBe(true); await plugin.onClose?.(ctx); }); });