%%%------------------------------------------------------------------- %%% @doc Тесты клиентского API для работы с конкретным календарём. %%% Покрывает GET, PUT, DELETE /v1/calendars/:id. %%% @end %%%------------------------------------------------------------------- -module(user_calendar_by_id_tests). -include_lib("eunit/include/eunit.hrl"). -export([test/0]). -spec test() -> ok. test() -> ct:pal("=== User Calendar By ID Tests ==="), Token = api_test_runner:get_user_token(), OtherToken = api_test_runner:register_and_login( api_test_runner:unique_email(<<"other">>), <<"pass">>), %% Studio calendar (sole personal Default cannot be deleted) CalId = api_test_runner:create_calendar(Token, #{title => <<"TestCal">>}), test_get_calendar(Token, CalId), test_get_calendar_guest(Token, CalId), test_get_calendar_not_found(Token), test_update_calendar(Token, CalId), test_update_calendar_settings(Token, CalId), test_update_calendar_forbidden(OtherToken, CalId), test_delete_calendar(Token, CalId), test_delete_default_personal_forbidden(Token), test_delete_calendar_forbidden(OtherToken, CalId), ct:pal("=== All user calendar by id tests passed ==="), ok. test_get_calendar(Token, CalId) -> ct:pal(" TEST: Get calendar by ID"), Path = <<"/v1/calendars/", CalId/binary>>, Cal = api_test_runner:client_get(Path, Token), ?assertEqual(CalId, maps:get(<<"id">>, Cal)), ?assert(maps:is_key(<<"title">>, Cal)), ct:pal(" OK: ~s", [maps:get(<<"title">>, Cal)]). test_get_calendar_guest(Token, CalId) -> ct:pal(" TEST: Guest GET commercial calendar (200), personal (403)"), Path = <<"/v1/calendars/", CalId/binary>>, {ok, 200, _, Body} = api_test_runner:client_request(get, Path, <<>>), #{<<"id">> := CalId} = jsx:decode(list_to_binary(Body), [return_maps]), PersonalId = api_test_runner:existing_personal_calendar_id(Token), PPath = <<"/v1/calendars/", PersonalId/binary>>, {ok, 403, _, _} = api_test_runner:client_request(get, PPath, <<>>), ct:pal(" OK: guest commercial 200, personal 403"). test_get_calendar_not_found(Token) -> ct:pal(" TEST: Get non-existent calendar (404)"), Resp = api_test_runner:client_request(get, <<"/v1/calendars/fakeid">>, Token), ?assertMatch({ok, 404, _, _}, Resp), ct:pal(" OK: got 404"). test_update_calendar(Token, CalId) -> ct:pal(" TEST: Update calendar"), Path = <<"/v1/calendars/", CalId/binary>>, Updated = api_test_runner:client_put(Path, Token, #{title => <<"Updated">>, description => <<"New desc">>}), ?assertEqual(<<"Updated">>, maps:get(<<"title">>, Updated)), ?assertEqual(<<"New desc">>, maps:get(<<"description">>, Updated)), ct:pal(" OK"). test_update_calendar_settings(Token, CalId) -> ct:pal(" TEST: Persist calendar.settings week_patterns + org defaults"), Path = <<"/v1/calendars/", CalId/binary>>, Settings = #{ <<"week_patterns">> => [ #{ <<"id">> => <<"pat1">>, <<"name">> => <<"Mon-Fri">>, <<"slots">> => [ #{<<"weekday">> => 1, <<"time">> => <<"10:00">>, <<"duration">> => 60, <<"title">> => <<"Open">>} ] } ], <<"default_location">> => #{ <<"address">> => <<"ул. Пример, 1">>, <<"lat">> => 55.75, <<"lon">> => 37.61 }, <<"default_duration_minutes">> => 60, <<"default_recurrence">> => #{ <<"enabled">> => true, <<"freq">> => <<"WEEKLY">>, <<"interval">> => 1 } }, Updated = api_test_runner:client_put(Path, Token, #{settings => Settings}), ?assertEqual(Settings, maps:get(<<"settings">>, Updated)), Got = api_test_runner:client_get(Path, Token), ?assertEqual(Settings, maps:get(<<"settings">>, Got)), Bad = api_test_runner:client_request(put, Path, Token, jsx:encode(#{settings => #{default_location => #{address => <<"">>}}})), ?assertMatch({ok, 400, _, _}, Bad), ct:pal(" OK"). test_update_calendar_forbidden(OtherToken, CalId) -> ct:pal(" TEST: Update calendar by non-owner (403)"), Path = <<"/v1/calendars/", CalId/binary>>, Resp = api_test_runner:client_request(put, Path, OtherToken, jsx:encode(#{title => <<"fail">>})), ?assertMatch({ok, 403, _, _}, Resp), ct:pal(" OK: got 403"). test_delete_calendar(Token, CalId) -> ct:pal(" TEST: Delete studio calendar (soft-delete)"), Path = <<"/v1/calendars/", CalId/binary>>, Resp = api_test_runner:client_request(delete, Path, Token), ?assertMatch({ok, 200, _, _}, Resp), ct:pal(" OK: deleted"). test_delete_default_personal_forbidden(Token) -> ct:pal(" TEST: Delete sole personal calendar (403 default_calendar)"), PersonalId = api_test_runner:existing_personal_calendar_id(Token), Path = <<"/v1/calendars/", PersonalId/binary>>, Resp = api_test_runner:client_request(delete, Path, Token), ?assertMatch({ok, 403, _, _}, Resp), ct:pal(" OK: got 403 default_calendar"). test_delete_calendar_forbidden(OtherToken, _DeletedCalId) -> ct:pal(" TEST: Delete calendar by non-owner (403)"), OwnerToken = api_test_runner:get_user_token(), NewCalId = api_test_runner:create_calendar(OwnerToken, #{title => <<"ForbiddenDel">>}), Path = <<"/v1/calendars/", NewCalId/binary>>, Resp = api_test_runner:client_request(delete, Path, OtherToken), ?assertMatch({ok, 403, _, _}, Resp), ct:pal(" OK: got 403").