-module(aion@testing@mock).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/aion/testing/mock.gleam").
-export([activity/3, child/6]).
-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(
" Typed activity and child-workflow mock registries for `aion/testing`.\n"
"\n"
" Tests register activity mocks against an `Activity(i, o)` value so the\n"
" handler is statically checked against the same input and output types that\n"
" `workflow.run` will use, and child doubles against the input/output/error\n"
" codecs that `workflow.spawn_and_wait` will use. The test-only FFI double\n"
" stores a type-erased wrapper in process-scoped state and intercepts\n"
" activity dispatch and child spawn by name.\n"
).
-file("src/aion/testing/mock.gleam", 82).
-spec activity_error_to_raw(aion@error:activity_error()) -> binary().
activity_error_to_raw(Activity_error) ->
case Activity_error of
{retryable, Message, _} ->
<<"retryable:"/utf8, Message/binary>>;
{terminal, Message@1, _} ->
<<"terminal:"/utf8, Message@1/binary>>;
{activity_decode_failed, Decode_error} ->
<<"terminal:activity decode failed: "/utf8,
(erlang:element(2, Decode_error))/binary>>;
{activity_timed_out, {timed_out, Message@2}} ->
<<"timeout:"/utf8, Message@2/binary>>;
{activity_cancelled, {cancelled, Reason}} ->
<<"cancelled:"/utf8, Reason/binary>>;
{activity_non_deterministic, {non_determinism_violation, Message@3}} ->
<<"non_determinism:"/utf8, Message@3/binary>>;
{activity_engine_failure, Message@4} ->
Message@4
end.
-file("src/aion/testing/mock.gleam", 19).
?DOC(
" Register a typed activity mock for the current process.\n"
"\n"
" A handler whose input or output type does not match the supplied activity will\n"
" fail at `gleam build`, before the workflow test can run.\n"
).
-spec activity(
EKA,
aion@activity:activity(EKB, EKC),
fun((EKB) -> {ok, EKC} | {error, aion@error:activity_error()})
) -> {ok, EKA} | {error, aion@error:engine_error()}.
activity(Env, Activity_value, Handler) ->
Input_codec = aion@activity:input_codec(Activity_value),
Output_codec = aion@activity:output_codec(Activity_value),
Name = aion@activity:name(Activity_value),
Raw_handler = fun(Raw_input) ->
case (erlang:element(3, Input_codec))(Raw_input) of
{ok, Typed_input} ->
case Handler(Typed_input) of
{ok, Typed_output} ->
{ok, (erlang:element(2, Output_codec))(Typed_output)};
{error, Activity_error} ->
{error, activity_error_to_raw(Activity_error)}
end;
{error, Decode_error} ->
{error,
<<"terminal:mock input decode failed: "/utf8,
(erlang:element(2, Decode_error))/binary>>}
end
end,
case aion_flow_ffi:testing_register_activity_mock(Name, Raw_handler) of
{ok, _} ->
{ok, Env};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.
-file("src/aion/testing/mock.gleam", 54).
?DOC(
" Register a typed child-workflow double for the current test process.\n"
"\n"
" `workflow.spawn_and_wait(name, ...)` calls with the same `name` execute\n"
" `handler` synchronously and record its typed result as the child terminal:\n"
" `Ok` is decoded by the parent's output codec and a typed `Error` surfaces\n"
" as `error.ChildWorkflowFailed`. Registering the child module's real\n"
" `execute` function as the handler runs the full child workflow body —\n"
" including its own activity dispatches against this process's activity\n"
" mocks — inside the parent test.\n"
).
-spec child(
EKJ,
binary(),
aion@codec:codec(EKK),
aion@codec:codec(EKM),
aion@codec:codec(EKO),
fun((EKK) -> {ok, EKM} | {error, EKO})
) -> {ok, EKJ} | {error, aion@error:engine_error()}.
child(
Env,
Name,
Child_input_codec,
Child_output_codec,
Child_error_codec,
Handler
) ->
Raw_handler = fun(Raw_input) ->
case (erlang:element(3, Child_input_codec))(Raw_input) of
{ok, Typed_input} ->
case Handler(Typed_input) of
{ok, Typed_output} ->
{ok,
<<"ok:"/utf8,
((erlang:element(2, Child_output_codec))(
Typed_output
))/binary>>};
{error, Workflow_error} ->
{ok,
<<"error:"/utf8,
((erlang:element(2, Child_error_codec))(
Workflow_error
))/binary>>}
end;
{error, Decode_error} ->
{error,
<<"child mock input decode failed: "/utf8,
(erlang:element(2, Decode_error))/binary>>}
end
end,
case aion_flow_ffi:testing_register_child_mock(Name, Raw_handler) of
{ok, _} ->
{ok, Env};
{error, Raw_error} ->
{error, {engine_failure, Raw_error}}
end.