%%%------------------------------------------------------------------- %%% @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]).