Files
EventHubBack/test/api/admins/admin_banned_words_tests.erl
T

261 lines
12 KiB
Erlang
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
%%%-------------------------------------------------------------------
%%% @doc Тесты административного API для управления бан-словами.
%%%
%%% Покрывает эндпоинты:
%%% GET /v1/admin/banned-words
%%% POST /v1/admin/banned-words
%%% PUT /v1/admin/banned-words/:word
%%% DELETE /v1/admin/banned-words/:word
%%% POST /v1/admin/banned-words/batch
%%%
%%% Проверяет:
%%% - получение списка слов
%%% - добавление нового слова
%%% - редактирование существующего слова
%%% - удаление слова
%%% - массовую загрузку списком
%%% - **фильтрацию** по слову (q)
%%% - **сортировку** по word, added_at
%%% - **комбинации фильтрации, сортировки и пагинации**
%%% - **пагинацию** (limit/offset, x-total-count)
%%% @end
%%%-------------------------------------------------------------------
-module(admin_banned_words_tests).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
%%%===================================================================
%%% Главная тестовая функция
%%%===================================================================
-spec test() -> ok.
test() ->
ct:pal("=== Admin Banned Words Tests ==="),
Token = api_test_runner:get_admin_token(),
% Добавляем несколько слов для тестирования
Unique = integer_to_binary(erlang:system_time()),
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),
test_add_word(Token),
test_update_word(Token, Word1),
test_batch_add_words(Token),
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.
%%%===================================================================
%%% Тестовые функции
%%%===================================================================
%% @doc GET /v1/admin/banned-words список всех слов.
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) >= 3),
ct:pal(" OK: ~p words", [length(Words)]).
%% @doc POST /v1/admin/banned-words добавление нового слова.
test_add_word(Token) ->
ct:pal(" TEST: Add a new banned word"),
Unique = integer_to_binary(erlang:system_time()),
NewWord = <<"newbadword", Unique/binary>>,
Result = api_test_runner:admin_post(<<"/v1/admin/banned-words">>, Token, #{<<"word">> => NewWord}),
?assertEqual(<<"added">>, maps:get(<<"status">>, Result)),
Words = api_test_runner:admin_get(<<"/v1/admin/banned-words">>, Token),
?assert(lists:any(fun(W) -> maps:get(<<"word">>, W) =:= NewWord end, Words)),
ct:pal(" OK").
%% @doc PUT /v1/admin/banned-words/:word редактирование слова.
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 массовая загрузка.
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>>],
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 удаление слова.
test_delete_word(Token, Word) ->
ct:pal(" TEST: Delete a banned word"),
Path = <<"/v1/admin/banned-words/", Word/binary>>,
Result = api_test_runner:admin_delete(Path, Token),
?assertEqual(<<"deleted">>, 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)),
ct:pal(" OK").
%% @doc PUT /v1/admin/banned-words/:word попытка переименовать в уже существующее слово (409).
test_update_word_conflict(Token) ->
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>>,
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 с невалидными элементами.
test_batch_add_invalid(Token) ->
ct:pal(" TEST: Batch add with invalid items"),
Unique = integer_to_binary(erlang:system_time()),
Words = [
<<"valid_", Unique/binary>>,
null,
12345,
<<"valid2_", 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(2, maps:get(<<"added">>, Result)),
?assertEqual(0, maps:get(<<"skipped">>, Result)),
?assertEqual(2, maps:get(<<"invalid_count">>, Result)),
ct:pal(" OK: invalid items filtered").
%% @doc POST /v1/admin/banned-words/batch с дубликатами.
test_batch_add_duplicates(Token) ->
ct:pal(" TEST: Batch add with duplicates"),
Unique = integer_to_binary(erlang:system_time()),
Word1 = <<"dup1_", Unique/binary>>,
Word2 = <<"dup2_", Unique/binary>>,
Words = [Word1, Word2, Word1, Word2],
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(2, maps:get(<<"added">>, Result)),
?assertEqual(2, maps:get(<<"skipped">>, Result)),
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]).