Current section
Files
Jump to
Current section
Files
src/aion@testing.erl
-module(aion@testing).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aion/testing.gleam").
-export([new/0, process_key/1, run/1, advance/2, current_time_milliseconds/1, mock_activity/3, observations/1, clear_observations/1, assert_replay/2]).
-export_type([test_env/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" Pure Gleam workflow test harness.\n"
"\n"
" `aion/testing` is the recommended way to test workflow author code. It runs\n"
" under `gleam test` with no engine, beamr, store, external services, or Rust\n"
" NIFs. Test code initialises a process-scoped `TestEnv`; the test-only\n"
" Erlang module `test/aion_flow_ffi.erl` occupies the same production FFI\n"
" namespace so workflow code and `@external` declarations are byte-identical\n"
" in tests and production.\n"
).
-opaque test_env() :: {test_env, binary()}.
-file("src/aion/testing.gleam", 31).
?DOC(
" Build a fresh `TestEnv` for the current test process.\n"
"\n"
" The simulated clock, activity mock registry, child/query/signal fixtures, and\n"
" observation capture are reset for the current process only.\n"
).
-spec new() -> {ok, test_env()} | {error, aion@error:engine_error()}.
new() ->
case aion_flow_ffi:testing_reset() of
{ok, Process_key} ->
{ok, {test_env, Process_key}};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/testing.gleam", 39).
?DOC(" Return the process key assigned by the test FFI double.\n").
-spec process_key(test_env()) -> binary().
process_key(Env) ->
erlang:element(2, Env).
-file("src/aion/testing.gleam", 44).
?DOC(" Run a workflow thunk under a fresh process-scoped test environment.\n").
-spec run(fun((test_env()) -> EPA)) -> {ok, EPA} |
{error, aion@error:engine_error()}.
run(Workflow) ->
case new() of
{ok, Env} ->
{ok, Workflow(Env)};
{error, Engine_error} ->
{error, Engine_error}
end.
-file("src/aion/testing.gleam", 52).
?DOC(" Advance the simulated test clock by a canonical duration.\n").
-spec advance(test_env(), aion@duration:duration()) -> {ok, test_env()} |
{error, aion@error:engine_error()}.
advance(Env, By) ->
aion@testing@clock:advance(Env, By).
-file("src/aion/testing.gleam", 60).
?DOC(" Return the current simulated clock value in milliseconds.\n").
-spec current_time_milliseconds(test_env()) -> {ok, integer()} |
{error, aion@error:engine_error()}.
current_time_milliseconds(Env) ->
aion@testing@clock:current_time_milliseconds(Env).
-file("src/aion/testing.gleam", 67).
?DOC(" Register a typed activity mock for the current test process.\n").
-spec mock_activity(
test_env(),
aion@activity:activity(EPH, EPI),
fun((EPH) -> {ok, EPI} | {error, aion@error:activity_error()})
) -> {ok, test_env()} | {error, aion@error:engine_error()}.
mock_activity(Env, Activity_value, Handler) ->
aion@testing@mock:activity(Env, Activity_value, Handler).
-file("src/aion/testing.gleam", 76).
?DOC(" Capture the current observation sequence emitted by the test FFI double.\n").
-spec observations(test_env()) -> {ok, binary()} |
{error, aion@error:engine_error()}.
observations(_) ->
case aion_flow_ffi:testing_observations() of
{ok, Raw} ->
{ok, Raw};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/testing.gleam", 84).
?DOC(" Clear the observation sequence for the current process.\n").
-spec clear_observations(test_env()) -> {ok, test_env()} |
{error, aion@error:engine_error()}.
clear_observations(Env) ->
case aion_flow_ffi:testing_clear_observations() of
{ok, _} ->
{ok, Env};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/testing.gleam", 96).
?DOC(
" Assert that a workflow emits the same observation sequence on a second run.\n"
"\n"
" This mirrors AD's production non-determinism detection in a lightweight test\n"
" harness: if replay emits different observable commands, the helper returns a\n"
" clear `ReplayError` diagnostic instead of requiring a live engine.\n"
).
-spec assert_replay(test_env(), fun(() -> {ok, EPT} | {error, EPU})) -> {ok,
EPT} |
{error, aion@testing@replay:replay_error(EPU)}.
assert_replay(Env, Workflow) ->
aion@testing@replay:assert_replay(Env, Workflow).