1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
// 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/);
});