#!/usr/bin/env -S deno run -A /** * Integration test (CFC Phase 2): a single `db.query` that projects BOTH a * CFC-labeled column AND an asCell `_cf_link` column. The labeled-write path * (rows written under the per-field label schema) must NOT break link decoding: * the consumer must (a) inherit the labeled column's confidentiality AND (b) * resolve the link column to a live Cell — through the real toolshed server. */ import app from "../../toolshed/app.ts"; import { Identity } from "@commonfabric/identity"; import { Runtime } from "../src/index.ts"; import { StorageManager } from "../src/storage/cache.deno.ts"; import { cfcLabelViewForDereferenceTraces } from "../src/cfc/label-view.ts"; import { cfcConfidentialityForObservationNode } from "../src/cfc/observation.ts"; const TIMEOUT_MS = 180000; async function runTest(base: URL) { const account = await Identity.fromPassphrase( "sqlite-cfc-label-link " + crypto.randomUUID(), ); const runtime = new Runtime({ apiUrl: base, storageManager: StorageManager.open({ as: account, memoryHost: new URL(base), }), }); const space = account.did(); try { const patternSource = await Deno.readTextFile( new URL("./sqlite-cfc-label-link.test.tsx", import.meta.url), ); const pattern = await runtime.patternManager.compilePattern(patternSource, { space, }); const resultCell = runtime.getCell( space, `sqlite-cfc-label-link-${crypto.randomUUID()}`, pattern.resultSchema, ); const result = await runtime.runSynced(resultCell, pattern, {}); const cancelSink = result.sink(() => {}); try { await runtime.editWithRetry((tx) => result.key("seed").withTx(tx).send({}) ); const deadline = Date.now() + 25000; let ready = false; while (Date.now() < deadline) { await runtime.idle(); await runtime.storageManager.synced(); if ((result.key("q").key("pending").get() as unknown) === false) { const arr = result.key("q").key("result").getRaw() as | unknown[] | undefined; if (Array.isArray(arr) && arr.length === 1) { ready = true; break; } } await new Promise((r) => setTimeout(r, 50)); } if (!ready) throw new Error("query never reflected the seeded row"); // (a) The link column still decoded to a resolvable sigil-link object and // round-trips to the author cell — labeling did not corrupt it. const sigil = result.key("q").key("result").key(0).key("author_cf_link") .getRaw(); if ( !sigil || typeof sigil !== "object" || !("/" in (sigil as Record)) ) { throw new Error( `link column is not a decoded sigil-link object: ${ JSON.stringify(sigil) }`, ); } const linked = runtime.getCellFromLink( sigil as Parameters[0], ); await linked.sync(); if (JSON.stringify(linked.get()) !== JSON.stringify({ name: "Ada" })) { throw new Error( `linked cell resolved to ${ JSON.stringify(linked.get()) }, expected {name:"Ada"}`, ); } // (b) A consumer reading `q.result[0].note` inherits its confidentiality // (via dereference-trace accumulation as the read traverses the links). const dtx = runtime.edit(); result.key("q").key("result").key(0).key("note").withTx(dtx).get(); const view = cfcLabelViewForDereferenceTraces( dtx, dtx.getCfcState().dereferenceTraces, ); const conf = cfcConfidentialityForObservationNode({ labelView: view, logicalPath: [], }); await dtx.commit(); if (!conf.some((a) => a === "secret-note")) { throw new Error( `note did not inherit confidentiality; got ${JSON.stringify(conf)}`, ); } console.log( "=== TEST PASSED: labeled column + asCell link column coexist; the " + "consumer inherits confidentiality:[secret-note] AND resolves the link ===", ); } finally { cancelSink(); } } finally { await runtime.dispose(); } } Deno.test({ name: "sqlite db.query labeled column + asCell link column (end to end)", fn: async () => { const server = Deno.serve({ port: 0 }, app.fetch); const base = new URL(`http://${server.addr.hostname}:${server.addr.port}`); let timeoutHandle: ReturnType; const timeoutPromise = new Promise((_, reject) => { timeoutHandle = setTimeout( () => reject(new Error(`Test timed out after ${TIMEOUT_MS}ms`)), TIMEOUT_MS, ); }); try { await Promise.race([runTest(base), timeoutPromise]); } finally { clearTimeout(timeoutHandle!); await server.shutdown(); } }, sanitizeResources: false, sanitizeOps: false, });