Files
EventHubBack/test/api/admins/admin_admins_tests.erl

180 lines
8.3 KiB
Erlang
Raw Permalink 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/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]).