// Loads a module for its test.mjs: its build, module.json and session record. handle() runs it under runModule // with module.json's calls and anyRecords, like a real host, and fails the test when the module makes a call // outside its session's scope (atlibs' own scopeAllows). respond(call) answers { ok, output } or { ok: false, error }; // http (runModule's allowlist) and fetch (its requests: (url, init) -> Response) serve com.fedfork.exec.http.fetch. import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { scopeAllows } from "../../atlibs/javascript/src/oauth/index.js"; import { runModule } from "../../atlibs/javascript/src/run-module/index.js"; import { did, load, now } from "../../atlibs/rust/src/harness.mjs"; export { did, now }; const root = new URL("../", import.meta.url); const json = (path) => JSON.parse(readFileSync(new URL(path, root), "utf8")); export async function loadModule(name) { const spec = json(`modules/${name}/module.json`); const scope = json(`records/${spec.session}.json`).clientMetadata.scope; const loaded = await load(new URL(`target/wasm32-unknown-unknown/release/${name.replaceAll("-", "_")}.wasm`, root)); async function handle(entry, input, { respond, budget, http, fetch } = {}) { const ctx = { wasm: { run: async ({ entry, input }) => loaded.run(entry, { ...input, now }) }, http: { fetch: fetch ?? (async (url) => assert.fail(`no fetch for ${url}`)) } }; const outside = []; const call = async (nsid, request) => { if (!scopeAllows(scope, nsid, request)) outside.push({ nsid, ...request }); const answer = await respond({ nsid, ...request }); if (answer?.ok === false) throw new Error(answer.error ?? "call failed"); return answer?.output; }; const ran = await runModule(ctx, { module: loaded.module, calls: spec.calls, anyRecords: spec.anyRecords, http: spec.http }, entry, { did, ...input }, call, { budget, http }); assert.deepEqual(outside, [], "calls outside the module's calls or session scope"); return ran; } return { ...loaded, spec, scope, handle }; } /** No imports, and an export per action. */ export function abiTest(test, { module, spec }) { test("module has no imports and exports every action", () => { assert.equal(WebAssembly.Module.imports(module).length, 0); const names = WebAssembly.Module.exports(module).map((e) => e.name); for (const name of ["memory", "alloc", ...spec.actions.map((a) => a.entry)]) assert.ok(names.includes(name), name); }); }