Files
EventHubBack/test/unit/stats_collector_tests.erl
aleksey dd754e28cc
CI / test (push) Failing after 25m40s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped
feat: upsert stats counters, daily buckets and ETS tops for admin metrics. Refs EventHub/EventHubBack#42 #43 #44 #45 #46
2026-07-17 23:10:00 +03:00

138 lines
4.7 KiB
Erlang

-module(stats_collector_tests).
-include_lib("eunit/include/eunit.hrl").
-include("records.hrl").
-define(TABLES, [user, event, calendar, booking, review, report, ticket,
subscription, stats_counter, stats_daily]).
setup() ->
eh_test_support:start_mnesia(),
eh_test_support:ensure_tables(?TABLES),
{ok, _} = stats_collector:start_link(),
ok = stats_collector:subscribe(),
ok.
cleanup(_) ->
case whereis(stats_collector) of
undefined -> ok;
Pid ->
unlink(Pid),
exit(Pid, kill),
wait_dead(Pid, 50)
end,
eh_test_support:delete_tables(?TABLES),
eh_test_support:stop_mnesia(),
ok.
wait_dead(_Pid, 0) -> ok;
wait_dead(Pid, N) ->
case is_process_alive(Pid) of
true ->
timer:sleep(20),
wait_dead(Pid, N - 1);
false -> ok
end.
stats_collector_test_() ->
{foreach, fun setup/0, fun cleanup/1, [
{"backfill from existing rows", fun test_backfill/0},
{"live write updates dims and daily", fun test_live_write/0},
{"status transition moves counters", fun test_status_move/0},
{"core_stats reads via collector", fun test_core_stats_api/0},
{"ticket avg resolution from counters", fun test_ticket_avg_resolution/0},
{"report avg resolution from counters", fun test_report_avg_resolution/0}
]}.
test_backfill() ->
Now = calendar:universal_time(),
ok = mnesia:dirty_write(eh_test_support:make_user(#{
id => <<"u1">>, status => pending, role => user, created_at => Now
})),
ok = mnesia:dirty_write(eh_test_support:make_user(#{
id => <<"u2">>, status => active, role => user, created_at => Now
})),
ok = stats_collector:rebuild(),
?assertEqual(1, core_stats:with_counter({user_by_status, pending}, fun() -> -1 end)),
?assertEqual(1, core_stats:with_counter({user_by_status, active}, fun() -> -1 end)),
{Date, _} = Now,
From = {Date, {0, 0, 0}},
To = {Date, {23, 59, 59}},
Daily = core_stats:with_daily(users_created, From, To, fun() -> [] end),
?assertEqual([{Date, 2}], Daily).
test_live_write() ->
Now = calendar:universal_time(),
ok = mnesia:dirty_write(eh_test_support:make_user(#{
id => <<"live1">>, status => active, created_at => Now
})),
wait_counter({user_by_status, active}, 1, 20),
?assertEqual(1, core_stats:with_counter({user_by_status, active}, fun() -> 0 end)).
test_status_move() ->
Now = calendar:universal_time(),
User = eh_test_support:make_user(#{
id => <<"move1">>, status => pending, created_at => Now
}),
ok = mnesia:dirty_write(User),
wait_counter({user_by_status, pending}, 1, 20),
ok = mnesia:dirty_write(User#user{status = active}),
wait_counter({user_by_status, pending}, 0, 20),
wait_counter({user_by_status, active}, 1, 20),
?assertEqual(0, core_stats:with_counter({user_by_status, pending}, fun() -> -1 end)),
?assertEqual(1, core_stats:with_counter({user_by_status, active}, fun() -> -1 end)).
test_core_stats_api() ->
?assert(core_stats:available()),
Now = calendar:universal_time(),
ok = mnesia:dirty_write(#report{
id = <<"r1">>,
reporter_id = <<"u1">>,
target_type = calendar,
target_id = <<"c1">>,
reason = <<"spam">>,
status = pending,
created_at = Now,
resolved_at = undefined,
resolved_by = <<>>
}),
wait_counter({report_by_status, pending}, 1, 20),
{ok, Dim} = core_stats:get_dim(report, status),
?assertEqual(1, proplists:get_value(pending, Dim, 0)).
test_ticket_avg_resolution() ->
First = {{2026, 1, 1}, {0, 0, 0}},
Closed = {{2026, 1, 1}, {2, 0, 0}}, %% 2 hours
ok = mnesia:dirty_write(#ticket{
id = <<"t_avg">>, reporter_id = <<"u1">>, error_hash = <<"h">>,
error_message = <<"e">>, stacktrace = <<>>, context = <<>>,
count = 1, first_seen = First, last_seen = Closed, status = closed,
assigned_to = <<>>, resolution_note = <<>>, closed_at = Closed,
source = <<"backend">>
}),
wait_counter(ticket_resolved_count, 1, 20),
Avg = core_ticket:avg_resolution_time(),
?assertEqual(2.0, Avg).
test_report_avg_resolution() ->
Created = {{2026, 1, 1}, {0, 0, 0}},
Resolved = {{2026, 1, 1}, {3, 0, 0}}, %% 3 hours
ok = mnesia:dirty_write(#report{
id = <<"r_avg">>, reporter_id = <<"u1">>, target_type = calendar,
target_id = <<"c1">>, reason = <<"spam">>, status = reviewed,
created_at = Created, resolved_at = Resolved, resolved_by = <<"adm">>
}),
wait_counter({report_resolved_count, reviewed}, 1, 20),
Avg = core_report:avg_resolution_time(reviewed),
?assertEqual(3.0, Avg).
wait_counter(Key, Expected, 0) ->
Actual = core_stats:with_counter(Key, fun() -> -999 end),
?assertEqual(Expected, Actual);
wait_counter(Key, Expected, N) ->
case core_stats:with_counter(Key, fun() -> -999 end) of
Expected -> ok;
_ ->
timer:sleep(25),
wait_counter(Key, Expected, N - 1)
end.