feat(calendar): single Default personal with guards and backfill
CI / test (push) Failing after 7m22s
CI / deploy-ift (push) Has been skipped
CI / e2e-ift (push) Has been skipped
CI / deploy-stage (push) Has been skipped
CI / e2e-stage (push) Has been skipped

Ensure title Default; reject second personal and delete/type-change of
the sole personal; migrate extras to commercial or soft-delete.

Refs EventHub/EventHubBack#64
This commit is contained in:
2026-07-30 22:07:15 +03:00
parent bb37909ffb
commit 10464de3ec
5 changed files with 157 additions and 45 deletions
+52 -13
View File
@@ -41,7 +41,10 @@ logic_calendar_test_() ->
{"Reject invalid default_location shape", fun test_update_settings_invalid_location/0},
{"Delete calendar test", fun test_delete_calendar/0},
{"Access control test", fun test_access_control/0},
{"Ensure default calendar test", fun test_ensure_default_calendar/0}
{"Ensure default calendar test", fun test_ensure_default_calendar/0},
{"Second personal rejected", fun test_personal_exists/0},
{"Personal type change forbidden", fun test_default_calendar_type_change/0},
{"Backfill extras to commercial or delete", fun test_backfill_extras/0}
]}.
create_test_user() ->
@@ -77,8 +80,6 @@ test_create_commercial_gate() ->
logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial)),
{ok, Personal} = logic_calendar:create_calendar(UserId, <<"Personal">>, <<>>, manual, personal),
?assertEqual(personal, Personal#calendar.type),
?assertMatch({error, subscription_required},
logic_calendar:update_calendar(UserId, Personal#calendar.id, [{type, commercial}])),
{ok, _} = logic_subscription:start_trial(UserId),
{ok, Commercial} = logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial),
?assertEqual(commercial, Commercial#calendar.type).
@@ -101,17 +102,19 @@ test_get_calendar() ->
test_list_calendars() ->
UserId = create_test_user(),
{ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 1">>, <<"">>, manual),
{ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 2">>, <<"">>, auto),
{ok, _} = logic_subscription:start_trial(UserId),
{ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 2">>, <<"">>, auto, commercial),
{ok, Calendars} = logic_calendar:list_calendars(UserId),
?assertEqual(2, length(Calendars)).
test_update_calendar() ->
UserId = create_test_user(),
{ok, Calendar} = logic_calendar:create_calendar(UserId, <<"Original">>, <<"">>, manual),
{ok, _} = logic_subscription:start_trial(UserId),
{ok, Calendar} = logic_calendar:create_calendar(
UserId, <<"Original">>, <<"">>, manual, commercial),
Updates = [{title, <<"Updated">>}, {type, commercial}, {confirmation, auto}],
Updates = [{title, <<"Updated">>}, {confirmation, auto}],
{ok, Updated} = logic_calendar:update_calendar(UserId, Calendar#calendar.id, Updates),
?assertEqual(<<"Updated">>, Updated#calendar.title),
?assertEqual(commercial, Updated#calendar.type),
@@ -216,12 +219,17 @@ test_update_settings_invalid_location() ->
test_delete_calendar() ->
UserId = create_test_user(),
{ok, Calendar} = logic_calendar:create_calendar(UserId, <<"Test">>, <<"">>, manual),
{ok, Personal} = logic_calendar:create_calendar(UserId, <<"Test">>, <<"">>, manual),
?assertMatch({error, default_calendar},
logic_calendar:delete_calendar(UserId, Personal#calendar.id)),
{ok, Deleted} = logic_calendar:delete_calendar(UserId, Calendar#calendar.id),
{ok, _} = logic_subscription:start_trial(UserId),
{ok, Commercial} = logic_calendar:create_calendar(
UserId, <<"Studio">>, <<"">>, manual, commercial),
{ok, Deleted} = logic_calendar:delete_calendar(UserId, Commercial#calendar.id),
?assertEqual(deleted, Deleted#calendar.status),
?assertMatch({error, access_denied}, logic_calendar:get_calendar(UserId, Calendar#calendar.id)).
?assertMatch({error, access_denied},
logic_calendar:get_calendar(UserId, Commercial#calendar.id)).
test_access_control() ->
OwnerId = create_test_user(),
@@ -234,7 +242,8 @@ test_access_control() ->
?assertNot(logic_calendar:can_access(OtherId, PersonalCalendar)),
{ok, _} = logic_subscription:start_trial(OwnerId),
{ok, CommercialCalendar} = logic_calendar:update_calendar(OwnerId, PersonalCalendar#calendar.id, [{type, commercial}]),
{ok, CommercialCalendar} = logic_calendar:create_calendar(
OwnerId, <<"Studio">>, <<"">>, manual, commercial),
?assert(logic_calendar:can_access(OtherId, CommercialCalendar)),
?assertNot(logic_calendar:can_edit(OtherId, CommercialCalendar)),
@@ -249,9 +258,39 @@ test_ensure_default_calendar() ->
?assertEqual(1, length(Calendars)),
Cal = hd(Calendars),
?assertEqual(personal, Cal#calendar.type),
?assertEqual(<<"Default">>, Cal#calendar.title),
?assertEqual(<<>>, Cal#calendar.short_name),
?assertEqual(active, Cal#calendar.status),
?assert(byte_size(Cal#calendar.title) > 0),
ok = logic_calendar:ensure_default_calendar(UserId),
{ok, Calendars2} = logic_calendar:list_calendars(UserId),
?assertEqual(1, length(Calendars2)).
?assertEqual(1, length(Calendars2)),
?assertEqual(<<"Default">>, (hd(Calendars2))#calendar.title).
test_personal_exists() ->
UserId = create_test_user(),
{ok, _} = logic_calendar:create_calendar(UserId, <<"One">>, <<"">>, manual, personal),
?assertMatch({error, personal_exists},
logic_calendar:create_calendar(UserId, <<"Two">>, <<"">>, manual, personal)).
test_default_calendar_type_change() ->
UserId = create_test_user(),
{ok, Personal} = logic_calendar:create_calendar(UserId, <<"One">>, <<"">>, manual, personal),
{ok, _} = logic_subscription:start_trial(UserId),
?assertMatch({error, default_calendar},
logic_calendar:update_calendar(UserId, Personal#calendar.id, [{type, commercial}])).
test_backfill_extras() ->
UserId = create_test_user(),
{ok, Keep0} = core_calendar:create(UserId, <<"Old">>, <<"">>, manual, personal),
{ok, Extra0} = core_calendar:create(UserId, <<"Extra">>, <<"">>, manual, personal),
%% datetime resolution is 1s — pin order explicitly
mnesia:dirty_write(Keep0#calendar{created_at = {{2020, 1, 1}, {0, 0, 0}}}),
mnesia:dirty_write(Extra0#calendar{created_at = {{2020, 1, 2}, {0, 0, 0}}}),
ok = logic_calendar:backfill_single_personal(),
{ok, List} = logic_calendar:list_calendars(UserId),
Personals = [C || C <- List, C#calendar.type =:= personal],
?assertEqual(1, length(Personals)),
?assertEqual(Keep0#calendar.id, (hd(Personals))#calendar.id),
?assertEqual(<<"Default">>, (hd(Personals))#calendar.title),
{ok, ExtraAfter} = core_calendar:get_by_id(Extra0#calendar.id),
?assertEqual(deleted, ExtraAfter#calendar.status).