Рефакторинг админских обработчиков - пагинация, сортировка, фильтрация. Финал
This commit is contained in:
@@ -14,7 +14,10 @@
|
||||
%%% - редактирование существующего слова
|
||||
%%% - удаление слова
|
||||
%%% - массовую загрузку списком
|
||||
%%% - пагинацию
|
||||
%%% - **фильтрацию** по слову (q)
|
||||
%%% - **сортировку** по word, added_at
|
||||
%%% - **комбинации фильтрации, сортировки и пагинации**
|
||||
%%% - **пагинацию** (limit/offset, x-total-count)
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(admin_banned_words_tests).
|
||||
@@ -29,23 +32,42 @@ test() ->
|
||||
ct:pal("=== Admin Banned Words Tests ==="),
|
||||
Token = api_test_runner:get_admin_token(),
|
||||
|
||||
% Добавляем два исходных слова
|
||||
% Добавляем несколько слов для тестирования
|
||||
Unique = integer_to_binary(erlang:system_time()),
|
||||
Word1 = <<"badword1_", Unique/binary>>,
|
||||
Word2 = <<"badword2_", Unique/binary>>,
|
||||
Word1 = <<"alpha_", Unique/binary>>,
|
||||
Word2 = <<"beta_", Unique/binary>>,
|
||||
Word3 = <<"gamma_", Unique/binary>>,
|
||||
api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Token, #{<<"word">> => Word1}),
|
||||
api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Token, #{<<"word">> => Word2}),
|
||||
api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Token, #{<<"word">> => Word3}),
|
||||
|
||||
test_list_words(Token, Word1, Word2),
|
||||
% ── Основные тесты ──
|
||||
test_list_words(Token),
|
||||
test_add_word(Token),
|
||||
test_update_word(Token, Word1),
|
||||
test_batch_add_words(Token),
|
||||
test_words_pagination(Token),
|
||||
test_delete_word(Token, Word2),
|
||||
test_update_word_conflict(Token),
|
||||
test_batch_add_invalid(Token),
|
||||
test_batch_add_duplicates(Token),
|
||||
|
||||
% ── Пагинация ──
|
||||
test_words_pagination(Token),
|
||||
test_pagination_headers(Token),
|
||||
|
||||
% ── Фильтрация ──
|
||||
test_filter_by_word(Token, Word3),
|
||||
|
||||
% ── Сортировка ──
|
||||
test_sort_by_word_asc(Token),
|
||||
test_sort_by_added_at_desc(Token),
|
||||
|
||||
% ── Комбинации ──
|
||||
test_filter_with_sort(Token, Unique),
|
||||
test_filter_sort_pagination(Token, Unique),
|
||||
|
||||
% Удаление beta только после всех тестов фильтрации/сортировки
|
||||
test_delete_word(Token, Word2),
|
||||
|
||||
ct:pal("=== All admin banned words tests passed ==="),
|
||||
ok.
|
||||
|
||||
@@ -54,17 +76,14 @@ test() ->
|
||||
%%%===================================================================
|
||||
|
||||
%% @doc GET /v1/admin/banned-words – список всех слов.
|
||||
-spec test_list_words(binary(), binary(), binary()) -> ok.
|
||||
test_list_words(Token, Word1, Word2) ->
|
||||
test_list_words(Token) ->
|
||||
ct:pal(" TEST: List all banned words"),
|
||||
Words = api_test_runner:admin_get(<<"/v1/admin/banned-words">>, Token),
|
||||
?assert(is_list(Words)),
|
||||
?assert(length(Words) >= 2),
|
||||
?assert(lists:any(fun(W) -> maps:get(<<"word">>, W) =:= Word1 orelse maps:get(<<"word">>, W) =:= Word2 end, Words)),
|
||||
?assert(length(Words) >= 3),
|
||||
ct:pal(" OK: ~p words", [length(Words)]).
|
||||
|
||||
%% @doc POST /v1/admin/banned-words – добавление нового слова.
|
||||
-spec test_add_word(binary()) -> ok.
|
||||
test_add_word(Token) ->
|
||||
ct:pal(" TEST: Add a new banned word"),
|
||||
Unique = integer_to_binary(erlang:system_time()),
|
||||
@@ -76,40 +95,31 @@ test_add_word(Token) ->
|
||||
ct:pal(" OK").
|
||||
|
||||
%% @doc PUT /v1/admin/banned-words/:word – редактирование слова.
|
||||
-spec test_update_word(binary(), binary()) -> ok.
|
||||
test_update_word(Token, Word) ->
|
||||
ct:pal(" TEST: Update a banned word"),
|
||||
NewWord = <<Word/binary, "_updated">>,
|
||||
Result = api_test_runner:admin_put(<<"/v1/admin/banned-words/", Word/binary>>, Token, #{<<"word">> => NewWord}),
|
||||
?assertEqual(<<"updated">>, maps:get(<<"status">>, Result)),
|
||||
% Проверяем, что старое слово отсутствует, а новое присутствует
|
||||
Words = api_test_runner:admin_get(<<"/v1/admin/banned-words">>, Token),
|
||||
?assertNot(lists:any(fun(W) -> maps:get(<<"word">>, W) =:= Word end, Words)),
|
||||
?assert(lists:any(fun(W) -> maps:get(<<"word">>, W) =:= NewWord end, Words)),
|
||||
ct:pal(" OK").
|
||||
|
||||
%% @doc POST /v1/admin/banned-words/batch – массовая загрузка.
|
||||
-spec test_batch_add_words(binary()) -> ok.
|
||||
test_batch_add_words(Token) ->
|
||||
ct:pal(" TEST: Batch add banned words"),
|
||||
Unique = integer_to_binary(erlang:system_time()),
|
||||
Words = [
|
||||
<<"batch1_", Unique/binary>>,
|
||||
<<"batch2_", Unique/binary>>,
|
||||
<<"batch3_", Unique/binary>>
|
||||
],
|
||||
Words = [<<"batch1_", Unique/binary>>, <<"batch2_", Unique/binary>>, <<"batch3_", Unique/binary>>],
|
||||
Body = jsx:encode(#{<<"words">> => Words}),
|
||||
{ok, 200, _, RespBody} = api_test_runner:admin_request(post, <<"/v1/admin/banned-words/batch">>, Token, Body),
|
||||
Result = jsx:decode(list_to_binary(RespBody), [return_maps]),
|
||||
?assertEqual(3, maps:get(<<"added">>, Result)),
|
||||
?assertEqual(0, maps:get(<<"skipped">>, Result)),
|
||||
% Проверяем наличие всех слов
|
||||
AllWords = api_test_runner:admin_get(<<"/v1/admin/banned-words">>, Token),
|
||||
[?assert(lists:any(fun(W) -> maps:get(<<"word">>, W) =:= Word end, AllWords)) || Word <- Words],
|
||||
ct:pal(" OK").
|
||||
|
||||
%% @doc DELETE /v1/admin/banned-words/:word – удаление слова.
|
||||
-spec test_delete_word(binary(), binary()) -> ok.
|
||||
test_delete_word(Token, Word) ->
|
||||
ct:pal(" TEST: Delete a banned word"),
|
||||
Path = <<"/v1/admin/banned-words/", Word/binary>>,
|
||||
@@ -119,29 +129,17 @@ test_delete_word(Token, Word) ->
|
||||
?assertNot(lists:any(fun(W) -> maps:get(<<"word">>, W) =:= Word end, Words)),
|
||||
ct:pal(" OK").
|
||||
|
||||
%% @doc GET /v1/admin/banned-words?limit=...&offset=... – пагинация.
|
||||
-spec test_words_pagination(binary()) -> ok.
|
||||
test_words_pagination(Token) ->
|
||||
ct:pal(" TEST: Banned words pagination"),
|
||||
Page1 = api_test_runner:admin_get(<<"/v1/admin/banned-words?limit=1&offset=0">>, Token),
|
||||
?assert(length(Page1) >= 1),
|
||||
Page2 = api_test_runner:admin_get(<<"/v1/admin/banned-words?limit=1&offset=1">>, Token),
|
||||
?assert(length(Page2) >= 1),
|
||||
Id1 = maps:get(<<"id">>, hd(Page1)),
|
||||
Id2 = maps:get(<<"id">>, hd(Page2)),
|
||||
?assertNotEqual(Id1, Id2),
|
||||
ct:pal(" OK").
|
||||
|
||||
%% @doc PUT /v1/admin/banned-words/:word – попытка переименовать в уже существующее слово.
|
||||
%% @doc PUT /v1/admin/banned-words/:word – попытка переименовать в уже существующее слово (409).
|
||||
test_update_word_conflict(Token) ->
|
||||
ct:pal(" TEST: Update banned word to existing word"),
|
||||
ct:pal(" TEST: Update banned word to existing word (conflict)"),
|
||||
Unique = integer_to_binary(erlang:system_time()),
|
||||
WordA = <<"conflictA_", Unique/binary>>,
|
||||
WordB = <<"conflictB_", Unique/binary>>,
|
||||
api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Token, #{<<"word">> => WordA}),
|
||||
api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Token, #{<<"word">> => WordB}),
|
||||
Path = <<"/v1/admin/banned-words/", WordA/binary>>,
|
||||
{ok, 409, _, _} = api_test_runner:admin_request(put, Path, Token, jsx:encode(#{<<"word">> => WordB})),
|
||||
Resp = api_test_runner:admin_request(put, Path, Token, jsx:encode(#{<<"word">> => WordB})),
|
||||
?assertMatch({ok, 409, _, _}, Resp),
|
||||
ct:pal(" OK: got 409 conflict").
|
||||
|
||||
%% @doc POST /v1/admin/banned-words/batch – с невалидными элементами.
|
||||
@@ -174,4 +172,90 @@ test_batch_add_duplicates(Token) ->
|
||||
Result = jsx:decode(list_to_binary(RespBody), [return_maps]),
|
||||
?assertEqual(2, maps:get(<<"added">>, Result)),
|
||||
?assertEqual(2, maps:get(<<"skipped">>, Result)),
|
||||
ct:pal(" OK: duplicates handled").
|
||||
ct:pal(" OK: duplicates handled").
|
||||
|
||||
%% ── Пагинация ──
|
||||
|
||||
%% @doc GET /v1/admin/banned-words?limit=...&offset=... – пагинация.
|
||||
test_words_pagination(Token) ->
|
||||
ct:pal(" TEST: Banned words pagination"),
|
||||
Page1 = api_test_runner:admin_get(<<"/v1/admin/banned-words?limit=1&offset=0">>, Token),
|
||||
?assert(length(Page1) >= 1),
|
||||
Page2 = api_test_runner:admin_get(<<"/v1/admin/banned-words?limit=1&offset=1">>, Token),
|
||||
?assert(length(Page2) >= 1),
|
||||
Id1 = maps:get(<<"id">>, hd(Page1)),
|
||||
Id2 = maps:get(<<"id">>, hd(Page2)),
|
||||
?assertNotEqual(Id1, Id2),
|
||||
ct:pal(" OK").
|
||||
|
||||
%% @doc Проверка заголовков пагинации (x-total-count).
|
||||
test_pagination_headers(Token) ->
|
||||
ct:pal(" TEST: Pagination headers (x-total-count)"),
|
||||
{ok, 200, Headers, _} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/banned-words?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]).
|
||||
|
||||
%% ── Фильтрация ──
|
||||
|
||||
%% @doc Фильтрация по слову (q).
|
||||
test_filter_by_word(Token, Word) ->
|
||||
ct:pal(" TEST: Filter banned words by word (q)"),
|
||||
Words = api_test_runner:admin_get(<<"/v1/admin/banned-words?q=", Word/binary>>, Token),
|
||||
?assert(is_list(Words)),
|
||||
?assert(length(Words) >= 1),
|
||||
[?assertEqual(Word, maps:get(<<"word">>, W)) || W <- Words],
|
||||
ct:pal(" OK: ~p words match", [length(Words)]).
|
||||
|
||||
%% ── Сортировка ──
|
||||
|
||||
%% @doc Сортировка по word по возрастанию.
|
||||
test_sort_by_word_asc(Token) ->
|
||||
ct:pal(" TEST: Sort banned words by word asc"),
|
||||
Words = api_test_runner:admin_get(<<"/v1/admin/banned-words?sort=word&order=asc">>, Token),
|
||||
WordList = [maps:get(<<"word">>, W) || W <- Words],
|
||||
?assertEqual(lists:sort(WordList), WordList),
|
||||
ct:pal(" OK: sorted by word asc").
|
||||
|
||||
%% @doc Сортировка по added_at по убыванию.
|
||||
test_sort_by_added_at_desc(Token) ->
|
||||
ct:pal(" TEST: Sort banned words by added_at desc"),
|
||||
Words = api_test_runner:admin_get(<<"/v1/admin/banned-words?sort=added_at&order=desc">>, Token),
|
||||
Dates = [maps:get(<<"added_at">>, W) || W <- Words],
|
||||
Sorted = lists:reverse(lists:sort(Dates)),
|
||||
?assertEqual(Sorted, Dates),
|
||||
ct:pal(" OK: sorted by added_at desc").
|
||||
|
||||
%% ── Комбинации ──
|
||||
|
||||
%% @doc Фильтр + сортировка: q=alpha, сортировка по added_at asc.
|
||||
test_filter_with_sort(Token, Unique) ->
|
||||
ct:pal(" TEST: Filter + sort (q=alpha, sort=added_at, order=asc)"),
|
||||
Q = <<"alpha_", Unique/binary>>,
|
||||
Words = api_test_runner:admin_get(
|
||||
<<"/v1/admin/banned-words?q=", Q/binary, "&sort=added_at&order=asc">>, Token),
|
||||
?assert(length(Words) >= 1),
|
||||
% Проверяем, что все слова содержат "alpha"
|
||||
[?assert(binary:match(maps:get(<<"word">>, W), <<"alpha">>) =/= nomatch) || W <- Words],
|
||||
Dates = [maps:get(<<"added_at">>, W) || W <- Words],
|
||||
?assertEqual(lists:sort(Dates), Dates),
|
||||
ct:pal(" OK: ~p words", [length(Words)]).
|
||||
|
||||
%% @doc Фильтр + сортировка + пагинация: q=beta, sort=word desc, limit=1.
|
||||
test_filter_sort_pagination(Token, Unique) ->
|
||||
ct:pal(" TEST: Filter + sort + pagination (q=beta, sort=word desc, limit=1)"),
|
||||
Q = <<"beta_", Unique/binary>>,
|
||||
{ok, 200, Headers, Body} = api_test_runner:admin_request(get,
|
||||
<<"/v1/admin/banned-words?q=", Q/binary, "&sort=word&order=desc&limit=1&offset=0">>, Token),
|
||||
Words = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
?assert(length(Words) =< 1),
|
||||
case Words of
|
||||
[W | _] -> ?assert(binary:match(maps:get(<<"word">>, W), <<"beta">>) =/= nomatch);
|
||||
_ -> ok
|
||||
end,
|
||||
Total = list_to_integer(
|
||||
proplists:get_value("x-total-count", Headers, "0")),
|
||||
?assert(Total >= 1),
|
||||
ct:pal(" OK: got ~p words (total ~p)", [length(Words), Total]).
|
||||
Reference in New Issue
Block a user