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