Рефакторинг обработчиков. Часть 3 #21

This commit is contained in:
2026-05-13 23:02:59 +03:00
parent 61bb44ab4a
commit 40806df62a
91 changed files with 6138 additions and 7150 deletions

View File

@@ -0,0 +1,99 @@
%%%-------------------------------------------------------------------
%%% @doc Тесты клиентского API для отмены вхождения повторяющегося события.
%%%
%%% Покрывает эндпоинты:
%%% DELETE /v1/events/:id/occurrences/:start_time
%%%
%%% Проверяет:
%%% - успешную отмену конкретного вхождения
%%% - ошибку 400 для не-recurring события
%%% - ошибку 403 при попытке отмены чужим пользователем
%%% - ошибку 401 без токена
%%% @end
%%%-------------------------------------------------------------------
-module(user_occurrence_cancel_tests).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
%%%===================================================================
%%% Главная тестовая функция
%%%===================================================================
-spec test() -> ok.
test() ->
ct:pal("=== User Occurrence Cancel Tests ==="),
OwnerToken = api_test_runner:get_user_token(),
OtherToken = api_test_runner:register_and_login(
api_test_runner:unique_email(<<"other">>), <<"pass">>),
% Создаём календарь и повторяющееся событие
CalId = api_test_runner:create_calendar(OwnerToken, #{title => <<"OccCancel">>}),
#{<<"id">> := RecurringId} = api_test_runner:client_post(
<<"/v1/calendars/", CalId/binary, "/events">>, OwnerToken,
#{title => <<"Weekly Standup">>,
start_time => <<"2026-06-01T10:00:00Z">>,
duration => 30,
recurrence => #{freq => <<"WEEKLY">>, interval => 1}}),
% Получаем вхождения (ответ список карт)
OccPath = <<"/v1/events/", RecurringId/binary, "/occurrences?from=2026-06-01T00:00:00Z&to=2026-06-30T00:00:00Z">>,
Occurrences = api_test_runner:client_get(OccPath, OwnerToken),
?assert(is_list(Occurrences)),
?assert(length(Occurrences) >= 1),
#{<<"start_time">> := FirstStart} = hd(Occurrences),
test_cancel_occurrence(OwnerToken, RecurringId, FirstStart),
test_cancel_occurrence_on_single_event(OwnerToken, CalId),
test_cancel_occurrence_forbidden(OtherToken, RecurringId, FirstStart),
test_cancel_occurrence_unauthorized(RecurringId, FirstStart),
ct:pal("=== All user occurrence cancel tests passed ==="),
ok.
%%%===================================================================
%%% Тестовые функции
%%%===================================================================
%% @doc Успешная отмена вхождения: 200 OK.
-spec test_cancel_occurrence(binary(), binary(), binary()) -> ok.
test_cancel_occurrence(Token, EventId, StartTime) ->
ct:pal(" TEST: Cancel occurrence"),
Path = <<"/v1/events/", EventId/binary, "/occurrences/", StartTime/binary>>,
Resp = api_test_runner:client_request(delete, Path, Token),
{ok, 200, _, Body} = Resp,
#{<<"status">> := Status} = jsx:decode(list_to_binary(Body), [return_maps]),
?assertEqual(<<"cancelled">>, Status),
ct:pal(" OK: occurrence cancelled").
%% @doc Попытка отменить вхождение для одиночного события: 400.
-spec test_cancel_occurrence_on_single_event(binary(), binary()) -> ok.
test_cancel_occurrence_on_single_event(Token, CalId) ->
ct:pal(" TEST: Cancel occurrence on non-recurring event"),
#{<<"id">> := SingleId} = api_test_runner:client_post(
<<"/v1/calendars/", CalId/binary, "/events">>, Token,
#{title => <<"Single">>,
start_time => <<"2026-06-02T10:00:00Z">>,
duration => 30}),
Path = <<"/v1/events/", SingleId/binary, "/occurrences/2026-06-02T10:00:00Z">>,
Resp = api_test_runner:client_request(delete, Path, Token),
?assertMatch({ok, 400, _, _}, Resp),
ct:pal(" OK: got 400").
%% @doc Попытка отмены чужим пользователем: 403.
-spec test_cancel_occurrence_forbidden(binary(), binary(), binary()) -> ok.
test_cancel_occurrence_forbidden(OtherToken, EventId, StartTime) ->
ct:pal(" TEST: Cancel occurrence by non-owner"),
Path = <<"/v1/events/", EventId/binary, "/occurrences/", StartTime/binary>>,
Resp = api_test_runner:client_request(delete, Path, OtherToken),
?assertMatch({ok, 403, _, _}, Resp),
ct:pal(" OK: got 403").
%% @doc Запрос без токена: 401.
-spec test_cancel_occurrence_unauthorized(binary(), binary()) -> ok.
test_cancel_occurrence_unauthorized(EventId, StartTime) ->
ct:pal(" TEST: Cancel occurrence without token"),
Path = <<"/v1/events/", EventId/binary, "/occurrences/", StartTime/binary>>,
Resp = api_test_runner:client_request(delete, Path, <<>>),
?assertMatch({ok, 401, _, _}, Resp),
ct:pal(" OK: got 401").