import { describe, expect, it } from "@effect/vitest";
import { Effect } from "effect";
import { withQueryContext } from "@executor-js/fumadb/query";
import { collectTables } from "./executor";
import { createSqliteTestFumaDb, type SqliteTestFumaDb } from "./sqlite-test-db";
// The artifact table is OWNER-scoped: a saved artifact belongs to the subject
// who generated it, and nobody else in the tenant may read it (org-tier
// sharing is a later change that reuses the `owner` column already here).
// Written against the real SQLite bring-up (`createSqliteTestFumaDb` runs the
// same statements the local/self-host/D1 hosts boot with) instead of asserting
// on the table definition object.
const TENANT = "t1";
const SUBJECT = "user_a";
const OTHER_SUBJECT = "user_b";
const withDb = (body: (db: SqliteTestFumaDb) => Promise): Promise =>
Effect.runPromise(
Effect.acquireUseRelease(
Effect.promise(() => createSqliteTestFumaDb({ tables: collectTables() })),
(db) => Effect.promise(() => body(db)),
(db) => Effect.promise(() => db.close()),
),
);
const insertArtifact = (
db: SqliteTestFumaDb,
row: {
readonly rowId: string;
readonly tenant: string;
readonly subject: string;
readonly id: string;
readonly title?: string;
},
): Promise =>
db.client.execute({
sql: `INSERT INTO artifact (row_id, tenant, owner, subject, id, title, description, code, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
args: [
row.rowId,
row.tenant,
"user",
row.subject,
row.id,
row.title ?? "Dashboard",
null,
"export default function App() { return null; }",
Date.now(),
Date.now(),
],
});
describe("artifact table", () => {
it.effect("is created by the runtime schema bring-up", () =>
Effect.promise(() =>
withDb(async (db) => {
const info = await db.client.execute("PRAGMA table_info('artifact')");
const columns = info.rows.map((row) => String(row["name"]));
expect(columns).toEqual(
expect.arrayContaining([
"row_id",
"tenant",
"owner",
"subject",
"id",
"title",
"description",
"code",
"bindings",
"created_at",
"updated_at",
]),
);
}),
),
);
it.effect("keys one row per (tenant, owner, subject, id)", () =>
Effect.promise(() =>
withDb(async (db) => {
await insertArtifact(db, { rowId: "a1", tenant: TENANT, subject: SUBJECT, id: "art_1" });
// The same artifact id under a DIFFERENT subject is a different row —
// ids are only unique within one owner partition.
await insertArtifact(db, {
rowId: "a2",
tenant: TENANT,
subject: OTHER_SUBJECT,
id: "art_1",
});
await expect(
insertArtifact(db, { rowId: "a3", tenant: TENANT, subject: SUBJECT, id: "art_1" }),
).rejects.toThrow();
const rows = await db.client.execute("SELECT row_id FROM artifact ORDER BY row_id");
expect(rows.rows.map((row) => String(row["row_id"]))).toEqual(["a1", "a2"]);
}),
),
);
it.effect("hides another subject's artifacts from a bound executor", () =>
Effect.promise(() =>
withDb(async (db) => {
await insertArtifact(db, {
rowId: "a1",
tenant: TENANT,
subject: SUBJECT,
id: "art_mine",
title: "Mine",
});
await insertArtifact(db, {
rowId: "a2",
tenant: TENANT,
subject: OTHER_SUBJECT,
id: "art_theirs",
title: "Theirs",
});
const scoped = withQueryContext(db.db, { tenant: TENANT, subject: SUBJECT });
const visible = await scoped.findMany("artifact", {});
expect(visible.map((row) => row.id)).toEqual(["art_mine"]);
const otherScoped = withQueryContext(db.db, { tenant: TENANT, subject: OTHER_SUBJECT });
const otherVisible = await otherScoped.findMany("artifact", {});
expect(otherVisible.map((row) => row.id)).toEqual(["art_theirs"]);
}),
),
);
});