// node --test web/knotless/knotless-cubit.test.mjs (after `cargo wasm`) // The cubit with the real knotless module, runModule, policyCall and the Node wasm runner; only OAuth // and the user's PDS are stubbed. import assert from "node:assert/strict"; import { mkdtemp, readFile } from "node:fs/promises"; import { registerHooks } from "node:module"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { test } from "node:test"; // The page resolves atlibs/ through its import map; Node gets the same mapping here. registerHooks({ resolve(specifier, context, next) { const lib = specifier.startsWith("atlibs/") && new URL(`./atlibs/${specifier.slice("atlibs/".length)}`, import.meta.url).href; return next(lib || specifier, context); }, }); const { KnotlessCubit, POLICY } = await import("./knotless-cubit.js"); const { localModule } = await import("./atlibs/run-module/index.js"); const { createNodeContext, fileFetch } = await import("./atlibs/server/index.js"); const did = "did:plc:lpfuqerea3deuoyrn7ojser4"; const modules = new URL("./modules/", import.meta.url); // A signed-in OAuth session over an in-memory PDS: fetchHandler is what sessionXrpc calls through. function session(records, writes) { return { did, async fetchHandler(path, init) { const [nsid, query] = path.slice("/xrpc/".length).split("?"); const params = Object.fromEntries(new URLSearchParams(query)); const body = init?.body && JSON.parse(init.body); if (nsid === "com.atproto.repo.getRecord") { const value = records[`${params.collection}/${params.rkey}`]; return value ? Response.json({ value }) : Response.json({ error: "RecordNotFound", message: "Could not locate record" }, { status: 400 }); } if (nsid === "com.atproto.repo.createRecord") { writes.push(body); records[`${body.collection}/${body.rkey}`] = body.record; return Response.json({ uri: `at://${did}/${body.collection}/${body.rkey}`, cid: "bafy" }); } if (nsid === "com.atproto.repo.applyWrites") { writes.push(body); for (const w of body.writes) records[`${w.collection}/${w.rkey}`] = w.value; return Response.json({}); } return Response.json({ error: "InvalidRequest" }, { status: 400 }); }, }; } test("the page's policy is records/knotless.json's scope", async () => { const record = JSON.parse(await readFile(new URL("../../records/knotless.json", import.meta.url))); assert.equal(POLICY.sessions.knotless.scope, record.clientMetadata.scope); const spec = JSON.parse(await readFile(new URL("knotless.json", modules))); assert.equal(spec.session, "knotless"); }); test("create a repository, then commit to it, as the signed-in user", async () => { const records = {}; const writes = []; const ctx = createNodeContext({ id: "knotless-test", stateDir: await mkdtemp(join(tmpdir(), "knotless-")) }); const client = Promise.resolve({ init: async () => ({ session: session(records, writes) }), signIn: async () => {}, revoke: async () => {} }); const knotless = localModule(new URL("knotless.wasm", modules), new URL("knotless.json", modules), fileFetch); const cubit = new KnotlessCubit({ ctx, client, knotless }); await cubit.init(); assert.equal(cubit.state.did, did); await cubit.createRepo({ name: "demo", description: "", defaultBranch: "" }); assert.equal(cubit.state.run.status, "done", cubit.state.run.error); assert.equal(cubit.state.run.result.url, `knotless://${did}/demo`); assert.equal(records["dev.knotless.repo/demo"].defaultBranch, "main"); await cubit.commit({ name: "demo", branch: "main", parent: "", message: "first", author: "", changes: '[{"path": "README.md", "content": "# hello\\n"}]' }); assert.equal(cubit.state.run.status, "done", cubit.state.run.error); assert.equal(cubit.state.run.result.changed, true); assert.deepEqual(writes.at(-1).writes.map((w) => w.collection), ["dev.knotless.pack", "dev.knotless.ref"]); await cubit.commit({ name: "demo", branch: "main", message: "bad", changes: "[]" }); assert.equal(cubit.state.run.status, "error"); assert.match(cubit.state.run.error, /empty/); });