import assert from "node:assert/strict"; import { test } from "node:test"; import { did, load, now } from "../../../rust/src/harness.mjs"; const { module, run, handle } = await load(new URL("../../../target/wasm32-unknown-unknown/release/vibescrobble.wasm", import.meta.url), new URL("./module.json", import.meta.url)); const start = (fields) => ({ did, now, ...fields, state: null, event: { type: "start" } }); const created = { ok: true, output: { uri: `at://${did}/com.vibescrobble.scrobble/new`, cid: "bafy" } }; 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", "scrobble"]) assert.ok(names.includes(name), name); }); test("scrobble creates one record and reports its uri", async () => { const calls = []; const ran = await handle("scrobble", { text: "πŸš€ shipped exec.fedfork.com", task: "#socialweb" }, { respond: ({ nsid, input }) => (calls.push({ nsid, input }), created) }); assert.deepEqual(calls, [ { nsid: "com.atproto.repo.createRecord", input: { repo: did, collection: "com.vibescrobble.scrobble", validate: false, record: { $type: "com.vibescrobble.scrobble", text: "πŸš€ shipped exec.fedfork.com", emoji: "πŸš€", createdAt: now, task: "#socialweb", }, }, }, ]); assert.deepEqual(ran.result, created.output); }); test("scrobble keeps whole emoji sequences and omits an empty task", () => { for (const emoji of ["πŸ‘©β€πŸ’»", "πŸ‘πŸ½", "πŸ‡ΊπŸ‡Έ", "❀️", "1️⃣", "βœ…", "⭐"]) { const record = run("scrobble", start({ text: `${emoji} x`, task: "" })).calls[0].input.record; assert.equal(record.emoji, emoji); assert.equal("task" in record, false); } }); test("scrobble rejects text that does not start with an emoji", () => { for (const text of ["hello", "1 thing", "# heading", "", " πŸš€ leading space", "β†’ arrow", "Β© 2026"]) { assert.match(run("scrobble", start({ text })).error, /emoji/, text); } }); test("scrobble truncates long text to 256 graphemes", () => { const record = run("scrobble", start({ text: `πŸš€${"a".repeat(400)}` })).calls[0].input.record; assert.equal([...new Intl.Segmenter().segment(record.text)].length, 256); }); test("scrobble rejects a bad did, now or task", () => { assert.match(run("scrobble", start({ did: "alice", text: "πŸš€" })).error, /did/); assert.match(run("scrobble", start({ now: "yesterday", text: "πŸš€" })).error, /now/); assert.match(run("scrobble", start({ text: "πŸš€", task: "x".repeat(300) })).error, /task/); }); test("a failed create surfaces as the module error", async () => { const ran = await handle("scrobble", { text: "πŸš€" }, { respond: () => ({ ok: false, error: "createRecord failed with 400" }) }); assert.equal(ran.error, "createRecord failed with 400"); });