Current section
Files
Jump to
Current section
Files
src/telega@testing@handler.erl
-module(telega@testing@handler).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/telega/testing/handler.gleam").
-export([test_handler/3, with_test_bot_advanced_with_dependencies/4, with_test_bot_advanced/3, with_test_bot/3, with_test_bot_with_dependencies/4]).
-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(
" Utilities for testing individual handlers in isolation and\n"
" setting up full bot instances with minimal boilerplate.\n"
"\n"
" ```gleam\n"
" import telega/testing/handler\n"
" import telega/testing/factory\n"
" import telega/testing/mock\n"
"\n"
" // Test a single handler without a router\n"
" pub fn my_handler_test() {\n"
" let #(ctx, calls) = handler.test_handler(\n"
" session: MySession(count: 0),\n"
" update: factory.text_update(text: \"hello\"),\n"
" handler: fn(ctx, _update) { reply.with_text(ctx, \"hi\") |> result.map(fn(_) { ctx }) },\n"
" )\n"
" mock.assert_call_count(from: calls, expected: 1)\n"
" }\n"
"\n"
" // Test with a full bot (router + actors)\n"
" pub fn my_bot_test() {\n"
" handler.with_test_bot(\n"
" router: build_router(),\n"
" session: fn() { MySession(count: 0) },\n"
" handler: fn(bot_subject, calls) {\n"
" let update = factory.command_update(\"start\")\n"
" bot.handle_update(bot_subject:, update:)\n"
" mock.assert_call_count(from: calls, expected: 1)\n"
" },\n"
" )\n"
" }\n"
" ```\n"
).
-file("src/telega/testing/handler.gleam", 50).
?DOC(
" Runs a handler function in isolation with a mock client.\n"
" Returns the resulting context and the recorded API calls subject.\n"
"\n"
" Use this to test a single handler without involving the router or actor system.\n"
).
-spec test_handler(
BMDN,
telega@update:update(),
fun((telega@bot:context(BMDN, BMDO, nil), telega@update:update()) -> {ok,
telega@bot:context(BMDN, BMDO, nil)} |
{error, BMDO})
) -> {{ok, telega@bot:context(BMDN, BMDO, nil)} | {error, BMDO},
gleam@erlang@process:subject(telega@testing@mock:api_call())}.
test_handler(Session, Update, Handler) ->
{Client, Calls} = telega@testing@mock:message_client(),
Config = telega@testing@context:config_with_client(Client),
Ctx = {context,
<<"test_chat:123"/utf8>>,
Update,
Config,
Session,
nil,
gleam@erlang@process:new_subject(),
none,
none,
telega@testing@factory:bot_user()},
Result = Handler(Ctx, Update),
{Result, Calls}.
-file("src/telega/testing/handler.gleam", 127).
?DOC(
" Lowest-level dependencies-aware bot harness: custom router handler, session settings,\n"
" and injected `dependencies`. `with_test_bot` / `with_test_bot_with_dependencies` /\n"
" `with_test_bot_advanced` all funnel through here.\n"
).
-spec with_test_bot_advanced_with_dependencies(
fun((telega@bot:context(BMFD, BMFE, BMFF), telega@update:update()) -> {ok,
telega@bot:context(BMFD, BMFE, BMFF)} |
{error, BMFE}),
telega@bot:session_settings(BMFD, BMFE),
BMFF,
fun((gleam@erlang@process:subject(telega@bot:bot_message()), gleam@erlang@process:subject(telega@testing@mock:api_call())) -> nil)
) -> nil.
with_test_bot_advanced_with_dependencies(
Router_handler,
Session_settings,
Dependencies,
Handler
) ->
{Client, Calls} = telega@testing@mock:message_client(),
Config = telega@testing@context:config_with_client(Client),
Reg@1 = case telega@internal@registry:start(<<"test_handler"/utf8>>) of
{ok, Reg} -> Reg;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"telega/testing/handler"/utf8>>,
function => <<"with_test_bot_advanced_with_dependencies"/utf8>>,
line => 139,
value => _assert_fail,
start => 4993,
'end' => 5044,
pattern_start => 5004,
pattern_end => 5011})
end,
Chat_factory_started@1 = case begin
_pipe = gleam@otp@factory_supervisor:worker_child(
fun telega@bot:start_chat_instance/1
),
_pipe@1 = gleam@otp@factory_supervisor:restart_strategy(
_pipe,
transient
),
gleam@otp@factory_supervisor:start(_pipe@1)
end of
{ok, Chat_factory_started} -> Chat_factory_started;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"telega/testing/handler"/utf8>>,
function => <<"with_test_bot_advanced_with_dependencies"/utf8>>,
line => 140,
value => _assert_fail@1,
start => 5047,
'end' => 5201,
pattern_start => 5058,
pattern_end => 5082})
end,
Started@1 = case telega@bot:start(
Reg@1,
Config,
telega@testing@factory:bot_user(),
Router_handler,
[],
Session_settings,
fun(_, _) -> {ok, nil} end,
Dependencies,
erlang:element(3, Chat_factory_started@1),
none
) of
{ok, Started} -> Started;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"telega/testing/handler"/utf8>>,
function => <<"with_test_bot_advanced_with_dependencies"/utf8>>,
line => 145,
value => _assert_fail@2,
start => 5205,
'end' => 5529,
pattern_start => 5216,
pattern_end => 5227})
end,
Handler(erlang:element(3, Started@1), Calls),
_ = telega@internal@registry:stop(Reg@1),
nil.
-file("src/telega/testing/handler.gleam", 108).
?DOC(" Lower-level variant with custom router handler and session settings.\n").
-spec with_test_bot_advanced(
fun((telega@bot:context(BMEQ, BMER, nil), telega@update:update()) -> {ok,
telega@bot:context(BMEQ, BMER, nil)} |
{error, BMER}),
telega@bot:session_settings(BMEQ, BMER),
fun((gleam@erlang@process:subject(telega@bot:bot_message()), gleam@erlang@process:subject(telega@testing@mock:api_call())) -> nil)
) -> nil.
with_test_bot_advanced(Router_handler, Session_settings, Handler) ->
with_test_bot_advanced_with_dependencies(
Router_handler,
Session_settings,
nil,
Handler
).
-file("src/telega/testing/handler.gleam", 78).
?DOC(
" Starts a full bot with router, registry, and actors using a mock client.\n"
" Calls `handler` with the bot subject and API calls subject, then cleans up.\n"
"\n"
" Use this to test end-to-end update handling with minimal boilerplate.\n"
).
-spec with_test_bot(
telega@router:router(BMED, any(), nil),
fun(() -> BMED),
fun((gleam@erlang@process:subject(telega@bot:bot_message()), gleam@erlang@process:subject(telega@testing@mock:api_call())) -> nil)
) -> nil.
with_test_bot(Router, Default_session, Handler) ->
with_test_bot_advanced(
fun(Ctx, Update) -> telega@router:handle(Router, Ctx, Update) end,
telega@testing@context:session_settings(Default_session),
Handler
).
-file("src/telega/testing/handler.gleam", 93).
?DOC(
" Like `with_test_bot`, but for a router whose handlers need injected\n"
" dependencies (`dependencies`). Starts the bot with the given `dependencies` so handlers can\n"
" read services from `ctx.dependencies`.\n"
).
-spec with_test_bot_with_dependencies(
telega@router:router(BMEJ, any(), BMEL),
fun(() -> BMEJ),
BMEL,
fun((gleam@erlang@process:subject(telega@bot:bot_message()), gleam@erlang@process:subject(telega@testing@mock:api_call())) -> nil)
) -> nil.
with_test_bot_with_dependencies(Router, Default_session, Dependencies, Handler) ->
with_test_bot_advanced_with_dependencies(
fun(Ctx, Update) -> telega@router:handle(Router, Ctx, Update) end,
telega@testing@context:session_settings(Default_session),
Dependencies,
Handler
).