138 lines
4.7 KiB
Erlang
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.
|