%%%------------------------------------------------------------------- %%% @doc Тесты административного API для управления жалобами. %%% %%% Покрывает эндпоинты: %%% GET /v1/admin/reports %%% GET /v1/admin/reports/:id %%% PUT /v1/admin/reports/:id %%% %%% Проверяет: %%% - получение списка жалоб %%% - получение жалобы по ID %%% - обновление статуса жалобы (review, dismiss) %%% - фильтрацию по статусу и типу цели %%% - комбинации фильтров (status + target_type) %%% - сортировку по created_at и status %%% - комбинации фильтрации с сортировкой %%% - пагинацию (limit/offset, x-total-count) %%% - комбинации фильтрации, сортировки и пагинации %%% @end %%%------------------------------------------------------------------- -module(admin_reports_tests). -include_lib("eunit/include/eunit.hrl"). -export([test/0]). %%%=================================================================== %%% Главная тестовая функция %%%=================================================================== -spec test() -> ok. test() -> ct:pal("=== Admin Reports Tests ==="), Token = api_test_runner:get_admin_token(), UserToken = api_test_runner:get_user_token(), CalId = api_test_runner:create_calendar(UserToken, #{title => <<"ReportsTestCal">>}), EventId = api_test_runner:create_event(UserToken, CalId, #{ title => <<"Event for reporting">>, start_time => api_test_runner:future_date(), duration => 60 }), #{<<"id">> := Report1Id} = api_test_runner:client_post(<<"/v1/reports">>, UserToken, #{ <<"target_type">> => <<"event">>, <<"target_id">> => EventId, <<"reason">> => <<"Inappropriate content">> }), #{<<"id">> := Report2Id} = api_test_runner:client_post(<<"/v1/reports">>, UserToken, #{ <<"target_type">> => <<"event">>, <<"target_id">> => EventId, <<"reason">> => <<"Spam">> }), test_list_reports(Token, Report1Id), test_get_report(Token, Report1Id), test_review_report(Token, Report1Id), test_dismiss_report(Token, Report2Id), test_filter_reports(Token), test_filter_combined_status_target(Token), test_sort_by_created_at_desc(Token), test_sort_by_created_at_asc(Token), test_sort_by_status_asc(Token), test_filter_status_sort_created_at(Token), test_filter_target_sort_status(Token), test_report_pagination(Token), test_pagination_headers(Token), test_filter_status_with_pagination(Token), test_filter_sort_pagination_combined(Token), ct:pal("=== All admin reports tests passed ==="), ok. %%%=================================================================== %%% Тестовые функции %%%=================================================================== -spec test_list_reports(binary(), binary()) -> ok. test_list_reports(Token, ReportId) -> ct:pal(" TEST: List all reports"), Reports = api_test_runner:admin_get(<<"/v1/admin/reports">>, Token), ?assert(is_list(Reports)), ?assert(length(Reports) >= 2), ?assert(lists:any(fun(R) -> maps:get(<<"id">>, R) =:= ReportId end, Reports)), ct:pal(" OK: ~p reports", [length(Reports)]). -spec test_get_report(binary(), binary()) -> ok. test_get_report(Token, ReportId) -> ct:pal(" TEST: Get report by ID"), Path = <<"/v1/admin/reports/", ReportId/binary>>, Report = api_test_runner:admin_get(Path, Token), ?assertEqual(ReportId, maps:get(<<"id">>, Report)), ?assertEqual(<<"pending">>, maps:get(<<"status">>, Report)), ct:pal(" OK"). -spec test_review_report(binary(), binary()) -> ok. test_review_report(Token, ReportId) -> ct:pal(" TEST: Review report (set status to reviewed)"), Path = <<"/v1/admin/reports/", ReportId/binary>>, Updated = api_test_runner:admin_put(Path, Token, #{<<"status">> => <<"reviewed">>}), ?assertEqual(<<"reviewed">>, maps:get(<<"status">>, Updated)), ct:pal(" OK"). -spec test_dismiss_report(binary(), binary()) -> ok. test_dismiss_report(Token, ReportId) -> ct:pal(" TEST: Dismiss report (set status to dismissed)"), Path = <<"/v1/admin/reports/", ReportId/binary>>, Updated = api_test_runner:admin_put(Path, Token, #{<<"status">> => <<"dismissed">>}), ?assertEqual(<<"dismissed">>, maps:get(<<"status">>, Updated)), ct:pal(" OK"). -spec test_filter_reports(binary()) -> ok. test_filter_reports(Token) -> ct:pal(" TEST: Filter reports by status=reviewed"), Reports = api_test_runner:admin_get(<<"/v1/admin/reports?status=reviewed">>, Token), ?assert(is_list(Reports)), [?assertEqual(<<"reviewed">>, maps:get(<<"status">>, R)) || R <- Reports], ct:pal(" OK: ~p reviewed reports", [length(Reports)]), ct:pal(" TEST: Filter reports by target_type=event"), Reports2 = api_test_runner:admin_get(<<"/v1/admin/reports?target_type=event">>, Token), ?assert(is_list(Reports2)), [?assertEqual(<<"event">>, maps:get(<<"target_type">>, R)) || R <- Reports2], ct:pal(" OK: ~p reports for events", [length(Reports2)]). -spec test_filter_combined_status_target(binary()) -> ok. test_filter_combined_status_target(Token) -> ct:pal(" TEST: Combined filter (status=dismissed & target_type=event)"), Reports = api_test_runner:admin_get( <<"/v1/admin/reports?status=dismissed&target_type=event">>, Token), ?assert(is_list(Reports)), [?assertEqual(<<"dismissed">>, maps:get(<<"status">>, R)) || R <- Reports], [?assertEqual(<<"event">>, maps:get(<<"target_type">>, R)) || R <- Reports], ct:pal(" OK: ~p reports match both criteria", [length(Reports)]). -spec test_sort_by_created_at_desc(binary()) -> ok. test_sort_by_created_at_desc(Token) -> ct:pal(" TEST: Sort by created_at desc"), {ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?sort=created_at&order=desc">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), Dates = [maps:get(<<"created_at">>, R) || R <- Reports], Sorted = lists:reverse(lists:sort(Dates)), ?assertEqual(Sorted, Dates), ct:pal(" OK: sorted by created_at desc"). -spec test_sort_by_created_at_asc(binary()) -> ok. test_sort_by_created_at_asc(Token) -> ct:pal(" TEST: Sort by created_at asc"), {ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?sort=created_at&order=asc">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), Dates = [maps:get(<<"created_at">>, R) || R <- Reports], ?assertEqual(lists:sort(Dates), Dates), ct:pal(" OK: sorted by created_at asc"). -spec test_sort_by_status_asc(binary()) -> ok. test_sort_by_status_asc(Token) -> ct:pal(" TEST: Sort by status asc"), {ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?sort=status&order=asc">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), Statuses = [maps:get(<<"status">>, R) || R <- Reports], ?assertEqual(lists:sort(Statuses), Statuses), ct:pal(" OK: sorted by status asc"). -spec test_filter_status_sort_created_at(binary()) -> ok. test_filter_status_sort_created_at(Token) -> ct:pal(" TEST: Filter status=reviewed + sort by created_at asc"), {ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?status=reviewed&sort=created_at&order=asc">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), [?assertEqual(<<"reviewed">>, maps:get(<<"status">>, R)) || R <- Reports], Dates = [maps:get(<<"created_at">>, R) || R <- Reports], ?assertEqual(lists:sort(Dates), Dates), ct:pal(" OK: ~p reports sorted", [length(Reports)]). -spec test_filter_target_sort_status(binary()) -> ok. test_filter_target_sort_status(Token) -> ct:pal(" TEST: Filter target_type=event + sort by status desc"), {ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?target_type=event&sort=status&order=desc">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), [?assertEqual(<<"event">>, maps:get(<<"target_type">>, R)) || R <- Reports], Statuses = [maps:get(<<"status">>, R) || R <- Reports], ?assertEqual(lists:reverse(lists:sort(Statuses)), Statuses), ct:pal(" OK: ~p reports sorted", [length(Reports)]). -spec test_report_pagination(binary()) -> ok. test_report_pagination(Token) -> ct:pal(" TEST: Report pagination"), Page1 = api_test_runner:admin_get(<<"/v1/admin/reports?limit=1&offset=0">>, Token), ?assert(length(Page1) >= 1), Page2 = api_test_runner:admin_get(<<"/v1/admin/reports?limit=1&offset=1">>, Token), ?assert(length(Page2) >= 0), case {Page1, Page2} of {[First|_], [Second|_]} -> Id1 = maps:get(<<"id">>, First), Id2 = maps:get(<<"id">>, Second), ?assertNotEqual(Id1, Id2); _ -> ok end, ct:pal(" OK"). -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/reports?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]). -spec test_filter_status_with_pagination(binary()) -> ok. test_filter_status_with_pagination(Token) -> ct:pal(" TEST: Filter status=dismissed + pagination (limit=1)"), {ok, 200, Headers, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?status=dismissed&limit=1&offset=0">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), ?assert(length(Reports) =< 1), [?assertEqual(<<"dismissed">>, maps:get(<<"status">>, R)) || R <- Reports], Total = list_to_integer( proplists:get_value("x-total-count", Headers, "0")), ?assert(Total >= 1), ct:pal(" OK: total count = ~p", [Total]). -spec test_filter_sort_pagination_combined(binary()) -> ok. test_filter_sort_pagination_combined(Token) -> ct:pal(" TEST: Filter target_type=event + sort by created_at desc + pagination (limit=1)"), {ok, 200, Headers, Body} = api_test_runner:admin_request(get, <<"/v1/admin/reports?target_type=event&sort=created_at&order=desc&limit=1&offset=0">>, Token, <<"">>), Reports = jsx:decode(list_to_binary(Body), [return_maps]), ?assert(length(Reports) =< 1), [?assertEqual(<<"event">>, maps:get(<<"target_type">>, R)) || R <- Reports], Total = list_to_integer( proplists:get_value("x-total-count", Headers, "0")), ?assert(Total >= 1), ct:pal(" OK: total count = ~p", [Total]).