Files
EventHubBack/test/api/admins/admin_subscriptions_tests.erl
T

290 lines
13 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/subscriptions
%%% GET /v1/admin/subscriptions/:id
%%% PUT /v1/admin/subscriptions/:id
%%% DELETE /v1/admin/subscriptions/:id
%%%
%%% Проверяет:
%%% - получение списка подписок
%%% - получение подписки по ID
%%% - обновление подписки (изменение плана, статуса)
%%% - удаление подписки
%%% - **фильтрацию** по статусу и плану
%%% - **комбинации фильтров** (status + plan)
%%% - **пагинацию** (limit/offset, xtotalcount)
%%% - **сортировку** по created_at и expires_at
%%% - **комбинации сортировки с фильтрацией и пагинацией**
%%% @end
%%%-------------------------------------------------------------------
-module(admin_subscriptions_tests).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
%%%===================================================================
%%% Главная тестовая функция
%%%===================================================================
-spec test() -> ok.
test() ->
ct:pal("=== Admin Subscriptions Tests ==="),
Token = api_test_runner:get_admin_token(),
UserToken = api_test_runner:get_user_token(),
% Создаём две подписки для разных проверок
#{<<"id">> := Sub1Id} = api_test_runner:client_post(<<"/v1/subscription">>, UserToken,
#{<<"action">> => <<"start_trial">>}),
% Для второй подписки создаём нового пользователя
User2Email = api_test_runner:unique_email(<<"sub2">>),
User2Token = api_test_runner:register_and_login(User2Email, <<"pass2">>),
#{<<"id">> := Sub2Id} = api_test_runner:client_post(<<"/v1/subscription">>, User2Token,
#{<<"action">> => <<"start_trial">>}),
% Основные проверки
test_list_subscriptions(Token, Sub1Id),
test_get_subscription(Token, Sub1Id),
test_update_subscription(Token, Sub1Id),
% Фильтрация
test_filter_subscriptions(Token),
% Комбинации фильтров
test_filter_combined_status_plan(Token),
test_filter_combined_plan_status(Token),
% Сортировка
test_sort_by_created_at_desc(Token),
test_sort_by_created_at_asc(Token),
test_sort_by_expires_at_asc(Token),
% Фильтр + сортировка
test_filter_status_sort_created_at(Token),
test_filter_plan_sort_expires_at(Token),
% Пагинация
test_subscription_pagination(Token),
test_pagination_headers(Token),
% Фильтр + пагинация
test_filter_status_with_pagination(Token),
% Фильтр + сортировка + пагинация
test_filter_sort_pagination_combined(Token),
% Удаляем вторую подписку, чтобы проверить delete
test_delete_subscription(Token, Sub2Id),
ct:pal("=== All admin subscriptions tests passed ==="),
ok.
%%%===================================================================
%%% Тестовые функции
%%%===================================================================
%% @doc GET /v1/admin/subscriptions список подписок.
-spec test_list_subscriptions(binary(), binary()) -> ok.
test_list_subscriptions(Token, SubId) ->
ct:pal(" TEST: List all subscriptions"),
Subs = api_test_runner:admin_get(<<"/v1/admin/subscriptions">>, Token),
?assert(is_list(Subs)),
?assert(length(Subs) >= 2),
?assert(lists:any(fun(S) -> maps:get(<<"id">>, S) =:= SubId end, Subs)),
ct:pal(" OK: ~p subscriptions", [length(Subs)]).
%% @doc GET /v1/admin/subscriptions/:id получение подписки по ID.
-spec test_get_subscription(binary(), binary()) -> ok.
test_get_subscription(Token, SubId) ->
ct:pal(" TEST: Get subscription by ID"),
Path = <<"/v1/admin/subscriptions/", SubId/binary>>,
Sub = api_test_runner:admin_get(Path, Token),
?assertEqual(SubId, maps:get(<<"id">>, Sub)),
?assert(maps:is_key(<<"plan">>, Sub)),
ct:pal(" OK").
%% @doc PUT /v1/admin/subscriptions/:id обновление подписки (план + статус).
-spec test_update_subscription(binary(), binary()) -> ok.
test_update_subscription(Token, SubId) ->
ct:pal(" TEST: Update subscription (change plan)"),
Path = <<"/v1/admin/subscriptions/", SubId/binary>>,
Updated = api_test_runner:admin_put(Path, Token, #{
<<"plan">> => <<"biannual">>,
<<"status">> => <<"active">>
}),
?assertEqual(<<"biannual">>, maps:get(<<"plan">>, Updated)),
?assertEqual(<<"active">>, maps:get(<<"status">>, Updated)),
ct:pal(" OK").
%% @doc DELETE /v1/admin/subscriptions/:id удаление подписки.
-spec test_delete_subscription(binary(), binary()) -> ok.
test_delete_subscription(Token, SubId) ->
ct:pal(" TEST: Delete subscription"),
Path = <<"/v1/admin/subscriptions/", SubId/binary>>,
Deleted = api_test_runner:admin_delete(Path, Token),
?assertEqual(<<"deleted">>, maps:get(<<"status">>, Deleted)),
ct:pal(" OK").
%% ── Фильтрация ──
%% @doc GET /v1/admin/subscriptions?status=...&plan=... фильтрация.
-spec test_filter_subscriptions(binary()) -> ok.
test_filter_subscriptions(Token) ->
ct:pal(" TEST: Filter subscriptions by status=active"),
Subs = api_test_runner:admin_get(<<"/v1/admin/subscriptions?status=active">>, Token),
?assert(is_list(Subs)),
?assert(length(Subs) >= 1),
[?assertEqual(<<"active">>, maps:get(<<"status">>, S)) || S <- Subs],
ct:pal(" OK: ~p active subscriptions", [length(Subs)]),
ct:pal(" TEST: Filter subscriptions by plan=biannual"),
Subs2 = api_test_runner:admin_get(<<"/v1/admin/subscriptions?plan=biannual">>, Token),
?assert(is_list(Subs2)),
?assert(length(Subs2) >= 1),
[?assertEqual(<<"biannual">>, maps:get(<<"plan">>, S)) || S <- Subs2],
ct:pal(" OK: ~p biannual subscriptions", [length(Subs2)]).
%% ── Комбинации фильтров ──
%% @doc Комбинация фильтров: status=active&plan=biannual
-spec test_filter_combined_status_plan(binary()) -> ok.
test_filter_combined_status_plan(Token) ->
ct:pal(" TEST: Combined filter (status=active & plan=biannual)"),
Subs = api_test_runner:admin_get(
<<"/v1/admin/subscriptions?status=active&plan=biannual">>, Token),
?assert(is_list(Subs)),
[?assertEqual(<<"active">>, maps:get(<<"status">>, S)) || S <- Subs],
[?assertEqual(<<"biannual">>, maps:get(<<"plan">>, S)) || S <- Subs],
ct:pal(" OK: ~p subscriptions match both criteria", [length(Subs)]).
%% @doc Комбинация фильтров: plan=biannual&status=active (обратный порядок)
-spec test_filter_combined_plan_status(binary()) -> ok.
test_filter_combined_plan_status(Token) ->
ct:pal(" TEST: Combined filter (plan=biannual & status=active)"),
Subs = api_test_runner:admin_get(
<<"/v1/admin/subscriptions?plan=biannual&status=active">>, Token),
?assert(is_list(Subs)),
[?assertEqual(<<"active">>, maps:get(<<"status">>, S)) || S <- Subs],
[?assertEqual(<<"biannual">>, maps:get(<<"plan">>, S)) || S <- Subs],
ct:pal(" OK: ~p subscriptions match both criteria", [length(Subs)]).
%% ── Сортировка ──
%% @doc Сортировка по created_at по убыванию.
-spec test_sort_by_created_at_desc(binary()) -> ok.
test_sort_by_created_at_desc(Token) ->
ct:pal(" TEST: Sort subscriptions by created_at desc"),
{ok, 200, _, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?sort=created_at&order=desc">>, Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
Dates = [maps:get(<<"created_at">>, S) || S <- Subs],
Sorted = lists:reverse(lists:sort(Dates)),
?assertEqual(Sorted, Dates),
ct:pal(" OK: sorted by created_at desc").
%% @doc Сортировка по created_at по возрастанию.
-spec test_sort_by_created_at_asc(binary()) -> ok.
test_sort_by_created_at_asc(Token) ->
ct:pal(" TEST: Sort subscriptions by created_at asc"),
{ok, 200, _, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?sort=created_at&order=asc">>, Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
Dates = [maps:get(<<"created_at">>, S) || S <- Subs],
?assertEqual(lists:sort(Dates), Dates),
ct:pal(" OK: sorted by created_at asc").
%% @doc Сортировка по expires_at по возрастанию.
-spec test_sort_by_expires_at_asc(binary()) -> ok.
test_sort_by_expires_at_asc(Token) ->
ct:pal(" TEST: Sort subscriptions by expires_at asc"),
{ok, 200, _, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?sort=expires_at&order=asc">>, Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
Dates = [maps:get(<<"expires_at">>, S) || S <- Subs],
?assertEqual(lists:sort(Dates), Dates),
ct:pal(" OK: sorted by expires_at asc").
%% ── Фильтр + сортировка ──
%% @doc Фильтр status=active + сортировка по created_at desc.
-spec test_filter_status_sort_created_at(binary()) -> ok.
test_filter_status_sort_created_at(Token) ->
ct:pal(" TEST: Filter status=active + sort by created_at desc"),
{ok, 200, _, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?status=active&sort=created_at&order=desc">>, Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
[?assertEqual(<<"active">>, maps:get(<<"status">>, S)) || S <- Subs],
Dates = [maps:get(<<"created_at">>, S) || S <- Subs],
Sorted = lists:reverse(lists:sort(Dates)),
?assertEqual(Sorted, Dates),
ct:pal(" OK: ~p subscriptions sorted", [length(Subs)]).
%% @doc Фильтр plan=biannual + сортировка по expires_at asc.
-spec test_filter_plan_sort_expires_at(binary()) -> ok.
test_filter_plan_sort_expires_at(Token) ->
ct:pal(" TEST: Filter plan=biannual + sort by expires_at asc"),
{ok, 200, _, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?plan=biannual&sort=expires_at&order=asc">>, Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
[?assertEqual(<<"biannual">>, maps:get(<<"plan">>, S)) || S <- Subs],
Dates = [maps:get(<<"expires_at">>, S) || S <- Subs],
?assertEqual(lists:sort(Dates), Dates),
ct:pal(" OK: ~p subscriptions sorted", [length(Subs)]).
%% ── Пагинация ──
%% @doc GET /v1/admin/subscriptions?limit=...&offset=... пагинация.
-spec test_subscription_pagination(binary()) -> ok.
test_subscription_pagination(Token) ->
ct:pal(" TEST: Subscription pagination"),
Page1 = api_test_runner:admin_get(<<"/v1/admin/subscriptions?limit=1&offset=0">>, Token),
?assert(length(Page1) >= 1),
Page2 = api_test_runner:admin_get(<<"/v1/admin/subscriptions?limit=1&offset=1">>, Token),
?assert(length(Page2) >= 1),
Id1 = maps:get(<<"id">>, hd(Page1)),
Id2 = maps:get(<<"id">>, hd(Page2)),
?assertNotEqual(Id1, Id2),
ct:pal(" OK").
%% @doc Проверка заголовков пагинации (x-total-count).
-spec test_pagination_headers(binary()) -> ok.
test_pagination_headers(Token) ->
ct:pal(" TEST: Pagination headers (x-total-count)"),
{ok, 200, Headers, _} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?limit=2&offset=0">>, Token, <<"">>),
Total = list_to_integer(
proplists:get_value("x-total-count", Headers, "0")),
?assert(Total >= 1),
ct:pal(" OK: total count = ~p", [Total]).
%% ── Фильтр + пагинация ──
%% @doc Фильтр status=active + пагинация limit=1.
-spec test_filter_status_with_pagination(binary()) -> ok.
test_filter_status_with_pagination(Token) ->
ct:pal(" TEST: Filter status=active + pagination (limit=1)"),
{ok, 200, Headers, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?status=active&limit=1&offset=0">>, Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
?assert(length(Subs) =< 1),
[?assertEqual(<<"active">>, maps:get(<<"status">>, S)) || S <- Subs],
Total = list_to_integer(
proplists:get_value("x-total-count", Headers, "0")),
?assert(Total >= 1),
ct:pal(" OK: total count = ~p", [Total]).
%% ── Фильтр + сортировка + пагинация ──
%% @doc Фильтр status=active + сортировка по created_at desc + пагинация limit=1.
-spec test_filter_sort_pagination_combined(binary()) -> ok.
test_filter_sort_pagination_combined(Token) ->
ct:pal(" TEST: Filter + sort + pagination (status=active, sort=created_at desc, limit=1)"),
{ok, 200, Headers, Body} = api_test_runner:admin_request(get,
<<"/v1/admin/subscriptions?status=active&sort=created_at&order=desc&limit=1&offset=0">>,
Token, <<"">>),
Subs = jsx:decode(list_to_binary(Body), [return_maps]),
?assert(length(Subs) =< 1),
[?assertEqual(<<"active">>, maps:get(<<"status">>, S)) || S <- Subs],
Total = list_to_integer(
proplists:get_value("x-total-count", Headers, "0")),
?assert(Total >= 1),
ct:pal(" OK: total count = ~p", [Total]).