Files
EventHubBack/test/api/admins/admin_stats_tests.erl
T
aleksey 17b2e617d7
Test & Deploy to Snapdeploy / build-test-deploy (push) Failing after 24m46s
bagofix
2026-06-05 23:20:03 +03:00

247 lines
12 KiB
Erlang
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
%%%-------------------------------------------------------------------
%%% @doc Тесты административного API для получения статистики.
%%%
%%% Покрывает эндпоинты:
%%% GET /v1/admin/stats
%%% GET /v1/admin/users/stats
%%% GET /v1/admin/events/stats
%%% GET /v1/admin/reports/stats
%%% GET /v1/admin/reviews/stats
%%% GET /v1/admin/subscriptions/stats
%%% GET /v1/admin/calendars/stats
%%%
%%% Проверяет:
%%% - получение общей статистики для всех ролей администраторов
%%% - для superadmin и admin – наличие ключевых метрик, включая pending_users_total, *_by_day
%%% - для moderator и support ответ непустой
%%% - работу с фильтром по датам (from, to)
%%% - статистику по пользователям (users/stats)
%%% - статистику по событиям (events/stats)
%%% - статистику по жалобам (reports/stats)
%%% - статистику по отзывам (reviews/stats)
%%% - статистику по подпискам (subscriptions/stats)
%%% - статистику по календарям (calendars/stats)
%%% @end
%%%-------------------------------------------------------------------
-module(admin_stats_tests).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
%%%===================================================================
%%% Главная тестовая функция
%%%===================================================================
-spec test() -> ok.
test() ->
ct:pal("=== Admin Stats Tests ==="),
SuperToken = api_test_runner:get_superadmin_token(),
AdminToken = api_test_runner:get_admin_token(),
ModerToken = api_test_runner:get_moderator_token(),
SupportToken = api_test_runner:get_support_token(),
% Создаём тестовые данные для ненулевой статистики
UserToken = api_test_runner:get_user_token(),
CalId = api_test_runner:create_calendar(UserToken, #{title => <<"StatsCal">>}),
EventId = api_test_runner:create_event(UserToken, CalId, #{
title => <<"Stats Event">>,
start_time => api_test_runner:future_date(),
duration => 60
}),
% Бронируем и подтверждаем, чтобы можно было оставить отзыв
#{<<"id">> := BookingId} = api_test_runner:client_post(
<<"/v1/events/", EventId/binary, "/bookings">>, UserToken, #{}),
api_test_runner:client_put(<<"/v1/bookings/", BookingId/binary>>, UserToken,
#{action => <<"confirm">>}),
% Оставляем отзыв
api_test_runner:client_post(<<"/v1/reviews">>, UserToken,
#{target_type => <<"event">>, target_id => EventId, rating => 5, comment => <<"Great!">>}),
% Жалоба
api_test_runner:client_post(<<"/v1/reports">>, UserToken,
#{<<"target_type">> => <<"event">>, <<"target_id">> => EventId, <<"reason">> => <<"Test">>}),
% Подписка
SubUserToken = api_test_runner:get_user_token(),
api_test_runner:client_post(<<"/v1/subscription">>, SubUserToken,
#{<<"action">> => <<"start_trial">>}),
% Общая статистика
test_stats_for_role("Superadmin", SuperToken, strict),
test_stats_for_role("Admin", AdminToken, strict),
test_stats_for_role("Moderator", ModerToken, loose),
test_stats_for_role("Support", SupportToken, loose),
test_stats_with_dates(SuperToken),
test_node_metrics_history(SuperToken),
% Детальная статистика
test_user_stats(SuperToken),
test_event_stats(SuperToken),
test_report_stats(SuperToken),
test_review_stats(SuperToken),
test_subscription_stats(SuperToken),
test_calendar_stats(SuperToken),
ct:pal("=== All admin stats tests passed ==="),
ok.
%%%===================================================================
%%% Тестовые функции
%%%===================================================================
-spec test_stats_for_role(string(), binary(), strict | loose) -> ok.
test_stats_for_role(RoleName, Token, Strictness) ->
ct:pal(" TEST: Get stats for role ~s", [RoleName]),
Stats = api_test_runner:admin_get(<<"/v1/admin/stats">>, Token),
?assert(is_map(Stats)),
case Strictness of
strict ->
% Тотальные значения
?assert(maps:is_key(<<"users_total">>, Stats)),
?assert(maps:is_key(<<"pending_users_total">>, Stats)),
?assert(maps:is_key(<<"events_total">>, Stats)),
?assert(maps:is_key(<<"calendars_total">>, Stats)),
?assert(maps:is_key(<<"reviews_total">>, Stats)),
?assert(maps:is_key(<<"reports_total">>, Stats)),
?assert(maps:is_key(<<"reports_pending">>, Stats)),
?assert(maps:is_key(<<"reports_reviewed">>, Stats)),
?assert(maps:is_key(<<"reports_dismissed">>, Stats)),
?assert(maps:is_key(<<"tickets_total">>, Stats)),
?assert(maps:is_key(<<"tickets_open">>, Stats)),
?assert(maps:is_key(<<"subscriptions_total">>, Stats)),
?assert(maps:is_key(<<"trial_subscriptions">>, Stats)),
% Массивы по дням
?assert(maps:is_key(<<"registrations_by_day">>, Stats)),
?assert(maps:is_key(<<"events_by_day">>, Stats)),
?assert(maps:is_key(<<"reviews_by_day">>, Stats)),
?assert(maps:is_key(<<"calendars_by_day">>, Stats)),
?assert(maps:is_key(<<"reports_by_day">>, Stats)),
?assert(maps:is_key(<<"tickets_by_day">>, Stats)),
?assert(maps:is_key(<<"subscriptions_by_day">>, Stats)),
?assert(maps:is_key(<<"pending_users_by_day">>, Stats)),
% Проверка, что они действительно списки
?assert(is_list(maps:get(<<"registrations_by_day">>, Stats))),
?assert(is_list(maps:get(<<"events_by_day">>, Stats))),
?assert(is_list(maps:get(<<"reviews_by_day">>, Stats))),
?assert(is_list(maps:get(<<"calendars_by_day">>, Stats))),
?assert(is_list(maps:get(<<"reports_by_day">>, Stats))),
?assert(is_list(maps:get(<<"tickets_by_day">>, Stats))),
?assert(is_list(maps:get(<<"subscriptions_by_day">>, Stats))),
?assert(is_list(maps:get(<<"pending_users_by_day">>, Stats)));
loose ->
?assert(map_size(Stats) > 0)
end,
ct:pal(" OK: ~p keys", [length(maps:keys(Stats))]).
-spec test_stats_with_dates(binary()) -> ok.
test_stats_with_dates(Token) ->
ct:pal(" TEST: Get stats with date range"),
From = <<"2026-01-01T00:00:00Z">>,
To = <<"2026-12-31T23:59:59Z">>,
Path = <<"/v1/admin/stats?from=", From/binary, "&to=", To/binary>>,
Stats = api_test_runner:admin_get(Path, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"users_total">>, Stats) orelse maps:is_key(<<"users">>, Stats)),
ct:pal(" OK").
%% @doc Тестирует эндпоинт GET /v1/admin/users/stats.
-spec test_user_stats(binary()) -> ok.
test_user_stats(Token) ->
ct:pal(" TEST: User statistics"),
Stats = api_test_runner:admin_get(<<"/v1/admin/users/stats">>, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"total_users">>, Stats)),
?assert(maps:is_key(<<"users_by_role">>, Stats)),
?assert(maps:is_key(<<"users_by_status">>, Stats)),
?assert(maps:is_key(<<"pending_users">>, Stats)),
ct:pal(" OK: total=~p, pending=~p", [maps:get(<<"total_users">>, Stats), maps:get(<<"pending_users">>, Stats)]).
%% @doc Тестирует эндпоинт GET /v1/admin/events/stats.
-spec test_event_stats(binary()) -> ok.
test_event_stats(Token) ->
ct:pal(" TEST: Event statistics"),
Stats = api_test_runner:admin_get(<<"/v1/admin/events/stats">>, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"total_events">>, Stats)),
?assert(maps:is_key(<<"events_by_type">>, Stats)),
?assert(maps:is_key(<<"events_by_status">>, Stats)),
?assert(maps:is_key(<<"top_events_by_rating">>, Stats)),
?assert(is_list(maps:get(<<"top_events_by_rating">>, Stats))),
ct:pal(" OK: total=~p", [maps:get(<<"total_events">>, Stats)]).
%% @doc Тестирует эндпоинт GET /v1/admin/reports/stats.
-spec test_report_stats(binary()) -> ok.
test_report_stats(Token) ->
ct:pal(" TEST: Report statistics"),
Stats = api_test_runner:admin_get(<<"/v1/admin/reports/stats">>, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"total_reports">>, Stats)),
?assert(maps:is_key(<<"reports_by_target_type">>, Stats)),
?assert(maps:is_key(<<"reports_by_status">>, Stats)),
?assert(maps:is_key(<<"top_targets_by_reports">>, Stats)),
?assert(is_list(maps:get(<<"top_targets_by_reports">>, Stats))),
ct:pal(" OK: total=~p", [maps:get(<<"total_reports">>, Stats)]).
%% @doc Тестирует эндпоинт GET /v1/admin/reviews/stats.
-spec test_review_stats(binary()) -> ok.
test_review_stats(Token) ->
ct:pal(" TEST: Review statistics"),
Stats = api_test_runner:admin_get(<<"/v1/admin/reviews/stats">>, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"total_reviews">>, Stats)),
?assert(maps:is_key(<<"reviews_by_target_type">>, Stats)),
?assert(maps:is_key(<<"reviews_by_status">>, Stats)),
?assert(maps:is_key(<<"top_targets_by_reviews">>, Stats)),
?assert(maps:is_key(<<"top_targets_by_positive_reviews">>, Stats)),
?assert(maps:is_key(<<"top_targets_by_negative_reviews">>, Stats)),
ct:pal(" OK: total=~p", [maps:get(<<"total_reviews">>, Stats)]).
%% @doc Тестирует эндпоинт GET /v1/admin/subscriptions/stats.
-spec test_subscription_stats(binary()) -> ok.
test_subscription_stats(Token) ->
ct:pal(" TEST: Subscription statistics"),
Stats = api_test_runner:admin_get(<<"/v1/admin/subscriptions/stats">>, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"total_subscriptions">>, Stats)),
?assert(maps:is_key(<<"subscriptions_by_plan">>, Stats)),
?assert(maps:is_key(<<"subscriptions_by_status">>, Stats)),
?assert(maps:is_key(<<"trial_subscriptions">>, Stats)),
?assert(maps:is_key(<<"ending_paid_subscriptions">>, Stats)),
?assert(is_list(maps:get(<<"ending_paid_subscriptions">>, Stats))),
ct:pal(" OK: total=~p, trial=~p", [maps:get(<<"total_subscriptions">>, Stats), maps:get(<<"trial_subscriptions">>, Stats)]).
%% @doc Тестирует эндпоинт GET /v1/admin/calendars/stats.
-spec test_calendar_stats(binary()) -> ok.
test_calendar_stats(Token) ->
ct:pal(" TEST: Calendar statistics"),
Stats = api_test_runner:admin_get(<<"/v1/admin/calendars/stats">>, Token),
?assert(is_map(Stats)),
?assert(maps:is_key(<<"total_calendars">>, Stats)),
?assert(maps:is_key(<<"calendars_by_type">>, Stats)),
?assert(maps:is_key(<<"calendars_by_status">>, Stats)),
?assert(maps:is_key(<<"top_calendars_by_reviews">>, Stats)),
?assert(maps:is_key(<<"top_calendars_by_positive_reviews">>, Stats)),
?assert(maps:is_key(<<"top_calendars_by_negative_reviews">>, Stats)),
?assert(maps:is_key(<<"top_calendars_by_rating">>, Stats)),
ct:pal(" OK: total=~p", [maps:get(<<"total_calendars">>, Stats)]).
test_node_metrics_history(Token) ->
ct:pal(" TEST: Node metrics history"),
Now = calendar:universal_time(),
FromSec = calendar:datetime_to_gregorian_seconds(Now) - 300,
ToSec = calendar:datetime_to_gregorian_seconds(Now) + 300,
FromDT = calendar:gregorian_seconds_to_datetime(FromSec),
ToDT = calendar:gregorian_seconds_to_datetime(ToSec),
From = handler_utils:datetime_to_iso8601(FromDT),
To = handler_utils:datetime_to_iso8601(ToDT),
Path = <<"/v1/admin/nodes/metrics?from=", From/binary, "&to=", To/binary>>,
{ok, 200, _, Body} = api_test_runner:admin_request(get, Path, Token, <<"">>),
Metrics = jsx:decode(list_to_binary(Body), [return_maps]),
?assert(is_list(Metrics)),
% Так как монитор работает, метрики уже должны быть
?assert(length(Metrics) >= 1),
% Проверяем структуру первой метрики
First = hd(Metrics),
?assert(maps:is_key(<<"timestamp">>, First)),
?assert(maps:is_key(<<"node">>, First)),
?assert(maps:is_key(<<"memory_total">>, First)),
?assert(maps:is_key(<<"memory_available">>, First)),
?assert(maps:is_key(<<"cpu_utilization">>, First)),
ct:pal(" OK: ~p metrics received", [length(Metrics)]).