Helpers for testing Hyperlang guests and the hosts that run them.
The kit plays the role vitest-pool-workers plays for Cloudflare Workers:
it compiles a guest, supplies fake bindings, runs exports, and starts
callback actors. It uses only the public door (Hyperlang.program/1,
Hyperlang.run/4, Hyperlang.module/1), so a test exercises exactly the
path production code takes.
import Hyperlang.Test
test "prices an item" do
prog =
program!("-module(shop).\n-export([result/1]).\nresult(Sku) -> catalog:price(Sku).\n",
bindings: %{"catalog" => %{price: fn _sku -> 42 end}})
assert {:ok, 42, meta} =
run(prog, :entry, ["A1"], bindings: %{"catalog" => %{price: fn _ -> 42 end}})
assert_stdout meta, []
endFake bindings
Wherever a binding is expected, the kit accepts a plain map of functions,
%{get: fn key -> ... end}. The function arity is the export arity. A
value may also be {fun, opts} to pass Hyperlang.Binding export options
such as nondeterministic: true. The kit backs each distinct export shape
with a generated Hyperlang.Binding module, so the same fake map at
compile time and at run time names the same module. Real bindings are
passed as a module (compile time) or {module, state} (run time).
Summary
Functions
Assert on guest stdout. subject is a run/4 result or its meta map.
expected is the list of chunks, a binary compared against the joined
chunks, or a regex matched against them.
Build a program. code is a source binary, or a source map
%{entry:, files:, manifest:} passed to Hyperlang.program/1 unchanged
except for fake bindings.
Like program/2, but returns the program or raises with the diagnostics.
Run export through Hyperlang.run/4 and return the door result.
Like run/4, but returns the value or raises with the error.
Functions
Assert on guest stdout. subject is a run/4 result or its meta map.
expected is the list of chunks, a binary compared against the joined
chunks, or a regex matched against them.
@spec program(binary() | map(), map() | keyword()) :: {:ok, Hyperlang.Program.t()} | {:error, [map()]}
Build a program. code is a source binary, or a source map
%{entry:, files:, manifest:} passed to Hyperlang.program/1 unchanged
except for fake bindings.
A binary with a -module(name) attribute becomes name.erl; any other
binary (a result/1 form list or an expression) becomes main.erl.
manifest is a manifest map or a keyword list; its bindings may hold
fake binding maps.
@spec program!(binary() | map(), map() | keyword()) :: Hyperlang.Program.t()
Like program/2, but returns the program or raises with the diagnostics.
@spec run( Hyperlang.Program.t() | binary() | map(), atom() | binary(), list(), keyword() ) :: {:ok, term(), map()} | {:error, term()}
Run export through Hyperlang.run/4 and return the door result.
target is a program or source code; code is compiled with program/2
using opts[:manifest] plus the binding modules named by
opts[:bindings]. Options:
:bindings—%{name => fake_map | {module, state}}:policy— the run policy (map or keyword list):syscall— the host syscall door:manifest— manifest for code targets
@spec run!( Hyperlang.Program.t() | binary() | map(), atom() | binary(), list(), keyword() ) :: term()
Like run/4, but returns the value or raises with the error.
@spec start_actor!(Hyperlang.Program.t(), term(), keyword()) :: pid()
Start the program's callback module as an unlinked GenServer and return
its pid. arg is the guest init/1 argument. Options are :bindings,
:policy, and :syscall, as for run/4; they become the actor's spec.