~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atlibs

0
0
main
113 lines · 3.9 KB Raw History

Test a module

A module does no I/O, so a test is the real host loop with your answers in place of the network. rust/src/harness.mjs loads a build and drives it with runModule.

Answer each call

load(wasm, moduleJson) loads a build; given its module.json, the run is held to that file's calls and anyRecords, as a host holds it, so a call outside them fails the test. handle(entry, input, { respond }) runs the entry; respond(call) returns { ok, output } or { ok: false, error }. It resolves to the run's { result, error, calls, events }.

examples/rust/hello/test.mjs

import assert from "node:assert/strict";
import { test } from "node:test";

import { did, load } from "../../../rust/src/harness.mjs";

const { handle } = await load(new URL("../../../target/wasm32-unknown-unknown/release/hello.wasm", import.meta.url), new URL("./module.json", import.meta.url));

// handle() runs the module under the real host loop; respond answers each call.
test("count lists records, then counts them", async () => {
	const ran = await handle("count", { collection: "xyz.statusphere.status" }, { respond: () => ({ ok: true, output: { records: [{}, {}] } }) });
	assert.equal(ran.calls[0].nsid, "com.atproto.repo.listRecords");
	assert.deepEqual(ran.result, { collection: "xyz.statusphere.status", records: 2 });
});

test("a failed call is the run's error", async () => {
	const ran = await handle("count", { collection: "xyz.statusphere.status" }, { respond: () => ({ ok: false, error: "RepoNotFound" }) });
	assert.match(ran.error, /RepoNotFound/);
});
node --test examples/rust/hello/test.mjs 2>&1 | grep -E '^ℹ (pass|fail)'
ℹ pass 2
ℹ fail 0

did and now are fixed (harness.mjs exports them), so outputs are stable.

Subscriptions and input

For a call with subscribe: true, respond returns its messages (an array or an async iterable); the subscription closes after the last. inputs feeds Event::Input, and sends in the result lists what the module wrote up its subscriptions.

examples/rust/echo/test.mjs

const respond = (messages) => (call) => (call.subscribe ? messages : { ok: true, output: { handle: "alice.test", did } });

examples/rust/echo/test.mjs

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");
});

One event at a time

run(entry, input) calls an entry once with the exact JSON the host would send, for checking one step:

examples/rust/statusphere/test.mjs

const start = (fields) => ({ did, now, ...fields, state: null, event: { type: "start" } });

examples/rust/statusphere/test.mjs

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", ...ENTRIES]) assert.ok(names.includes(name), name);
});

Both hosts

The same tests run against the Rust host: swap.mjs points runModule at it.

node --import ./rust/host/swap.mjs --test examples/rust/*/test.mjs

A page

Test a page's cubit with the real modules and stubbed OAuth: Run modules in a page.

Everything

cargo wasm && cargo host && cargo loopback && cargo host-test
node --test examples/rust/*/test.mjs examples/web/*/*.test.mjs examples/node-hono/test.mjs javascript/src/*/*.test.mjs rust/host/*.test.mjs docs/docs.test.mjs

Previous: Calls | Next: Publish a module