import { http, HttpResponse } from "msw"; import { expect, test } from "vitest"; import { server } from "../test/msw"; import { createCatalogRelationship, deleteCatalogRelationship, getCatalogMetrics, rebuildGeneratedRelationships, setCatalogRelationshipStatus, type CatalogMetrics, type CatalogRelationship, } from "./catalog-databases"; const databaseId = "d4baf0f5-b9c3-4dc5-aad2-2f5676a36e64"; const globalMetrics: CatalogMetrics = { scope: "global", databaseId: null, tables: 163, columns: 2_275, sensitiveColumns: 5, relationships: 21, descriptionTargets: 2_438, describedTargets: 1_829, descriptionCoverage: 75.02, updatedAt: "2026-08-31T08:45:00.000Z", }; test("loads global catalog metrics without a database filter", async () => { let requestedUrl: URL | undefined; server.use(http.get("/api/catalog/metrics", ({ request }) => { requestedUrl = new URL(request.url); return HttpResponse.json(globalMetrics); })); await expect(getCatalogMetrics()).resolves.toEqual(globalMetrics); expect(requestedUrl?.pathname).toBe("/api/catalog/metrics"); expect(requestedUrl?.searchParams.has("databaseId")).toBe(false); }); test("loads metrics scoped to the requested database", async () => { const databaseMetrics: CatalogMetrics = { ...globalMetrics, scope: "database", databaseId, tables: 42, columns: 508, sensitiveColumns: 3, relationships: 8, descriptionTargets: 550, describedTargets: 413, descriptionCoverage: 75.09, }; let requestedDatabaseId: string | null = null; server.use(http.get("/api/catalog/metrics", ({ request }) => { requestedDatabaseId = new URL(request.url).searchParams.get("databaseId"); return HttpResponse.json(databaseMetrics); })); await expect(getCatalogMetrics(databaseId)).resolves.toEqual(databaseMetrics); expect(requestedDatabaseId).toBe(databaseId); }); test("propagates an unsuccessful catalog metrics response", async () => { server.use(http.get("/api/catalog/metrics", () => new HttpResponse(null, { status: 503 }))); await expect(getCatalogMetrics()).rejects.toMatchObject({ status: 503 }); }); const logicalRelationship: CatalogRelationship = { id: "11111111-1111-4111-8111-111111111111", databaseId, constraintName: null, sourceTableId: "22222222-2222-4222-8222-222222222222", sourceTableName: "orders", targetTableId: "33333333-3333-4333-8333-333333333333", targetTableName: "users", updateRule: null, deleteRule: null, deferrable: false, initiallyDeferred: false, columns: [{ position: 1, sourceColumnId: "44444444-4444-4444-8444-444444444444", sourceColumnName: "user_id", targetColumnId: "55555555-5555-4555-8555-555555555555", targetColumnName: "id", }], origin: "manual", status: "active", lastSyncedDatabaseVersion: null, lastSyncedAt: null, createdAt: "2026-08-31T12:00:00.000Z", updatedAt: "2026-08-31T12:00:00.000Z", }; test("creates a manual relationship from the selected source and target columns", async () => { let requestBody: unknown; server.use(http.post("/api/catalog/databases/:databaseId/relationships", async ({ request }) => { requestBody = await request.json(); return HttpResponse.json(logicalRelationship, { status: 201 }); })); await expect(createCatalogRelationship( databaseId, logicalRelationship.columns[0].sourceColumnId, logicalRelationship.columns[0].targetColumnId, )).resolves.toEqual(logicalRelationship); expect(requestBody).toEqual({ sourceColumnId: logicalRelationship.columns[0].sourceColumnId, targetColumnId: logicalRelationship.columns[0].targetColumnId, }); }); test("rebuilds generated relationships for one database", async () => { let calls = 0; server.use(http.post( "/api/catalog/databases/:databaseId/relationships/rebuild-generated", () => { calls += 1; return HttpResponse.json({ added: 12, alreadyPresent: 7, excluded: 3, ambiguous: 2 }); }, )); await expect(rebuildGeneratedRelationships(databaseId)).resolves.toEqual({ added: 12, alreadyPresent: 7, excluded: 3, ambiguous: 2, }); expect(calls).toBe(1); }); test("changes whether a logical relationship is active or excluded", async () => { let requestBody: unknown; server.use(http.patch( "/api/catalog/databases/:databaseId/relationships/:relationshipId", async ({ request }) => { requestBody = await request.json(); return HttpResponse.json({ ...logicalRelationship, status: "excluded" }); }, )); await expect(setCatalogRelationshipStatus( databaseId, logicalRelationship.id, "excluded", )).resolves.toMatchObject({ status: "excluded" }); expect(requestBody).toEqual({ status: "excluded" }); }); test("permanently deletes a logical relationship", async () => { let calls = 0; server.use(http.delete( "/api/catalog/databases/:databaseId/relationships/:relationshipId", () => { calls += 1; return new HttpResponse(null, { status: 204 }); }, )); await expect(deleteCatalogRelationship(databaseId, logicalRelationship.id)).resolves.toBeUndefined(); expect(calls).toBe(1); });