-module(handler_ticket_auto_report_tests). -include_lib("eunit/include/eunit.hrl"). -include("records.hrl"). %% ---------------------------------------------------------------- %% Фикстуры %% ---------------------------------------------------------------- setup() -> catch mnesia:stop(), case mnesia:start() of {atomic, ok} -> ok; ok -> ok end, {atomic, ok} = mnesia:create_table(ticket, [ {attributes, record_info(fields, ticket)}, {ram_copies, [node()]}]), %% rate-limit ETS может остаться между тестами catch ets:delete(eventhub_ticket_rate), ok. cleanup(_) -> catch mnesia:delete_table(ticket), catch ets:delete(eventhub_ticket_rate), mnesia:stop(). %% ---------------------------------------------------------------- %% Тесты: авто-регистрация backend-ошибок через handler_utils %% ---------------------------------------------------------------- handler_ticket_auto_report_test_() -> {foreach, fun setup/0, fun cleanup/1, [ {"maybe_report creates backend ticket async", fun test_maybe_report_creates/0}, {"maybe_report skips /tickets routes", fun test_maybe_report_skips_tickets/0}, {"duplicate maybe_report bumps count", fun test_maybe_report_dedupe/0} ]}. test_maybe_report_creates() -> Before = length(core_ticket:list_all()), ok = handler_utils:maybe_report_internal_error( #{<<"route">> => <<"/v1/calendars">>, <<"method">> => <<"GET">>}, <<"simulated_500">>, #{} ), Ticket = wait_for_ticket(<<"simulated_500">>, 20), ?assertEqual(Before + 1, length(core_ticket:list_all())), ?assertEqual(<<"backend">>, Ticket#ticket.source), ?assert(is_binary(Ticket#ticket.error_hash)), ?assert(byte_size(Ticket#ticket.error_hash) > 0). test_maybe_report_skips_tickets() -> Before = length(core_ticket:list_all()), ok = handler_utils:maybe_report_internal_error( #{<<"route">> => <<"/v1/tickets">>, <<"method">> => <<"POST">>}, <<"ticket_path_should_not_report">>, #{} ), timer:sleep(200), ?assertEqual(Before, length(core_ticket:list_all())). test_maybe_report_dedupe() -> Msg = <<"same_backend_error">>, ok = handler_utils:maybe_report_internal_error( #{<<"route">> => <<"/v1/events">>, <<"method">> => <<"POST">>}, Msg, #{}), T1 = wait_for_ticket(Msg, 20), ok = handler_utils:maybe_report_internal_error( #{<<"route">> => <<"/v1/events">>, <<"method">> => <<"POST">>}, Msg, #{}), T2 = wait_for_ticket_count(Msg, 2, 20), ?assertEqual(T1#ticket.id, T2#ticket.id), ?assertEqual(2, T2#ticket.count). %% ---------------------------------------------------------------- %% Helpers %% ---------------------------------------------------------------- wait_for_ticket(Msg, 0) -> error({timeout_waiting_ticket, Msg}); wait_for_ticket(Msg, Retries) -> case [T || T <- core_ticket:list_all(), T#ticket.error_message =:= Msg] of [T | _] -> T; [] -> timer:sleep(50), wait_for_ticket(Msg, Retries - 1) end. wait_for_ticket_count(Msg, MinCount, 0) -> error({timeout_waiting_count, Msg, MinCount}); wait_for_ticket_count(Msg, MinCount, Retries) -> case [T || T <- core_ticket:list_all(), T#ticket.error_message =:= Msg] of [T | _] when T#ticket.count >= MinCount -> T; _ -> timer:sleep(50), wait_for_ticket_count(Msg, MinCount, Retries - 1) end.