feat: calendar follow API (POST/DELETE follow, GET following). Refs EventHub/EventHubFront#9
This commit is contained in:
+113
@@ -0,0 +1,113 @@
|
||||
%%%-------------------------------------------------------------------
|
||||
%%% @doc Тесты follow / unfollow чужого календаря.
|
||||
%%%
|
||||
%%% POST /v1/calendars/:id/follow
|
||||
%%% DELETE /v1/calendars/:id/follow
|
||||
%%% GET /v1/user/following
|
||||
%%% GET /v1/calendars/:id (поле following)
|
||||
%%% @end
|
||||
%%%-------------------------------------------------------------------
|
||||
-module(user_calendar_follow_tests).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-export([test/0]).
|
||||
|
||||
-spec test() -> ok.
|
||||
test() ->
|
||||
ct:pal("=== User Calendar Follow Tests ==="),
|
||||
OwnerEmail = api_test_runner:unique_email(<<"flowner">>),
|
||||
OwnerToken = api_test_runner:register_and_login(OwnerEmail, <<"pass">>),
|
||||
FollowerEmail = api_test_runner:unique_email(<<"fluser">>),
|
||||
FollowerToken = api_test_runner:register_and_login(FollowerEmail, <<"pass">>),
|
||||
|
||||
%% commercial calendar requires subscription
|
||||
{ok, 201, _, _} = api_test_runner:client_request(
|
||||
post, <<"/v1/subscription">>, OwnerToken,
|
||||
jsx:encode(#{action => <<"start_trial">>})),
|
||||
CalId = api_test_runner:create_calendar(OwnerToken, #{
|
||||
title => <<"FollowMe">>,
|
||||
type => <<"commercial">>
|
||||
}),
|
||||
FollowPath = <<"/v1/calendars/", CalId/binary, "/follow">>,
|
||||
|
||||
test_follow(FollowerToken, FollowPath, CalId),
|
||||
test_follow_idempotent(FollowerToken, FollowPath, CalId),
|
||||
test_get_calendar_following(FollowerToken, CalId, true),
|
||||
test_list_following(FollowerToken, CalId),
|
||||
test_own_forbidden(OwnerToken, FollowPath),
|
||||
test_unfollow(FollowerToken, FollowPath, CalId),
|
||||
test_unfollow_idempotent(FollowerToken, FollowPath, CalId),
|
||||
test_get_calendar_following(FollowerToken, CalId, false),
|
||||
test_list_following_empty(FollowerToken),
|
||||
test_unauthorized(FollowPath),
|
||||
|
||||
ct:pal("=== All user calendar follow tests passed ==="),
|
||||
ok.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Cases
|
||||
%%%===================================================================
|
||||
|
||||
test_follow(Token, Path, CalId) ->
|
||||
ct:pal(" TEST: POST follow"),
|
||||
{ok, 200, _, Body} = api_test_runner:client_request(post, Path, Token, <<"{}">>),
|
||||
Resp = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
?assertEqual(CalId, maps:get(<<"calendar_id">>, Resp)),
|
||||
?assertEqual(true, maps:get(<<"following">>, Resp)),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_follow_idempotent(Token, Path, CalId) ->
|
||||
ct:pal(" TEST: POST follow idempotent"),
|
||||
{ok, 200, _, Body} = api_test_runner:client_request(post, Path, Token, <<"{}">>),
|
||||
Resp = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
?assertEqual(CalId, maps:get(<<"calendar_id">>, Resp)),
|
||||
?assertEqual(true, maps:get(<<"following">>, Resp)),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_get_calendar_following(Token, CalId, Expected) ->
|
||||
ct:pal(" TEST: GET calendar following=~p", [Expected]),
|
||||
Cal = api_test_runner:client_get(<<"/v1/calendars/", CalId/binary>>, Token),
|
||||
?assertEqual(Expected, maps:get(<<"following">>, Cal)),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_list_following(Token, CalId) ->
|
||||
ct:pal(" TEST: GET /v1/user/following"),
|
||||
List = api_test_runner:client_get(<<"/v1/user/following">>, Token),
|
||||
?assert(is_list(List)),
|
||||
Match = [C || C <- List, maps:get(<<"id">>, C) =:= CalId],
|
||||
?assertMatch([_], Match),
|
||||
[C] = Match,
|
||||
?assertEqual(true, maps:get(<<"following">>, C)),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_own_forbidden(Token, Path) ->
|
||||
ct:pal(" TEST: follow own calendar forbidden"),
|
||||
Resp = api_test_runner:client_request(post, Path, Token, <<"{}">>),
|
||||
?assertMatch({ok, 403, _, _}, Resp),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_unfollow(Token, Path, CalId) ->
|
||||
ct:pal(" TEST: DELETE unfollow"),
|
||||
Resp = api_test_runner:client_delete(Path, Token),
|
||||
?assertEqual(CalId, maps:get(<<"calendar_id">>, Resp)),
|
||||
?assertEqual(false, maps:get(<<"following">>, Resp)),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_unfollow_idempotent(Token, Path, CalId) ->
|
||||
ct:pal(" TEST: DELETE unfollow idempotent"),
|
||||
Resp = api_test_runner:client_delete(Path, Token),
|
||||
?assertEqual(CalId, maps:get(<<"calendar_id">>, Resp)),
|
||||
?assertEqual(false, maps:get(<<"following">>, Resp)),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_list_following_empty(Token) ->
|
||||
ct:pal(" TEST: GET following empty"),
|
||||
List = api_test_runner:client_get(<<"/v1/user/following">>, Token),
|
||||
?assertEqual([], List),
|
||||
ct:pal(" OK").
|
||||
|
||||
test_unauthorized(Path) ->
|
||||
ct:pal(" TEST: unauthorized"),
|
||||
Resp = api_test_runner:client_request(post, Path, <<>>, <<"{}">>),
|
||||
?assertMatch({ok, 401, _, _}, Resp),
|
||||
ct:pal(" OK").
|
||||
Regular → Executable
+4
@@ -40,6 +40,7 @@ all() ->
|
||||
user_test_reviews,
|
||||
user_test_review_by_id,
|
||||
user_test_review_vote,
|
||||
user_test_calendar_follow,
|
||||
user_test_my_reviews,
|
||||
user_test_search,
|
||||
user_test_refresh,
|
||||
@@ -140,6 +141,9 @@ user_test_review_by_id(_Config) ->
|
||||
user_test_review_vote(_Config) ->
|
||||
user_review_vote_tests:test().
|
||||
|
||||
user_test_calendar_follow(_Config) ->
|
||||
user_calendar_follow_tests:test().
|
||||
|
||||
user_test_my_reviews(_Config) ->
|
||||
user_my_reviews_tests:test().
|
||||
|
||||
|
||||
Executable
+49
@@ -0,0 +1,49 @@
|
||||
-module(core_calendar_follow_tests).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
-define(TABLES, [calendar_follow]).
|
||||
|
||||
setup() ->
|
||||
eh_test_support:start_mnesia(),
|
||||
eh_test_support:ensure_tables(?TABLES),
|
||||
ok.
|
||||
|
||||
cleanup(_) ->
|
||||
eh_test_support:delete_tables(?TABLES),
|
||||
eh_test_support:stop_mnesia(),
|
||||
ok.
|
||||
|
||||
core_calendar_follow_test_() ->
|
||||
{foreach, fun setup/0, fun cleanup/1, [
|
||||
{"follow and is_following", fun test_follow/0},
|
||||
{"follow idempotent", fun test_follow_idempotent/0},
|
||||
{"unfollow", fun test_unfollow/0},
|
||||
{"list_by_user", fun test_list_by_user/0}
|
||||
]}.
|
||||
|
||||
test_follow() ->
|
||||
{ok, F} = core_calendar_follow:follow(<<"cal1">>, <<"u1">>),
|
||||
?assertEqual(<<"cal1">>, F#calendar_follow.calendar_id),
|
||||
?assertEqual(<<"u1">>, F#calendar_follow.user_id),
|
||||
?assert(core_calendar_follow:is_following(<<"u1">>, <<"cal1">>)),
|
||||
?assertNot(core_calendar_follow:is_following(<<"u2">>, <<"cal1">>)).
|
||||
|
||||
test_follow_idempotent() ->
|
||||
{ok, F1} = core_calendar_follow:follow(<<"cal1">>, <<"u1">>),
|
||||
{ok, F2} = core_calendar_follow:follow(<<"cal1">>, <<"u1">>),
|
||||
?assertEqual(F1#calendar_follow.id, F2#calendar_follow.id),
|
||||
?assertEqual(1, length(core_calendar_follow:list_by_user(<<"u1">>))).
|
||||
|
||||
test_unfollow() ->
|
||||
{ok, _} = core_calendar_follow:follow(<<"cal1">>, <<"u1">>),
|
||||
ok = core_calendar_follow:unfollow(<<"cal1">>, <<"u1">>),
|
||||
?assertNot(core_calendar_follow:is_following(<<"u1">>, <<"cal1">>)),
|
||||
ok = core_calendar_follow:unfollow(<<"cal1">>, <<"u1">>).
|
||||
|
||||
test_list_by_user() ->
|
||||
{ok, _} = core_calendar_follow:follow(<<"cal1">>, <<"u1">>),
|
||||
{ok, _} = core_calendar_follow:follow(<<"cal2">>, <<"u1">>),
|
||||
{ok, _} = core_calendar_follow:follow(<<"cal1">>, <<"u2">>),
|
||||
?assertEqual(2, length(core_calendar_follow:list_by_user(<<"u1">>))),
|
||||
?assertEqual(2, length(core_calendar_follow:list_by_calendar(<<"cal1">>))).
|
||||
Regular → Executable
+2
@@ -126,6 +126,8 @@ table_opts(calendar) ->
|
||||
[{ram_copies, [node()]}, {attributes, record_info(fields, calendar)}];
|
||||
table_opts(calendar_share) ->
|
||||
[{ram_copies, [node()]}, {attributes, record_info(fields, calendar_share)}];
|
||||
table_opts(calendar_follow) ->
|
||||
[{ram_copies, [node()]}, {attributes, record_info(fields, calendar_follow)}];
|
||||
table_opts(calendar_specialist) ->
|
||||
[{ram_copies, [node()]}, {attributes, record_info(fields, calendar_specialist)}];
|
||||
table_opts(event) ->
|
||||
|
||||
Regular → Executable
+2
-1
@@ -10,7 +10,8 @@
|
||||
"20260716230000_ticket_source_and_hash_index",
|
||||
"20260717180000_stats_counters",
|
||||
"20260717190000_admin_stats_indexes",
|
||||
"20260719210000_review_vote"
|
||||
"20260719210000_review_vote",
|
||||
"20260720210000_calendar_follow"
|
||||
]).
|
||||
|
||||
setup() ->
|
||||
|
||||
Reference in New Issue
Block a user