180 lines
8.3 KiB
Erlang
180 lines
8.3 KiB
Erlang
%%%-------------------------------------------------------------------
|
||
%%% @doc Тесты административного API для управления администраторами.
|
||
%%%
|
||
%%% Покрывает эндпоинты:
|
||
%%% GET /v1/admin/admins
|
||
%%% POST /v1/admin/admins
|
||
%%% GET /v1/admin/admins/:id
|
||
%%% PUT /v1/admin/admins/:id
|
||
%%% DELETE /v1/admin/admins/:id
|
||
%%%
|
||
%%% Проверяет:
|
||
%%% - получение списка администраторов (только суперадмин)
|
||
%%% - создание нового администратора
|
||
%%% - получение администратора по ID
|
||
%%% - обновление администратора
|
||
%%% - удаление (блокировку) администратора
|
||
%%% - ошибки 403 для обычного администратора
|
||
%%% - ошибки 409 (дубликат email) и 400 (неверная роль) при создании
|
||
%%% @end
|
||
%%%-------------------------------------------------------------------
|
||
-module(admin_admins_tests).
|
||
-include_lib("eunit/include/eunit.hrl").
|
||
|
||
-export([test/0]).
|
||
|
||
%%%===================================================================
|
||
%%% Главная тестовая функция
|
||
%%%===================================================================
|
||
|
||
-spec test() -> ok.
|
||
test() ->
|
||
ct:pal("=== Admin Admins Tests ==="),
|
||
SuperToken = api_test_runner:get_superadmin_token(),
|
||
|
||
% Создаём нового администратора для проверки CRUD (у него будет свой ID)
|
||
AdminEmail = api_test_runner:unique_email(<<"newadmin.admins.tests">>),
|
||
AdminPassword = <<"AdminPass123">>,
|
||
#{<<"id">> := AdminId} = create_admin(SuperToken, AdminEmail, AdminPassword, <<"admin">>),
|
||
|
||
% Токен обычного администратора (уже существующего admin@eventhub.local)
|
||
AdminToken = api_test_runner:get_admin_token(),
|
||
|
||
% Тесты с правами суперадмина
|
||
test_list_admins(SuperToken),
|
||
test_get_admin(SuperToken, AdminId),
|
||
test_update_admin(SuperToken, AdminId),
|
||
test_delete_admin(SuperToken, AdminId),
|
||
|
||
% Тесты ограничений для обычного админа (используем готовый токен)
|
||
test_list_admins_forbidden(AdminToken),
|
||
test_create_admin_forbidden(AdminToken),
|
||
test_get_admin_forbidden(AdminToken, AdminId),
|
||
test_update_admin_forbidden(AdminToken, AdminId),
|
||
test_delete_admin_forbidden(AdminToken, AdminId),
|
||
|
||
% Тесты валидации при создании
|
||
test_create_admin_duplicate_email(SuperToken),
|
||
test_create_admin_invalid_role(SuperToken),
|
||
|
||
ct:pal("=== All admin admins tests passed ==="),
|
||
ok.
|
||
|
||
%%%===================================================================
|
||
%%% Тестовые функции
|
||
%%%===================================================================
|
||
|
||
%% @doc GET /v1/admin/admins – список администраторов.
|
||
-spec test_list_admins(binary()) -> ok.
|
||
test_list_admins(Token) ->
|
||
ct:pal(" TEST: List all admins"),
|
||
Admins = api_test_runner:admin_get(<<"/v1/admin/admins">>, Token),
|
||
?assert(is_list(Admins)),
|
||
?assert(length(Admins) >= 1),
|
||
ct:pal(" OK: ~p admins", [length(Admins)]).
|
||
|
||
%% @doc GET /v1/admin/admins/:id – получение администратора.
|
||
-spec test_get_admin(binary(), binary()) -> ok.
|
||
test_get_admin(Token, AdminId) ->
|
||
ct:pal(" TEST: Get admin by ID"),
|
||
Path = <<"/v1/admin/admins/", AdminId/binary>>,
|
||
Admin = api_test_runner:admin_get(Path, Token),
|
||
?assertEqual(AdminId, maps:get(<<"id">>, Admin)),
|
||
ct:pal(" OK: ~s", [maps:get(<<"email">>, Admin)]).
|
||
|
||
%% @doc PUT /v1/admin/admins/:id – обновление администратора.
|
||
-spec test_update_admin(binary(), binary()) -> ok.
|
||
test_update_admin(Token, AdminId) ->
|
||
ct:pal(" TEST: Update admin"),
|
||
Path = <<"/v1/admin/admins/", AdminId/binary>>,
|
||
Updated = api_test_runner:admin_put(Path, Token, #{nickname => <<"UpdatedAdmin">>}),
|
||
?assertEqual(<<"UpdatedAdmin">>, maps:get(<<"nickname">>, Updated)),
|
||
ct:pal(" OK").
|
||
|
||
%% @doc DELETE /v1/admin/admins/:id – удаление (блокировка).
|
||
-spec test_delete_admin(binary(), binary()) -> ok.
|
||
test_delete_admin(Token, AdminId) ->
|
||
ct:pal(" TEST: Delete (block) admin"),
|
||
Path = <<"/v1/admin/admins/", AdminId/binary>>,
|
||
Result = api_test_runner:admin_request(delete, Path, Token),
|
||
{ok, 200, _, _} = Result,
|
||
ct:pal(" OK: admin blocked (or deleted)"),
|
||
% Проверяем, что админ больше не в списке
|
||
Admins = api_test_runner:admin_get(<<"/v1/admin/admins">>, Token),
|
||
?assertNot(lists:any(fun(A) -> maps:get(<<"id">>, A) =:= AdminId end, Admins)).
|
||
|
||
%% ── Тесты ограничений ──
|
||
|
||
-spec test_list_admins_forbidden(binary()) -> ok.
|
||
test_list_admins_forbidden(Token) ->
|
||
ct:pal(" TEST: List admins as non-superadmin (403)"),
|
||
Resp = api_test_runner:admin_request(get, <<"/v1/admin/admins">>, Token),
|
||
?assertMatch({ok, 403, _, _}, Resp),
|
||
ct:pal(" OK: got 403").
|
||
|
||
-spec test_create_admin_forbidden(binary()) -> ok.
|
||
test_create_admin_forbidden(Token) ->
|
||
ct:pal(" TEST: Create admin as non-superadmin (403)"),
|
||
Resp = api_test_runner:admin_request(post, <<"/v1/admin/admins">>, Token,
|
||
jsx:encode(#{email => <<"x@x.com">>, password => <<"p">>, role => <<"moderator">>})),
|
||
?assertMatch({ok, 403, _, _}, Resp),
|
||
ct:pal(" OK: got 403").
|
||
|
||
-spec test_get_admin_forbidden(binary(), binary()) -> ok.
|
||
test_get_admin_forbidden(Token, AdminId) ->
|
||
ct:pal(" TEST: Get admin by ID as non-superadmin (403)"),
|
||
Path = <<"/v1/admin/admins/", AdminId/binary>>,
|
||
Resp = api_test_runner:admin_request(get, Path, Token),
|
||
?assertMatch({ok, 403, _, _}, Resp),
|
||
ct:pal(" OK: got 403").
|
||
|
||
-spec test_update_admin_forbidden(binary(), binary()) -> ok.
|
||
test_update_admin_forbidden(Token, AdminId) ->
|
||
ct:pal(" TEST: Update admin as non-superadmin (403)"),
|
||
Path = <<"/v1/admin/admins/", AdminId/binary>>,
|
||
Resp = api_test_runner:admin_request(put, Path, Token, jsx:encode(#{nickname => <<"fail">>})),
|
||
?assertMatch({ok, 403, _, _}, Resp),
|
||
ct:pal(" OK: got 403").
|
||
|
||
-spec test_delete_admin_forbidden(binary(), binary()) -> ok.
|
||
test_delete_admin_forbidden(Token, AdminId) ->
|
||
ct:pal(" TEST: Delete admin as non-superadmin (403)"),
|
||
Path = <<"/v1/admin/admins/", AdminId/binary>>,
|
||
Resp = api_test_runner:admin_request(delete, Path, Token),
|
||
?assertMatch({ok, 403, _, _}, Resp),
|
||
ct:pal(" OK: got 403").
|
||
|
||
%% ── Валидация создания ──
|
||
|
||
-spec test_create_admin_duplicate_email(binary()) -> ok.
|
||
test_create_admin_duplicate_email(SuperToken) ->
|
||
ct:pal(" TEST: Create admin with duplicate email (409)"),
|
||
Email = api_test_runner:unique_email(<<"dupadmin">>),
|
||
% Создаём первого администратора
|
||
{ok, 201, _, _} = api_test_runner:admin_request(post, <<"/v1/admin/admins">>, SuperToken,
|
||
jsx:encode(#{email => Email, password => <<"Pass1234">>, role => <<"admin">>})),
|
||
% Пытаемся создать второго с тем же email
|
||
Resp = api_test_runner:admin_request(post, <<"/v1/admin/admins">>, SuperToken,
|
||
jsx:encode(#{email => Email, password => <<"Pass1234">>, role => <<"admin">>})),
|
||
?assertMatch({ok, 409, _, _}, Resp),
|
||
ct:pal(" OK: got 409").
|
||
|
||
-spec test_create_admin_invalid_role(binary()) -> ok.
|
||
test_create_admin_invalid_role(SuperToken) ->
|
||
ct:pal(" TEST: Create admin with invalid role (400)"),
|
||
Resp = api_test_runner:admin_request(post, <<"/v1/admin/admins">>, SuperToken,
|
||
jsx:encode(#{email => <<"badrole@test.local">>, password => <<"Pass1234">>, role => <<"superhero">>})),
|
||
?assertMatch({ok, 400, _, _}, Resp),
|
||
ct:pal(" OK: got 400").
|
||
|
||
%%%===================================================================
|
||
%%% Вспомогательные функции
|
||
%%%===================================================================
|
||
|
||
%% @private Создаёт администратора и возвращает его данные.
|
||
-spec create_admin(binary(), binary(), binary(), binary()) -> map().
|
||
create_admin(Token, Email, Password, Role) ->
|
||
ct:pal(" Creating test admin ~s...", [Email]),
|
||
{ok, 201, _, Body} = api_test_runner:admin_request(post, <<"/v1/admin/admins">>, Token,
|
||
jsx:encode(#{email => Email, password => Password, role => Role})),
|
||
jsx:decode(list_to_binary(Body), [return_maps]). |