import assert from "node:assert/strict"; import { test } from "node:test"; import { did, load } from "../../../rust/src/harness.mjs"; const { module, run, handle } = await load(new URL("../../../target/wasm32-unknown-unknown/release/echo.wasm", import.meta.url), new URL("./module.json", import.meta.url)); const service = "https://relay.example"; const SUB = "com.atproto.sync.subscribeRepos"; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); const respond = (messages) => (call) => (call.subscribe ? messages : { ok: true, output: { handle: "alice.test", did } }); test("module has no imports and exports the ABI", () => { assert.equal(WebAssembly.Module.imports(module).length, 0); const names = WebAssembly.Module.exports(module).map((e) => e.name); for (const name of ["memory", "alloc", "watch"]) assert.ok(names.includes(name), name); }); test("start describes the repo, subscribes and sets a timer in one output", () => { const out = run("watch", { did, now: "x", service, seconds: "2", state: null, event: { type: "start" } }); assert.deepEqual(out.calls, [ { id: "who", nsid: "com.atproto.repo.describeRepo", params: { repo: did } }, { id: "feed", nsid: SUB, service, subscribe: true }, ]); assert.deepEqual(out.timers, [{ id: "stop", ms: 2000 }]); assert.deepEqual(out.state, { handle: null, count: 0, last: null }); }); // Paced (as rust/host/conformance.test.mjs): four instant messages would end the stream before the // module's cancel lands, and which comes first would be a scheduler's choice, not the module's. async function* paced(messages, ms = 20) { for (const message of messages) { await sleep(ms); yield message; } } test("counts messages to the limit, then cancels the stream and finishes", async () => { const messages = [1, 2, 3, 4].map((seq) => ({ $type: `${SUB}#commit`, seq })); const ran = await handle("watch", { service, limit: 3 }, { respond: respond(paced(messages)) }); assert.equal(ran.error, undefined); assert.deepEqual(ran.result, { handle: "alice.test", count: 3, last: { $type: `${SUB}#commit`, seq: 3 }, stopped: "limit" }); assert.ok(ran.calls.some((c) => c.id === "feed" && c.cancelled)); }); test("a stream that closes early finishes with what it saw; a failed stream reports its error", async () => { const closed = await handle("watch", { service, limit: "10" }, { respond: respond([{ seq: 1 }]) }); assert.equal(closed.result.stopped, "closed"); assert.equal(closed.result.count, 1); async function* failing() { yield { seq: 9 }; throw new Error("FutureCursor"); } const failed = await handle("watch", { service }, { respond: (call) => (call.subscribe ? failing() : { ok: false, error: "RepoNotFound" }) }); assert.deepEqual(failed.result, { handle: null, count: 1, last: { $type: null, seq: 9 }, stopped: "FutureCursor" }); }); test("inputs are echoed up the stream; the timer ends a quiet run", async () => { async function* quiet() { await sleep(1200); } async function* inputs() { yield { $type: "#ping", n: 1 }; } const ran = await handle("watch", { service, seconds: 1 }, { respond: respond(quiet()), inputs: inputs() }); assert.deepEqual(ran.sends, [{ nsid: SUB, data: { $type: "#ping", n: 1 } }]); assert.equal(ran.result.stopped, "time"); }); test("bad input is an error, as data", async () => { assert.equal((await handle("watch", { service: "http://x" }, { respond: respond([]) })).error, "service must be an https origin"); assert.equal(run("watch", { did, now: "x", service, limit: "0", event: { type: "start" } }).error, "limit must be 1 to 1000"); assert.match(run("watch", { now: "x", service, event: { type: "start" } }).error, /invalid input/); }); test("connect passes its input to one iroh.connect call and finishes with the output", async () => { const input = { ticket: "endpointabc", hostKeys: [{ type: "ssh-ed25519", publicKey: "AAAA" }], command: "cat", stdin: "hi" }; const ran = await handle("connect", input, { respond: (call) => (call.nsid === "com.fedfork.exec.iroh.connect" && call.input.stdin === "hi" ? { ok: true, output: { output: "hi", exitCode: 0 } } : { ok: false, error: "unexpected" }) }); assert.deepEqual(ran.result, { output: "hi", exitCode: 0 }); });