2.5 KiB
run-wasm-conformance
(no submodules)
Purpose
Exports runWasmSandboxConformance(label, makeSandbox) — the
conformance harness every WASM-tier sandbox host (this package's own,
or any future implementation) MUST pass. The harness pins the
observable contract: preopens reflect policy.fs[], the outbound
allowlist reflects policy.fetch[], the rw: prefix turns a preopen
read-write, dispose() is idempotent, and the instrument registry
exposes the canonical plugin.permission_denied_total counter.
The harness is exported from the ./testing subpath so adapters
consume it as a normal import. The in-package fixture sandbox passes
every scenario; the harness is self-tested via
run-wasm-conformance.test.ts.
Features
- Six pinned scenarios covering the spec delta's observable behaviour.
- Uses the host's
describe/itfrom@suaveplan/testing/runnerso adapter test reports show nested suites under the host's tree. WasmSandboxFactorysignature lets adapters pass a minimal builder rather than re-implementing the fullcreateWasmSandboxsurface.
Usage: Basic
// In an adapter package's test file:
import { runWasmSandboxConformance } from "@suaveplan/api-sandbox-wasm/testing";
import { makeMyAdapterSandbox } from "./my-adapter.js";
runWasmSandboxConformance("my-adapter", makeMyAdapterSandbox);
Usage: Advanced
runWasmSandboxConformance("with-cache", async (input) => {
const cached = sandboxCache.get(JSON.stringify(input));
if (cached !== undefined) return cached;
const handle = await createWasmSandbox({ /* ... */ });
sandboxCache.set(JSON.stringify(input), handle);
return handle;
});
API Reference
runWasmSandboxConformance(label: string, makeSandbox: WasmSandboxFactory): voidWasmSandboxFactory = (input) => Promise<WasmSandboxHandle>whereinputis{ fsRules, fetchRules, denyCapability }.
Implementation Notes
The harness consumes the adapter's WasmSandboxHandle via its public
surface only — preopens, outbound, instruments, and dispose().
Adapters that wrap or proxy the handle must preserve those properties
identically.
The harness deliberately uses dispose() after every scenario rather
than letting GC handle it, because a WASM-runtime under load can pin
memory longer than the test budget allows. Adapters that hold open OS
resources MUST close them in dispose() for the harness to pass on
constrained CI runners.