-module(api_admin_tests). -export([test/0]). %% Учётные данные по умолчанию (используются, если словарь процесса пуст) -define(FALLBACK_ADMIN_SUPER_EMAIL, <<"superadmin@eventhub.local">>). -define(FALLBACK_ADMIN_SUPER_PASSWORD, <<"123456">>). -define(FALLBACK_ADMIN_MODER_EMAIL, <<"moderator@eventhub.local">>). -define(FALLBACK_ADMIN_MODER_PASSWORD, <<"123456">>). -define(FALLBACK_ADMIN_SUPPORT_EMAIL, <<"support@eventhub.local">>). -define(FALLBACK_ADMIN_SUPPORT_PASSWORD,<<"123456">>). test() -> ct:pal("Testing admin panel API...~n"), AdminURL = api_test_runner:get_admin_url(), UserURL = api_test_runner:get_base_url(), % Получаем токен суперадмина (уже проинициализирован в api_test_runner) AdminToken = api_test_runner:get_admin_token(), %% TEST 1: Admin healthcheck (public) ct:pal(" TEST 1: Admin healthcheck... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/health", []}, [], []), ct:pal("OK~n"), %% TEST 2: Admin login (дополнительная проверка) ct:pal(" TEST 2: Admin login (attempt)... "), % Теперь используем суперадмина, который гарантированно создан LoginBody = jsx:encode(#{<<"email">> => ?FALLBACK_ADMIN_SUPER_EMAIL, <<"password">> => ?FALLBACK_ADMIN_SUPER_PASSWORD}), case httpc:request(post, {AdminURL ++ "/v1/admin/login", [], "application/json", LoginBody}, [], []) of {ok, {{_, 200, _}, _, _}} -> ct:pal("OK (logged in)~n"); _ -> ct:pal("SKIPPED (credentials not found, using runner token)~n") end, %% TEST 3: Admin stats (superadmin) ct:pal(" TEST 3: Admin stats (superadmin)... "), {ok, {{_, 200, _}, _, StatsResp1}} = httpc:request(get, {AdminURL ++ "/v1/admin/stats", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), Stats1 = jsx:decode(list_to_binary(StatsResp1), [return_maps]), ct:pal(" OK (keys: ~p)~n", [maps:keys(Stats1)]), %% TEST 4: List users ct:pal(" TEST 4: List users... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/users", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 5: Get user by ID ct:pal(" TEST 5: Get user by ID... "), UserId = api_test_runner:get_user_id(), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/users/" ++ binary_to_list(UserId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 6: List reports ct:pal(" TEST 6: List reports... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/reports", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% ── TEST 7: Full moderation flow (create event, report, resolve) ── ct:pal(" TEST 7: Moderation flow... "), % Создаём календарь и событие от имени пользователя UserToken = api_test_runner:get_user_token(), CalId = api_test_runner:create_calendar(UserToken, #{title => <<"ModerationTest">>}), EventId = api_test_runner:create_event(UserToken, CalId, #{ title => <<"Event to report">>, start_time => api_SUITE:future_date(), duration => 60 }), % Подаём жалобу на это событие CreateBody = jsx:encode(#{ <<"target_type">> => <<"event">>, <<"target_id">> => EventId, <<"reason">> => <<"Inappropriate content">> }), {ok, {{_, 201, _}, _, CreateResp}} = httpc:request(post, {UserURL ++ "/v1/reports", [{"Authorization", "Bearer " ++ binary_to_list(UserToken)}], "application/json", CreateBody}, [], []), #{<<"id">> := ReportId} = jsx:decode(list_to_binary(CreateResp), [return_maps]), % Администратор изменяет статус жалобы EditBody = jsx:encode(#{ <<"status">> => <<"reviewed">>, <<"reason">> => <<"Issue resolved">> }), {ok, {{_, 200, _}, _, _}} = httpc:request(put, {AdminURL ++ "/v1/admin/reports/" ++ binary_to_list(ReportId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", EditBody}, [], []), ct:pal("OK~n"), %% TEST 8: List banned words ct:pal(" TEST 8: List banned words... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/banned-words", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 9: Add banned word ct:pal(" TEST 9: Add banned word... "), BannedWordBody = jsx:encode(#{<<"word">> => <<"test_banned_word">>}), {ok, {{_, 201, _}, _, _}} = httpc:request(post, {AdminURL ++ "/v1/admin/banned-words", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", BannedWordBody}, [], []), ct:pal("OK~n"), %% TEST 10: Delete banned word ct:pal(" TEST 10: Delete banned word... "), {ok, {{_, 200, _}, _, _}} = httpc:request(delete, {AdminURL ++ "/v1/admin/banned-words/test_banned_word", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 11: List tickets ct:pal(" TEST 11: List tickets... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/tickets", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 12: Create ticket ct:pal(" TEST 12: Create ticket... "), TicketBody = jsx:encode(#{<<"error_message">> => <<"Test error">>, <<"stacktrace">> => <<"trace">>}), {ok, {{_, 201, _}, _, TicketResp}} = httpc:request(post, {AdminURL ++ "/v1/admin/tickets", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", TicketBody}, [], []), #{<<"id">> := TicketId} = jsx:decode(list_to_binary(TicketResp), [return_maps]), ct:pal("OK~n"), %% TEST 13: Get ticket by ID ct:pal(" TEST 13: Get ticket by ID... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/tickets/" ++ binary_to_list(TicketId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 14: Update ticket ct:pal(" TEST 14: Update ticket... "), UpdateTicketBody = jsx:encode(#{<<"status">> => <<"closed">>}), {ok, {{_, 200, _}, _, _}} = httpc:request(put, {AdminURL ++ "/v1/admin/tickets/" ++ binary_to_list(TicketId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", UpdateTicketBody}, [], []), ct:pal("OK~n"), %% TEST 15: Delete ticket ct:pal(" TEST 15: Delete ticket... "), {ok, {{_, 200, _}, _, _}} = httpc:request(delete, {AdminURL ++ "/v1/admin/tickets/" ++ binary_to_list(TicketId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 16: Ticket stats ct:pal(" TEST 16: Ticket stats... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/tickets/stats", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 17: List subscriptions ct:pal(" TEST 17: List subscriptions... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/subscriptions", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 18: Create subscription ct:pal(" TEST 18: Create subscription... "), SubBody = jsx:encode(#{<<"user_id">> => UserId, <<"plan">> => <<"monthly">>}), {ok, {{_, 201, _}, _, SubResp}} = httpc:request(post, {AdminURL ++ "/v1/admin/subscriptions", [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", SubBody}, [], []), #{<<"id">> := SubId} = jsx:decode(list_to_binary(SubResp), [return_maps]), ct:pal("OK~n"), %% TEST 19: Get subscription by ID ct:pal(" TEST 19: Get subscription by ID... "), {ok, {{_, 200, _}, _, _}} = httpc:request(get, {AdminURL ++ "/v1/admin/subscriptions/" ++ binary_to_list(SubId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 20: Update subscription ct:pal(" TEST 20: Update subscription... "), UpdateSubBody = jsx:encode(#{<<"status">> => <<"cancelled">>}), {ok, {{_, 200, _}, _, _}} = httpc:request(put, {AdminURL ++ "/v1/admin/subscriptions/" ++ binary_to_list(SubId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", UpdateSubBody}, [], []), ct:pal("OK~n"), %% TEST 21: Delete subscription ct:pal(" TEST 21: Delete subscription... "), {ok, {{_, 200, _}, _, _}} = httpc:request(delete, {AdminURL ++ "/v1/admin/subscriptions/" ++ binary_to_list(SubId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}]}, [], []), ct:pal("OK~n"), %% TEST 22: Moderation - block user ct:pal(" TEST 22: Moderation - block user... "), ModBody = jsx:encode(#{<<"action">> => <<"block">>, <<"reason">> => <<"test">>}), {ok, {{_, 200, _}, _, _}} = httpc:request(put, {AdminURL ++ "/v1/admin/user/" ++ binary_to_list(UserId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", ModBody}, [], []), ct:pal("OK~n"), %% TEST 23: Moderation - unblock user ct:pal(" TEST 23: Moderation - unblock user... "), UnblockBody = jsx:encode(#{<<"action">> => <<"unblock">>, <<"reason">> => <<"restore">>}), {ok, {{_, 200, _}, _, _}} = httpc:request(put, {AdminURL ++ "/v1/admin/user/" ++ binary_to_list(UserId), [{"Authorization", "Bearer " ++ binary_to_list(AdminToken)}], "application/json", UnblockBody}, [], []), ct:pal("OK~n"), ct:pal("~n✅ Admin API tests passed!~n"), {?MODULE, ok}.