Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
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<string, unknown>();
const ctx = createPluginContext<DatabasePluginConfig>({
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);
});
});

View File

@@ -0,0 +1,32 @@
/**
* Test setup for @suaveplan/api-plugin-database.
*
* Installs a real in-memory OpenTelemetry MeterProvider BEFORE any source
* module imports so the meters resolved on first call bind to it.
*/
import { metrics } from "@opentelemetry/api";
import {
AggregationTemporality,
InMemoryMetricExporter,
MeterProvider,
PeriodicExportingMetricReader,
} from "@opentelemetry/sdk-metrics";
export const testInMemoryMetricExporter = new InMemoryMetricExporter(
AggregationTemporality.CUMULATIVE
);
const reader = new PeriodicExportingMetricReader({
exporter: testInMemoryMetricExporter,
exportIntervalMillis: 60_000,
});
export const testMeterProvider = new MeterProvider({ readers: [reader] });
metrics.setGlobalMeterProvider(testMeterProvider);
export async function flushTestMetrics() {
await testMeterProvider.forceFlush();
const collected = testInMemoryMetricExporter.getMetrics();
testInMemoryMetricExporter.reset();
return collected;
}