Расширена статистика для разделов #22

This commit is contained in:
2026-05-28 13:49:08 +03:00
parent 2dac5e5102
commit 1986fd4694
20 changed files with 1189 additions and 71 deletions
+148 -13
View File
@@ -3,13 +3,24 @@
%%%
%%% Покрывает эндпоинты:
%%% 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
%%% - получение общей статистики для всех ролей администраторов
%%% - для 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).
@@ -27,6 +38,31 @@ test() ->
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),
@@ -34,6 +70,14 @@ test() ->
test_stats_with_dates(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.
@@ -41,9 +85,6 @@ test() ->
%%% Тестовые функции
%%%===================================================================
%% @doc Проверяет получение статистики для конкретной роли.
%% strict – ожидаем ключи users_total/events_total/pending_users_total и *_by_day
%% loose – просто убеждаемся, что ответ непустой
-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]),
@@ -51,18 +92,32 @@ test_stats_for_role(RoleName, Token, Strictness) ->
?assert(is_map(Stats)),
case Strictness of
strict ->
HasUsers = maps:is_key(<<"users_total">>, Stats) orelse maps:is_key(<<"users">>, Stats),
HasEvents = maps:is_key(<<"events_total">>, Stats) orelse maps:is_key(<<"events">>, Stats),
?assert(HasUsers orelse HasEvents),
% Проверяем наличие новых ключей
% Тотальные значения
?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)),
% Проверяем, что *_by_day являются списками
% Проверка, что они действительно списки
?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))),
@@ -74,7 +129,6 @@ test_stats_for_role(RoleName, Token, Strictness) ->
end,
ct:pal(" OK: ~p keys", [length(maps:keys(Stats))]).
%% @doc GET /v1/admin/stats?from=...&to=... проверяет фильтрацию по датам.
-spec test_stats_with_dates(binary()) -> ok.
test_stats_with_dates(Token) ->
ct:pal(" TEST: Get stats with date range"),
@@ -84,4 +138,85 @@ test_stats_with_dates(Token) ->
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").
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)]).
@@ -14,9 +14,6 @@
-module(user_verification_tests).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
-export([verification_test/0]).
verification_test() -> test().
-spec test() -> ok.
test() ->