-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( BEIE, telega@update:update(), fun((telega@bot:context(BEIE, BEIF, nil), telega@update:update()) -> {ok, telega@bot:context(BEIE, BEIF, nil)} | {error, BEIF}) ) -> {{ok, telega@bot:context(BEIE, BEIF, nil)} | {error, BEIF}, 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(BEJU, BEJV, BEJW), telega@update:update()) -> {ok, telega@bot:context(BEJU, BEJV, BEJW)} | {error, BEJV}), telega@bot:session_settings(BEJU, BEJV), BEJW, 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 => <>, 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 => <>, 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 => <>, 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(BEJH, BEJI, nil), telega@update:update()) -> {ok, telega@bot:context(BEJH, BEJI, nil)} | {error, BEJI}), telega@bot:session_settings(BEJH, BEJI), 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(BEIU, any(), nil), fun(() -> BEIU), 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(BEJA, any(), BEJC), fun(() -> BEJA), BEJC, 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 ).