import { afterEach, beforeEach, describe, it } from "@std/testing/bdd"; import { expect } from "@std/expect"; import { Identity } from "@commonfabric/identity"; import { Runtime } from "../src/runtime.ts"; import { StorageManager } from "../src/storage/cache.deno.ts"; import { parseFabricRef } from "../src/sandbox/fabric-import-specifier.ts"; import { resolveFabricRefToIdentity } from "../src/fabric-ref-resolution.ts"; import { slugIdForSpace } from "../src/slugs.ts"; import { entityIdFrom } from "../src/create-ref.ts"; import type { Cell } from "../src/cell.ts"; const signer = await Identity.fromPassphrase("fabric ref resolution test"); const space = signer.did(); const ENTRY_A = "Avcny13Rj8q-2ClANy_-k0ikWWQcXx7QTdsiqGfrC1c"; describe("fabric ref resolution", () => { let storageManager: ReturnType; let runtime: Runtime; beforeEach(() => { storageManager = StorageManager.emulate({ as: signer }); runtime = new Runtime({ apiUrl: new URL(import.meta.url), storageManager, }); }); afterEach(async () => { await runtime?.dispose(); await storageManager?.close(); }); function parse(specifier: string) { const ref = parseFabricRef(specifier); if (ref === undefined) throw new Error(`not a fabric ref: ${specifier}`); return ref; } function pieceCell(label: string): Cell { return runtime.getCell( space, { space, random: `piece-${label}` }, ); } async function writeSlug(slug: string, target: Cell): Promise { const slugCell = runtime.getCellFromEntityId( space, entityIdFrom(slugIdForSpace(space, slug)), ); await runtime.editWithRetry((tx) => { const slugWithTx = slugCell.withTx(tx); slugWithTx.setRawUntyped( target.withTx(tx).getAsWriteRedirectLink({ base: slugWithTx }), ); }); } it("returns terminal identities for pattern refs", async () => { const result = await resolveFabricRefToIdentity( runtime, space, parse(`cf:pattern:${ENTRY_A}`), ); expect(result).toEqual({ entryIdentity: ENTRY_A, chain: [`pattern:${ENTRY_A}`, `entryIdentity:${ENTRY_A}`], }); }); it("rejects space names until name to DID resolution exists", async () => { await expect( resolveFabricRefToIdentity(runtime, space, parse("cf:/kitchen/todo")), ).rejects.toThrow( "space names require name→DID resolution (open question 2); use a DID", ); }); it("resolves slug to piece patternIdentity metadata", async () => { const piece = pieceCell("identity"); await runtime.editWithRetry((tx) => { const pieceWithTx = piece.withTx(tx); pieceWithTx.set({ name: "piece" }); pieceWithTx.setMetaRaw("patternIdentity", { identity: ENTRY_A, symbol: "default", }); }); await writeSlug("dep", piece); const result = await resolveFabricRefToIdentity( runtime, space, parse("cf:dep"), ); expect(result.entryIdentity).toBe(ENTRY_A); expect(result.chain).toEqual([ "slug:dep", `piece:${piece.getAsNormalizedFullLink().id}`, `patternIdentity:${ENTRY_A}`, `entryIdentity:${ENTRY_A}`, ]); }); it("reports slug to plain data cells with the chain", async () => { const cell = runtime.getCell(space, { space, random: "plain-data" }); await runtime.editWithRetry((tx) => { cell.withTx(tx).set({ value: 1 }); }); await writeSlug("plain", cell); await expect( resolveFabricRefToIdentity(runtime, space, parse("cf:plain")), ).rejects.toThrow( `cf:plain does not resolve to a pattern (chain: slug:plain)`, ); }); it("wraps missing slug errors with the chain", async () => { await expect( resolveFabricRefToIdentity(runtime, space, parse("cf:missing")), ).rejects.toThrow(`Slug "missing" not found. (chain: slug:missing)`); }); it("reports a piece without a pattern identity with the chain", async () => { // A piece cell carrying only a legacy `pattern` link (no `patternIdentity`) // is unrecoverable post-retirement (the sanctioned data-wipe outcome). const piece = pieceCell("legacy"); await runtime.editWithRetry((tx) => { piece.withTx(tx).set({ name: "legacy" }); }); await writeSlug("legacy", piece); await expect( resolveFabricRefToIdentity(runtime, space, parse("cf:legacy")), ).rejects.toThrow("does not resolve to a pattern"); }); });