Рефакторинг админских обработчиков - пагинация, сортировка, фильтрация. Финал
This commit is contained in:
@@ -5,7 +5,8 @@
|
||||
%%% GET /v1/admin/calendars/:id
|
||||
%%% PUT /v1/admin/calendars/:id
|
||||
%%% DELETE /v1/admin/calendars/:id
|
||||
%%% Проверяет пагинацию, фильтрацию по статусу/типу, поиск и сортировку.
|
||||
%%% Проверяет пагинацию, фильтрацию по статусу/типу, поиск и сортировку,
|
||||
%%% а также комбинации фильтров, сортировки и пагинации.
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(admin_calendars_tests).
|
||||
@@ -36,9 +37,20 @@ test() ->
|
||||
test_filter_by_status(AdminToken),
|
||||
test_filter_by_type(AdminToken),
|
||||
test_search(AdminToken),
|
||||
%% Комбинации фильтров
|
||||
test_filter_combined_status_type(AdminToken),
|
||||
%% Сортировка
|
||||
test_sort_by_title(AdminToken),
|
||||
test_sort_by_created_at(AdminToken)
|
||||
test_sort_by_created_at(AdminToken),
|
||||
%% Фильтр + сортировка
|
||||
test_filter_status_with_sort(AdminToken),
|
||||
test_filter_type_with_sort(AdminToken),
|
||||
test_filter_search_with_sort(AdminToken),
|
||||
%% Фильтр + пагинация
|
||||
test_filter_status_with_pagination(AdminToken),
|
||||
%% Фильтр + сортировка + пагинация
|
||||
test_filter_type_with_sort_and_pagination(AdminToken),
|
||||
test_filter_sort_pagination_combined(AdminToken)
|
||||
after
|
||||
cleanup(AdminToken, CalIds)
|
||||
end,
|
||||
@@ -52,18 +64,14 @@ test() ->
|
||||
setup() ->
|
||||
ct:pal(" Setup: register user and create calendars"),
|
||||
AdminToken = api_test_runner:get_admin_token(),
|
||||
|
||||
Email = api_test_runner:unique_email(<<"caladmin">>),
|
||||
Password = <<"testpass">>,
|
||||
UserToken = api_test_runner:register_and_login(Email, Password),
|
||||
|
||||
CalId1 = create_calendar(UserToken, <<"Personal Active">>, <<"Desc1">>),
|
||||
CalId2 = create_calendar(UserToken, <<"Commercial">>, <<"Desc2">>),
|
||||
CalId3 = create_calendar(UserToken, <<"Frozen">>, <<"Desc3">>),
|
||||
|
||||
update_calendar_admin(AdminToken, CalId2, #{<<"type">> => <<"commercial">>}),
|
||||
update_calendar_admin(AdminToken, CalId3, #{<<"status">> => <<"frozen">>}),
|
||||
|
||||
ct:pal(" Created calendars: ~p", [[CalId1, CalId2, CalId3]]),
|
||||
{AdminToken, UserToken, [CalId1, CalId2, CalId3]}.
|
||||
|
||||
@@ -76,10 +84,8 @@ create_calendar(UserToken, Title, Description) ->
|
||||
|
||||
-spec update_calendar_admin(binary(), binary(), map()) -> ok.
|
||||
update_calendar_admin(AdminToken, CalId, Updates) ->
|
||||
% Получаем текущий календарь
|
||||
{ok, 200, _, RespBody} = api_test_runner:admin_request(get, <<"/v1/admin/calendars/", CalId/binary>>, AdminToken, <<"">>),
|
||||
Cal = jsx:decode(list_to_binary(RespBody), [return_maps]),
|
||||
% Сливаем изменения
|
||||
UpdatedCal = maps:merge(Cal, Updates),
|
||||
Body = jsx:encode(UpdatedCal),
|
||||
{ok, 200, _, _} = api_test_runner:admin_request(put, <<"/v1/admin/calendars/", CalId/binary>>, AdminToken, Body),
|
||||
@@ -110,7 +116,6 @@ test_update_calendar(AdminToken, CalId, UserToken) ->
|
||||
ct:pal(" TEST: Update calendar (admin)"),
|
||||
NewTitle = <<"Updated by admin">>,
|
||||
update_calendar_admin(AdminToken, CalId, #{<<"title">> => NewTitle}),
|
||||
% Проверяем через пользовательский API
|
||||
{ok, 200, _, UserResp} = api_test_runner:client_request(get, <<"/v1/calendars/", CalId/binary>>, UserToken, <<"">>),
|
||||
UserCal = jsx:decode(list_to_binary(UserResp), [return_maps]),
|
||||
?assertEqual(NewTitle, maps:get(<<"title">>, UserCal)),
|
||||
@@ -129,7 +134,6 @@ test_update_calendar_invalid_body(AdminToken, CalId) ->
|
||||
test_delete_calendar(AdminToken, CalId) ->
|
||||
ct:pal(" TEST: Delete calendar (admin)"),
|
||||
{ok, 200, _, _} = api_test_runner:admin_request(delete, <<"/v1/admin/calendars/", CalId/binary>>, AdminToken, <<"">>),
|
||||
% Проверяем, что календарь не удалён физически, а помечен как deleted (soft delete)
|
||||
{ok, 200, _, RespBody} = api_test_runner:admin_request(get, <<"/v1/admin/calendars/", CalId/binary>>, AdminToken, <<"">>),
|
||||
Cal = jsx:decode(list_to_binary(RespBody), [return_maps]),
|
||||
?assertEqual(<<"deleted">>, maps:get(<<"status">>, Cal)),
|
||||
@@ -148,11 +152,8 @@ test_pagination(AdminToken) ->
|
||||
{ok, 200, Headers1, Body1} = api_test_runner:admin_request(get, <<"/v1/admin/calendars?limit=2&offset=0">>, AdminToken, <<"">>),
|
||||
List1 = jsx:decode(list_to_binary(Body1), [return_maps]),
|
||||
?assertEqual(2, length(List1)),
|
||||
TotalCount = list_to_integer(
|
||||
proplists:get_value("x-total-count", Headers1, "0")
|
||||
),
|
||||
TotalCount = list_to_integer(proplists:get_value("x-total-count", Headers1, "0")),
|
||||
?assert(TotalCount >= 3),
|
||||
|
||||
{ok, 200, _, Body2} = api_test_runner:admin_request(get, <<"/v1/admin/calendars?limit=2&offset=2">>, AdminToken, <<"">>),
|
||||
List2 = jsx:decode(list_to_binary(Body2), [return_maps]),
|
||||
?assert(length(List2) >= 1),
|
||||
@@ -166,7 +167,6 @@ test_filter_by_status(AdminToken) ->
|
||||
{ok, 200, _, BodyActive} = api_test_runner:admin_request(get, <<"/v1/admin/calendars?status=active">>, AdminToken, <<"">>),
|
||||
ActiveList = jsx:decode(list_to_binary(BodyActive), [return_maps]),
|
||||
[?assertEqual(<<"active">>, maps:get(<<"status">>, C)) || C <- ActiveList],
|
||||
|
||||
{ok, 200, _, BodyFrozen} = api_test_runner:admin_request(get, <<"/v1/admin/calendars?status=frozen">>, AdminToken, <<"">>),
|
||||
FrozenList = jsx:decode(list_to_binary(BodyFrozen), [return_maps]),
|
||||
[?assertEqual(<<"frozen">>, maps:get(<<"status">>, C)) || C <- FrozenList],
|
||||
@@ -189,18 +189,30 @@ test_search(AdminToken) ->
|
||||
HasMatch = lists:any(fun(C) ->
|
||||
Title = string:lowercase(binary_to_list(maps:get(<<"title">>, C, <<>>))),
|
||||
Desc = string:lowercase(binary_to_list(maps:get(<<"description">>, C, <<>>))),
|
||||
string:str(Title, "commercial") > 0 orelse
|
||||
string:str(Desc, "commercial") > 0
|
||||
end, Results),
|
||||
string:str(Title, "commercial") > 0 orelse string:str(Desc, "commercial") > 0
|
||||
end, Results),
|
||||
?assert(HasMatch),
|
||||
ct:pal(" OK: search works").
|
||||
|
||||
%%%===================================================================
|
||||
%%% Комбинации фильтров
|
||||
%%%===================================================================
|
||||
test_filter_combined_status_type(AdminToken) ->
|
||||
ct:pal(" TEST: Combined filter (status=active, type=personal)"),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?status=active&type=personal">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
[?assertEqual(<<"active">>, maps:get(<<"status">>, C)) || C <- List],
|
||||
[?assertEqual(<<"personal">>, maps:get(<<"type">>, C)) || C <- List],
|
||||
ct:pal(" OK: ~p calendars match both criteria", [length(List)]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Сортировка
|
||||
%%%===================================================================
|
||||
test_sort_by_title(AdminToken) ->
|
||||
ct:pal(" TEST: Sort by title asc"),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/calendars?sort_by=title&sort_order=asc">>, AdminToken, <<"">>),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?sort_by=title&sort_order=asc">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
Titles = [maps:get(<<"title">>, C) || C <- List],
|
||||
?assertEqual(lists:sort(Titles), Titles),
|
||||
@@ -208,9 +220,86 @@ test_sort_by_title(AdminToken) ->
|
||||
|
||||
test_sort_by_created_at(AdminToken) ->
|
||||
ct:pal(" TEST: Sort by created_at desc"),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get, <<"/v1/admin/calendars?sort_by=created_at&sort_order=desc">>, AdminToken, <<"">>),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?sort_by=created_at&sort_order=desc">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
Dates = [maps:get(<<"created_at">>, C) || C <- List],
|
||||
Sorted = lists:reverse(lists:sort(Dates)),
|
||||
?assertEqual(Sorted, Dates),
|
||||
ct:pal(" OK: sorted by created_at desc").
|
||||
ct:pal(" OK: sorted by created_at desc").
|
||||
|
||||
%%%===================================================================
|
||||
%%% Фильтр + сортировка
|
||||
%%%===================================================================
|
||||
test_filter_status_with_sort(AdminToken) ->
|
||||
ct:pal(" TEST: Filter status=frozen + sort by title asc"),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?status=frozen&sort_by=title&sort_order=asc">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
[?assertEqual(<<"frozen">>, maps:get(<<"status">>, C)) || C <- List],
|
||||
Titles = [maps:get(<<"title">>, C) || C <- List],
|
||||
?assertEqual(lists:sort(Titles), Titles),
|
||||
ct:pal(" OK: ~p calendars", [length(List)]).
|
||||
|
||||
test_filter_type_with_sort(AdminToken) ->
|
||||
ct:pal(" TEST: Filter type=commercial + sort by created_at desc"),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?type=commercial&sort_by=created_at&sort_order=desc">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
[?assertEqual(<<"commercial">>, maps:get(<<"type">>, C)) || C <- List],
|
||||
Dates = [maps:get(<<"created_at">>, C) || C <- List],
|
||||
Sorted = lists:reverse(lists:sort(Dates)),
|
||||
?assertEqual(Sorted, Dates),
|
||||
ct:pal(" OK: ~p calendars", [length(List)]).
|
||||
|
||||
test_filter_search_with_sort(AdminToken) ->
|
||||
ct:pal(" TEST: Search q=Personal + sort by title desc"),
|
||||
{ok, 200, _, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?q=Personal&sort_by=title&sort_order=desc">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
[?assert(string:str(string:lowercase(binary_to_list(maps:get(<<"title">>, C))), "personal") > 0
|
||||
orelse string:str(string:lowercase(binary_to_list(maps:get(<<"description">>, C))), "personal") > 0)
|
||||
|| C <- List],
|
||||
Titles = [maps:get(<<"title">>, C) || C <- List],
|
||||
?assertEqual(lists:reverse(lists:sort(Titles)), Titles),
|
||||
ct:pal(" OK: ~p calendars", [length(List)]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Фильтр + пагинация
|
||||
%%%===================================================================
|
||||
test_filter_status_with_pagination(AdminToken) ->
|
||||
ct:pal(" TEST: Filter status=active + pagination (limit=1, offset=0)"),
|
||||
{ok, 200, Headers, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?status=active&limit=1&offset=0">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
?assertEqual(1, length(List)),
|
||||
[?assertEqual(<<"active">>, maps:get(<<"status">>, C)) || C <- List],
|
||||
Total = list_to_integer(proplists:get_value("x-total-count", Headers, "0")),
|
||||
?assert(Total >= 1),
|
||||
ct:pal(" OK: total count = ~p", [Total]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Фильтр + сортировка + пагинация
|
||||
%%%===================================================================
|
||||
test_filter_type_with_sort_and_pagination(AdminToken) ->
|
||||
ct:pal(" TEST: Filter type=personal + sort by created_at asc + pagination (limit=1)"),
|
||||
{ok, 200, Headers, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?type=personal&sort_by=created_at&sort_order=asc&limit=1&offset=0">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
?assertEqual(1, length(List)),
|
||||
[?assertEqual(<<"personal">>, maps:get(<<"type">>, C)) || C <- List],
|
||||
Total = list_to_integer(proplists:get_value("x-total-count", Headers, "0")),
|
||||
?assert(Total >= 1),
|
||||
ct:pal(" OK: total count = ~p", [Total]).
|
||||
|
||||
test_filter_sort_pagination_combined(AdminToken) ->
|
||||
ct:pal(" TEST: status=active + sort_by=title asc + limit=2"),
|
||||
{ok, 200, Headers, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/calendars?status=active&sort_by=title&sort_order=asc&limit=2&offset=0">>, AdminToken, <<"">>),
|
||||
List = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
[?assertEqual(<<"active">>, maps:get(<<"status">>, C)) || C <- List],
|
||||
Titles = [maps:get(<<"title">>, C) || C <- List],
|
||||
?assertEqual(lists:sort(Titles), Titles),
|
||||
Total = list_to_integer(proplists:get_value("x-total-count", Headers, "0")),
|
||||
?assert(Total >= 1),
|
||||
ct:pal(" OK: ~p calendars (total ~p)", [length(List), Total]).
|
||||
Reference in New Issue
Block a user