feat(share): calendar_share invite/ACL for personal and commercial.
Co-editor and deputy grants with mirror_to_default; personal out of search; list owned+shared. Refs EventHub/EventHubBack#73
This commit is contained in:
@@ -0,0 +1,137 @@
|
||||
-module(logic_calendar_share_tests).
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
-include("records.hrl").
|
||||
|
||||
-define(TABLES, [user, calendar, calendar_share, calendar_share_invite, notification, subscription]).
|
||||
|
||||
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.
|
||||
|
||||
logic_calendar_share_test_() ->
|
||||
{foreach, fun setup/0, fun cleanup/1, [
|
||||
{"personal invite accept mirror default", fun test_personal_invite_accept/0},
|
||||
{"commercial deputy write can_edit", fun test_commercial_deputy/0},
|
||||
{"decline invite", fun test_decline/0},
|
||||
{"duplicate pending", fun test_duplicate_pending/0},
|
||||
{"revoke share", fun test_revoke/0},
|
||||
{"personal access via share read", fun test_personal_share_access/0},
|
||||
{"list calendars includes shared", fun test_list_includes_shared/0},
|
||||
{"search excludes personal", fun test_search_excludes_personal/0}
|
||||
]}.
|
||||
|
||||
seed_owner_personal() ->
|
||||
Id = base64:encode(crypto:strong_rand_bytes(12), #{padding => false}),
|
||||
Owner = eh_test_support:make_user(#{
|
||||
id => Id, email => <<"own-", Id/binary, "@ex.com">>, status => active}),
|
||||
mnesia:dirty_write(Owner),
|
||||
{ok, Cal} = core_calendar:create(Owner#user.id, <<"Default">>, <<>>, manual, personal),
|
||||
{Owner#user.id, Cal#calendar.id}.
|
||||
|
||||
seed_owner_commercial() ->
|
||||
Id = base64:encode(crypto:strong_rand_bytes(12), #{padding => false}),
|
||||
Owner = eh_test_support:make_user(#{
|
||||
id => Id, email => <<"co-", Id/binary, "@ex.com">>, status => active}),
|
||||
mnesia:dirty_write(Owner),
|
||||
{ok, _} = core_subscription:create(Owner#user.id, monthly, true),
|
||||
{ok, Cal} = core_calendar:create(Owner#user.id, <<"Studio">>, <<>>, manual, commercial),
|
||||
{Owner#user.id, Cal#calendar.id}.
|
||||
|
||||
make_active_user(Email) ->
|
||||
Id = base64:encode(crypto:strong_rand_bytes(12), #{padding => false}),
|
||||
U = eh_test_support:make_user(#{
|
||||
id => Id, email => Email, status => active, nickname => <<"n-", Id/binary>>}),
|
||||
mnesia:dirty_write(U),
|
||||
U.
|
||||
|
||||
test_personal_invite_accept() ->
|
||||
{OwnerId, CalId} = seed_owner_personal(),
|
||||
Co = make_active_user(<<"co@ex.com">>),
|
||||
{ok, Inv} = logic_calendar_share_invite:create(OwnerId, CalId,
|
||||
#{user_id => Co#user.id}, #{rights => write}),
|
||||
?assertEqual(pending, Inv#calendar_share_invite.status),
|
||||
{ok, Inv2, Share} = logic_calendar_share_invite:accept(Co#user.id,
|
||||
Inv#calendar_share_invite.id, #{}),
|
||||
?assertEqual(accepted, Inv2#calendar_share_invite.status),
|
||||
?assertEqual(write, Share#calendar_share.rights),
|
||||
?assertEqual(true, Share#calendar_share.mirror_to_default),
|
||||
{ok, Cal} = core_calendar:get_by_id(CalId),
|
||||
?assert(logic_calendar:can_edit(Co#user.id, Cal)),
|
||||
?assert(logic_calendar:can_access(Co#user.id, Cal)).
|
||||
|
||||
test_commercial_deputy() ->
|
||||
{OwnerId, CalId} = seed_owner_commercial(),
|
||||
Dep = make_active_user(<<"dep@ex.com">>),
|
||||
{ok, Inv} = logic_calendar_share_invite:create(OwnerId, CalId,
|
||||
#{user_id => Dep#user.id}, #{rights => write}),
|
||||
{ok, _, Share} = logic_calendar_share_invite:accept(Dep#user.id,
|
||||
Inv#calendar_share_invite.id, #{}),
|
||||
?assertEqual(false, Share#calendar_share.mirror_to_default),
|
||||
{ok, Cal} = core_calendar:get_by_id(CalId),
|
||||
?assert(logic_calendar:can_edit(Dep#user.id, Cal)),
|
||||
?assertEqual({error, access_denied},
|
||||
logic_calendar:delete_calendar(Dep#user.id, CalId)).
|
||||
|
||||
test_decline() ->
|
||||
{OwnerId, CalId} = seed_owner_personal(),
|
||||
U = make_active_user(<<"d@ex.com">>),
|
||||
{ok, Inv} = logic_calendar_share_invite:create(OwnerId, CalId,
|
||||
#{user_id => U#user.id}, #{}),
|
||||
{ok, Inv2} = logic_calendar_share_invite:decline(U#user.id, Inv#calendar_share_invite.id),
|
||||
?assertEqual(declined, Inv2#calendar_share_invite.status),
|
||||
?assertEqual({error, not_found}, core_calendar_share:get(CalId, U#user.id)).
|
||||
|
||||
test_duplicate_pending() ->
|
||||
{OwnerId, CalId} = seed_owner_personal(),
|
||||
U = make_active_user(<<"dup@ex.com">>),
|
||||
{ok, _} = logic_calendar_share_invite:create(OwnerId, CalId, #{user_id => U#user.id}, #{}),
|
||||
?assertEqual({error, already_pending},
|
||||
logic_calendar_share_invite:create(OwnerId, CalId, #{user_id => U#user.id}, #{})).
|
||||
|
||||
test_revoke() ->
|
||||
{OwnerId, CalId} = seed_owner_personal(),
|
||||
U = make_active_user(<<"rev@ex.com">>),
|
||||
{ok, Inv} = logic_calendar_share_invite:create(OwnerId, CalId, #{user_id => U#user.id}, #{}),
|
||||
{ok, _, _} = logic_calendar_share_invite:accept(U#user.id, Inv#calendar_share_invite.id, #{}),
|
||||
ok = logic_calendar_share:revoke(OwnerId, CalId, U#user.id),
|
||||
{ok, Cal} = core_calendar:get_by_id(CalId),
|
||||
?assertNot(logic_calendar:can_access(U#user.id, Cal)).
|
||||
|
||||
test_personal_share_access() ->
|
||||
{OwnerId, CalId} = seed_owner_personal(),
|
||||
U = make_active_user(<<"read@ex.com">>),
|
||||
Stranger = make_active_user(<<"str@ex.com">>),
|
||||
{ok, Inv} = logic_calendar_share_invite:create(OwnerId, CalId,
|
||||
#{user_id => U#user.id}, #{rights => read}),
|
||||
{ok, _, _} = logic_calendar_share_invite:accept(U#user.id, Inv#calendar_share_invite.id,
|
||||
#{mirror_to_default => false}),
|
||||
{ok, Cal} = core_calendar:get_by_id(CalId),
|
||||
?assert(logic_calendar:can_access(U#user.id, Cal)),
|
||||
?assertNot(logic_calendar:can_edit(U#user.id, Cal)),
|
||||
?assertNot(logic_calendar:can_access(Stranger#user.id, Cal)).
|
||||
|
||||
test_list_includes_shared() ->
|
||||
{OwnerId, CalId} = seed_owner_personal(),
|
||||
U = make_active_user(<<"list@ex.com">>),
|
||||
{ok, Inv} = logic_calendar_share_invite:create(OwnerId, CalId, #{user_id => U#user.id}, #{}),
|
||||
{ok, _, _} = logic_calendar_share_invite:accept(U#user.id, Inv#calendar_share_invite.id, #{}),
|
||||
{ok, List} = logic_calendar:list_calendars(U#user.id),
|
||||
Ids = [C#calendar.id || C <- List],
|
||||
?assert(lists:member(CalId, Ids)).
|
||||
|
||||
test_search_excludes_personal() ->
|
||||
{_OwnerId, CalId} = seed_owner_personal(),
|
||||
{ok, Cal} = core_calendar:get_by_id(CalId),
|
||||
?assertEqual(false, logic_search_discoverable(Cal)).
|
||||
|
||||
%% Access private helper via same rule as logic_search (duplicated check).
|
||||
logic_search_discoverable(#calendar{type = personal}) -> false;
|
||||
logic_search_discoverable(#calendar{type = commercial} = C) ->
|
||||
logic_calendar:booking_open(C);
|
||||
logic_search_discoverable(_) -> false.
|
||||
Reference in New Issue
Block a user