Files
ThothII/backend/test/effective-relationship-snapshot.test.ts
T
Codex 076c9742c5 feat: consolidate database management work
Add catalog-owned logical relationships and runtime snapshots, extend the database-management UI and validation coverage, and document the updated operational workflow.

Keep active sensitive-generation status in a tooltip and indicator, and update the layout E2E to follow the history action in its new database-scoped location.
2026-09-01 14:46:55 +02:00

197 lines
5.6 KiB
TypeScript

import { describe, expect, test, vi } from "vitest";
import { EffectiveRelationshipSnapshotProvider } from "../src/catalog/effective-relationship-snapshot.js";
import type { CatalogRelationship, WorkspaceDatabase } from "../src/catalog/types.js";
const timestamp = "2026-08-31T10:00:00.000Z";
function database(): WorkspaceDatabase {
return {
id: "database-1",
workspaceId: "psd",
engine: "postgres",
databaseName: "warehouse",
schema: "public",
version: 1,
createdAt: timestamp,
updatedAt: timestamp,
binding: { transport: "postgres_direct" },
connectionStatus: "reachable",
schemaSyncedVersion: 1,
schemaSyncedAt: timestamp,
};
}
const operations = {
async run<T>(_databaseId: string, operation: () => Promise<T>): Promise<T> {
return await operation();
},
};
function relationship(options: {
id: string;
origin: "physical" | "generated" | "manual";
status?: "active" | "excluded";
sourceTable?: string;
sourceColumns?: string[];
targetTable?: string;
targetColumns?: string[];
}): CatalogRelationship {
const sourceColumns = options.sourceColumns ?? ["user_id"];
const targetColumns = options.targetColumns ?? ["id"];
const columns = sourceColumns.map((sourceColumnName, position) => ({
position: position + 1,
sourceColumnId: `source-${position}`,
sourceColumnName,
targetColumnId: `target-${position}`,
targetColumnName: targetColumns[position],
}));
const common = {
id: options.id,
databaseId: "database-1",
sourceTableId: "source-table",
sourceTableName: options.sourceTable ?? "orders",
targetTableId: "target-table",
targetTableName: options.targetTable ?? "users",
columns,
createdAt: timestamp,
updatedAt: timestamp,
};
if (options.origin === "physical") {
return {
...common,
constraintName: `fk_${options.id}`,
updateRule: "NO ACTION",
deleteRule: "NO ACTION",
deferrable: false,
initiallyDeferred: false,
lastSyncedDatabaseVersion: 1,
lastSyncedAt: timestamp,
origin: "physical",
status: "active",
};
}
return {
...common,
constraintName: null,
updateRule: null,
deleteRule: null,
deferrable: false,
initiallyDeferred: false,
lastSyncedDatabaseVersion: null,
lastSyncedAt: null,
origin: options.origin,
status: options.status ?? "active",
columns: [columns[0]!],
};
}
describe("EffectiveRelationshipSnapshotProvider", () => {
test("returns no projection for a workspace without a Catalog database", async () => {
const list = vi.fn();
const provider = new EffectiveRelationshipSnapshotProvider(
{
get: vi.fn().mockResolvedValue(undefined),
getByWorkspace: vi.fn().mockResolvedValue(undefined),
},
{ list },
operations,
);
await expect(provider.render("legacy")).resolves.toBeUndefined();
expect(list).not.toHaveBeenCalled();
});
test("renders a deterministic active map with physical precedence", async () => {
const duplicateGenerated = relationship({ id: "generated", origin: "generated" });
const physical = relationship({ id: "physical", origin: "physical" });
const compositePhysical = relationship({
id: "physical-composite",
origin: "physical",
sourceTable: "order_lines",
sourceColumns: ["order_id", "tenant_id"],
targetTable: "orders",
targetColumns: ["id", "tenant_id"],
});
const manual = relationship({
id: "manual",
origin: "manual",
sourceTable: "invoices",
sourceColumns: ["customer_id"],
targetTable: "customers",
targetColumns: ["id"],
});
const excluded = relationship({
id: "excluded",
origin: "generated",
status: "excluded",
sourceTable: "invoices",
});
const list = vi.fn().mockResolvedValue([
manual,
duplicateGenerated,
excluded,
physical,
compositePhysical,
]);
const provider = new EffectiveRelationshipSnapshotProvider(
{
get: vi.fn().mockResolvedValue(database()),
getByWorkspace: vi.fn().mockResolvedValue(database()),
},
{ list },
operations,
);
const rendered = await provider.render("psd");
expect(rendered?.endsWith("\n")).toBe(true);
expect(JSON.parse(rendered!)).toEqual({
schemaVersion: 1,
workspaceId: "psd",
relationships: [
{
sourceTable: "invoices",
sourceColumns: ["customer_id"],
targetTable: "customers",
targetColumns: ["id"],
origin: "manual",
},
{
sourceTable: "order_lines",
sourceColumns: ["order_id", "tenant_id"],
targetTable: "orders",
targetColumns: ["id", "tenant_id"],
origin: "physical",
},
{
sourceTable: "orders",
sourceColumns: ["user_id"],
targetTable: "users",
targetColumns: ["id"],
origin: "physical",
},
],
});
expect(list).toHaveBeenCalledWith("database-1");
});
test("fails closed when the Catalog schema is absent or stale", async () => {
const stale = database();
delete stale.schemaSyncedVersion;
delete stale.schemaSyncedAt;
const list = vi.fn();
const provider = new EffectiveRelationshipSnapshotProvider(
{
get: vi.fn().mockResolvedValue(stale),
getByWorkspace: vi.fn().mockResolvedValue(stale),
},
{ list },
operations,
);
await expect(provider.render("psd")).rejects.toThrow(
"effective relationship snapshot requires a current full schema synchronization",
);
expect(list).not.toHaveBeenCalled();
});
});