Hyperlang.Test (Hyperlang v0.1.0)

Copy Markdown View Source

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, []
end

Fake 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.

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.

Functions

assert_stdout(subject, expected)

(macro)

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.

program(code, manifest \\ %{})

@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.

program!(code, manifest \\ %{})

@spec program!(binary() | map(), map() | keyword()) :: Hyperlang.Program.t()

Like program/2, but returns the program or raises with the diagnostics.

run(target, export, args, opts \\ [])

@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

run!(target, export, args, opts \\ [])

@spec run!(
  Hyperlang.Program.t() | binary() | map(),
  atom() | binary(),
  list(),
  keyword()
) :: term()

Like run/4, but returns the value or raises with the error.

start_actor!(program, arg, opts \\ [])

@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.