feat(calendar): single Default personal with guards and backfill
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:
@@ -152,6 +152,8 @@ update_calendar(Req) ->
|
|||||||
handler_utils:send_error(Req2, 403, <<"Access denied">>);
|
handler_utils:send_error(Req2, 403, <<"Access denied">>);
|
||||||
{error, subscription_required} ->
|
{error, subscription_required} ->
|
||||||
handler_utils:send_error(Req2, 402, <<"Subscription required for commercial calendar">>);
|
handler_utils:send_error(Req2, 402, <<"Subscription required for commercial calendar">>);
|
||||||
|
{error, default_calendar} ->
|
||||||
|
handler_utils:send_error(Req2, 403, <<"default_calendar">>);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
handler_utils:send_error(Req2, 404, <<"Calendar not found">>);
|
handler_utils:send_error(Req2, 404, <<"Calendar not found">>);
|
||||||
{error, {invalid_settings, Key}} when is_binary(Key) ->
|
{error, {invalid_settings, Key}} when is_binary(Key) ->
|
||||||
@@ -176,6 +178,8 @@ delete_calendar(Req) ->
|
|||||||
case logic_calendar:delete_calendar(UserId, CalendarId) of
|
case logic_calendar:delete_calendar(UserId, CalendarId) of
|
||||||
{ok, _} ->
|
{ok, _} ->
|
||||||
handler_utils:send_json(Req1, 200, #{status => <<"deleted">>});
|
handler_utils:send_json(Req1, 200, #{status => <<"deleted">>});
|
||||||
|
{error, default_calendar} ->
|
||||||
|
handler_utils:send_error(Req1, 403, <<"default_calendar">>);
|
||||||
{error, access_denied} ->
|
{error, access_denied} ->
|
||||||
handler_utils:send_error(Req1, 403, <<"Access denied">>);
|
handler_utils:send_error(Req1, 403, <<"Access denied">>);
|
||||||
{error, not_found} ->
|
{error, not_found} ->
|
||||||
|
|||||||
@@ -140,6 +140,8 @@ create_calendar(Req) ->
|
|||||||
handler_utils:send_json(Req2, 201, Response);
|
handler_utils:send_json(Req2, 201, Response);
|
||||||
{error, subscription_required} ->
|
{error, subscription_required} ->
|
||||||
handler_utils:send_error(Req2, 402, <<"Subscription required for commercial calendar">>);
|
handler_utils:send_error(Req2, 402, <<"Subscription required for commercial calendar">>);
|
||||||
|
{error, personal_exists} ->
|
||||||
|
handler_utils:send_error(Req2, 409, <<"personal_exists">>);
|
||||||
{error, user_inactive} ->
|
{error, user_inactive} ->
|
||||||
handler_utils:send_error(Req2, 403, <<"User account is not active">>);
|
handler_utils:send_error(Req2, 403, <<"User account is not active">>);
|
||||||
{error, {content_banned, _Words}} ->
|
{error, {content_banned, _Words}} ->
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
-include("records.hrl").
|
-include("records.hrl").
|
||||||
|
|
||||||
-export([create_calendar/3, create_calendar/4, create_calendar/5, get_calendar/2, list_calendars/1,
|
-export([create_calendar/3, create_calendar/4, create_calendar/5, get_calendar/2, list_calendars/1,
|
||||||
update_calendar/3, delete_calendar/2, ensure_default_calendar/1]).
|
update_calendar/3, delete_calendar/2, ensure_default_calendar/1, backfill_single_personal/0]).
|
||||||
-export([can_access/2, can_edit/2, booking_open/1]).
|
-export([can_access/2, can_edit/2, booking_open/1]).
|
||||||
-export([normalize_settings/1]).
|
-export([normalize_settings/1]).
|
||||||
-export([admin_list_all/0, admin_get_by_id/1, admin_update/2, admin_delete/1]).
|
-export([admin_list_all/0, admin_get_by_id/1, admin_update/2, admin_delete/1]).
|
||||||
|
|
||||||
|
-define(DEFAULT_PERSONAL_TITLE, <<"Default">>).
|
||||||
|
|
||||||
%% Создание календаря с политикой по умолчанию (manual)
|
%% Создание календаря с политикой по умолчанию (manual)
|
||||||
create_calendar(UserId, Title, Description) ->
|
create_calendar(UserId, Title, Description) ->
|
||||||
create_calendar(UserId, Title, Description, manual).
|
create_calendar(UserId, Title, Description, manual).
|
||||||
@@ -34,7 +36,12 @@ create_calendar(UserId, Title, Description, Confirmation, Type) ->
|
|||||||
{error, subscription_required}
|
{error, subscription_required}
|
||||||
end;
|
end;
|
||||||
personal ->
|
personal ->
|
||||||
core_calendar:create(UserId, Title2, Desc2, Confirmation, Type)
|
case has_active_personal_calendar(UserId) of
|
||||||
|
true ->
|
||||||
|
{error, personal_exists};
|
||||||
|
false ->
|
||||||
|
core_calendar:create(UserId, Title2, Desc2, Confirmation, Type)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
case Result of
|
case Result of
|
||||||
{ok, Cal} ->
|
{ok, Cal} ->
|
||||||
@@ -51,42 +58,91 @@ create_calendar(UserId, Title, Description, Confirmation, Type) ->
|
|||||||
{error, user_not_found}
|
{error, user_not_found}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
%% @doc Создаёт дефолтный personal-календарь после активации пользователя.
|
%% @doc Единственный personal владельца: title Default; лишние → commercial|delete.
|
||||||
%% Идемпотентно: если у владельца уже есть active personal — ok.
|
%% Вызывается после verify; идемпотентно.
|
||||||
-spec ensure_default_calendar(UserId :: binary()) -> ok | {error, term()}.
|
-spec ensure_default_calendar(UserId :: binary()) -> ok | {error, term()}.
|
||||||
ensure_default_calendar(UserId) ->
|
ensure_default_calendar(UserId) ->
|
||||||
case has_active_personal_calendar(UserId) of
|
normalize_owner_personals(UserId).
|
||||||
true ->
|
|
||||||
ok;
|
%% @doc Backfill всех владельцев: ≤1 personal с title Default.
|
||||||
false ->
|
-spec backfill_single_personal() -> ok.
|
||||||
|
backfill_single_personal() ->
|
||||||
|
Owners = lists:usort([
|
||||||
|
C#calendar.owner_id
|
||||||
|
|| C <- core_calendar:list_all(),
|
||||||
|
C#calendar.status =:= active,
|
||||||
|
C#calendar.type =:= personal
|
||||||
|
]),
|
||||||
|
lists:foreach(fun(OwnerId) -> _ = normalize_owner_personals(OwnerId) end, Owners),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
-spec normalize_owner_personals(UserId :: binary()) -> ok | {error, term()}.
|
||||||
|
normalize_owner_personals(UserId) ->
|
||||||
|
Personals = active_personals(UserId),
|
||||||
|
case Personals of
|
||||||
|
[] ->
|
||||||
case core_user:get_by_id(UserId) of
|
case core_user:get_by_id(UserId) of
|
||||||
{ok, User} ->
|
{ok, #user{status = active}} ->
|
||||||
Title = default_calendar_title(User),
|
case create_calendar(UserId, ?DEFAULT_PERSONAL_TITLE, <<>>, manual, personal) of
|
||||||
case create_calendar(UserId, Title, <<>>, manual, personal) of
|
|
||||||
{ok, _} -> ok;
|
{ok, _} -> ok;
|
||||||
Error -> Error
|
Error -> Error
|
||||||
end;
|
end;
|
||||||
|
{ok, _} ->
|
||||||
|
{error, user_inactive};
|
||||||
Error ->
|
Error ->
|
||||||
Error
|
Error
|
||||||
end
|
end;
|
||||||
|
[Keep | Rest] ->
|
||||||
|
_ = core_calendar:update(Keep#calendar.id, [{title, ?DEFAULT_PERSONAL_TITLE}]),
|
||||||
|
lists:foreach(fun convert_or_delete_extra_personal/1, Rest),
|
||||||
|
ok
|
||||||
|
end.
|
||||||
|
|
||||||
|
active_personals(UserId) ->
|
||||||
|
case core_calendar:list_by_owner(UserId) of
|
||||||
|
{ok, Calendars} ->
|
||||||
|
lists:sort(
|
||||||
|
fun(#calendar{created_at = A, id = IdA}, #calendar{created_at = B, id = IdB}) ->
|
||||||
|
{A, IdA} =< {B, IdB}
|
||||||
|
end,
|
||||||
|
[C || C <- Calendars, C#calendar.type =:= personal]
|
||||||
|
);
|
||||||
|
_ ->
|
||||||
|
[]
|
||||||
end.
|
end.
|
||||||
|
|
||||||
has_active_personal_calendar(UserId) ->
|
has_active_personal_calendar(UserId) ->
|
||||||
case core_calendar:list_by_owner(UserId) of
|
active_personals(UserId) =/= [].
|
||||||
{ok, Calendars} ->
|
|
||||||
lists:any(
|
|
||||||
fun(#calendar{type = personal}) -> true;
|
|
||||||
(_) -> false
|
|
||||||
end,
|
|
||||||
Calendars);
|
|
||||||
_ ->
|
|
||||||
false
|
|
||||||
end.
|
|
||||||
|
|
||||||
default_calendar_title(#user{nickname = Nick}) when is_binary(Nick), byte_size(Nick) > 0 ->
|
convert_or_delete_extra_personal(#calendar{id = Id} = Cal) ->
|
||||||
Nick;
|
case calendar_has_content(Id) of
|
||||||
default_calendar_title(_) ->
|
true ->
|
||||||
<<"Мой календарь">>.
|
%% System backfill: bypass subscription gate.
|
||||||
|
_ = core_calendar:update(Id, [{type, commercial}]),
|
||||||
|
ok;
|
||||||
|
false ->
|
||||||
|
_ = core_calendar:delete(Id),
|
||||||
|
ok
|
||||||
|
end,
|
||||||
|
Cal.
|
||||||
|
|
||||||
|
calendar_has_content(CalendarId) ->
|
||||||
|
Events =
|
||||||
|
try
|
||||||
|
case core_event:list_by_calendar(CalendarId) of
|
||||||
|
{ok, Es} -> Es;
|
||||||
|
_ -> []
|
||||||
|
end
|
||||||
|
catch
|
||||||
|
_:_ -> []
|
||||||
|
end,
|
||||||
|
Specs =
|
||||||
|
try
|
||||||
|
core_calendar_specialist:list_by_calendar(CalendarId)
|
||||||
|
catch
|
||||||
|
_:_ -> []
|
||||||
|
end,
|
||||||
|
Events =/= [] orelse Specs =/= [].
|
||||||
|
|
||||||
%% Получение календаря с проверкой доступа
|
%% Получение календаря с проверкой доступа
|
||||||
get_calendar(UserId, CalendarId) ->
|
get_calendar(UserId, CalendarId) ->
|
||||||
@@ -155,13 +211,11 @@ apply_calendar_update(CalendarId, Calendar, ValidUpdates) ->
|
|||||||
end
|
end
|
||||||
end.
|
end.
|
||||||
|
|
||||||
gate_type_change(UserId, #calendar{type = personal}, Updates) ->
|
%% Единственный personal нельзя превратить в commercial (дневник пользователя).
|
||||||
|
gate_type_change(_UserId, #calendar{type = personal}, Updates) ->
|
||||||
case lists:keyfind(type, 1, Updates) of
|
case lists:keyfind(type, 1, Updates) of
|
||||||
{type, commercial} ->
|
{type, commercial} ->
|
||||||
case logic_subscription:can_create_commercial_calendar(UserId) of
|
{error, default_calendar};
|
||||||
true -> {ok, Updates};
|
|
||||||
false -> {error, subscription_required}
|
|
||||||
end;
|
|
||||||
_ ->
|
_ ->
|
||||||
{ok, Updates}
|
{ok, Updates}
|
||||||
end;
|
end;
|
||||||
@@ -200,9 +254,11 @@ apply_text_results(Updates, [], []) -> Updates;
|
|||||||
apply_text_results(Updates, [F | Fs], [T | Ts]) ->
|
apply_text_results(Updates, [F | Fs], [T | Ts]) ->
|
||||||
apply_text_results(lists:keystore(F, 1, Updates, {F, T}), Fs, Ts).
|
apply_text_results(lists:keystore(F, 1, Updates, {F, T}), Fs, Ts).
|
||||||
|
|
||||||
%% Удаление календаря
|
%% Удаление календаря (единственный personal удалять нельзя)
|
||||||
delete_calendar(UserId, CalendarId) ->
|
delete_calendar(UserId, CalendarId) ->
|
||||||
case core_calendar:get_by_id(CalendarId) of
|
case core_calendar:get_by_id(CalendarId) of
|
||||||
|
{ok, #calendar{type = personal}} ->
|
||||||
|
{error, default_calendar};
|
||||||
{ok, Calendar} ->
|
{ok, Calendar} ->
|
||||||
case can_edit(UserId, Calendar) of
|
case can_edit(UserId, Calendar) of
|
||||||
true ->
|
true ->
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
%% @doc Backfill: ≤1 personal per owner, title Default; extras → commercial | delete.
|
||||||
|
-module('20260730200000_single_default_personal').
|
||||||
|
|
||||||
|
-export([up/0, down/0]).
|
||||||
|
|
||||||
|
up() ->
|
||||||
|
logic_calendar:backfill_single_personal(),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
down() ->
|
||||||
|
ok.
|
||||||
@@ -41,7 +41,10 @@ logic_calendar_test_() ->
|
|||||||
{"Reject invalid default_location shape", fun test_update_settings_invalid_location/0},
|
{"Reject invalid default_location shape", fun test_update_settings_invalid_location/0},
|
||||||
{"Delete calendar test", fun test_delete_calendar/0},
|
{"Delete calendar test", fun test_delete_calendar/0},
|
||||||
{"Access control test", fun test_access_control/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() ->
|
create_test_user() ->
|
||||||
@@ -77,8 +80,6 @@ test_create_commercial_gate() ->
|
|||||||
logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial)),
|
logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial)),
|
||||||
{ok, Personal} = logic_calendar:create_calendar(UserId, <<"Personal">>, <<>>, manual, personal),
|
{ok, Personal} = logic_calendar:create_calendar(UserId, <<"Personal">>, <<>>, manual, personal),
|
||||||
?assertEqual(personal, Personal#calendar.type),
|
?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, _} = logic_subscription:start_trial(UserId),
|
||||||
{ok, Commercial} = logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial),
|
{ok, Commercial} = logic_calendar:create_calendar(UserId, <<"Studio">>, <<>>, manual, commercial),
|
||||||
?assertEqual(commercial, Commercial#calendar.type).
|
?assertEqual(commercial, Commercial#calendar.type).
|
||||||
@@ -101,17 +102,19 @@ test_get_calendar() ->
|
|||||||
test_list_calendars() ->
|
test_list_calendars() ->
|
||||||
UserId = create_test_user(),
|
UserId = create_test_user(),
|
||||||
{ok, _} = logic_calendar:create_calendar(UserId, <<"Calendar 1">>, <<"">>, manual),
|
{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),
|
{ok, Calendars} = logic_calendar:list_calendars(UserId),
|
||||||
?assertEqual(2, length(Calendars)).
|
?assertEqual(2, length(Calendars)).
|
||||||
|
|
||||||
test_update_calendar() ->
|
test_update_calendar() ->
|
||||||
UserId = create_test_user(),
|
UserId = create_test_user(),
|
||||||
{ok, Calendar} = logic_calendar:create_calendar(UserId, <<"Original">>, <<"">>, manual),
|
|
||||||
{ok, _} = logic_subscription:start_trial(UserId),
|
{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),
|
{ok, Updated} = logic_calendar:update_calendar(UserId, Calendar#calendar.id, Updates),
|
||||||
?assertEqual(<<"Updated">>, Updated#calendar.title),
|
?assertEqual(<<"Updated">>, Updated#calendar.title),
|
||||||
?assertEqual(commercial, Updated#calendar.type),
|
?assertEqual(commercial, Updated#calendar.type),
|
||||||
@@ -216,12 +219,17 @@ test_update_settings_invalid_location() ->
|
|||||||
|
|
||||||
test_delete_calendar() ->
|
test_delete_calendar() ->
|
||||||
UserId = create_test_user(),
|
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),
|
?assertEqual(deleted, Deleted#calendar.status),
|
||||||
|
?assertMatch({error, access_denied},
|
||||||
?assertMatch({error, access_denied}, logic_calendar:get_calendar(UserId, Calendar#calendar.id)).
|
logic_calendar:get_calendar(UserId, Commercial#calendar.id)).
|
||||||
|
|
||||||
test_access_control() ->
|
test_access_control() ->
|
||||||
OwnerId = create_test_user(),
|
OwnerId = create_test_user(),
|
||||||
@@ -234,7 +242,8 @@ test_access_control() ->
|
|||||||
?assertNot(logic_calendar:can_access(OtherId, PersonalCalendar)),
|
?assertNot(logic_calendar:can_access(OtherId, PersonalCalendar)),
|
||||||
|
|
||||||
{ok, _} = logic_subscription:start_trial(OwnerId),
|
{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)),
|
?assert(logic_calendar:can_access(OtherId, CommercialCalendar)),
|
||||||
?assertNot(logic_calendar:can_edit(OtherId, CommercialCalendar)),
|
?assertNot(logic_calendar:can_edit(OtherId, CommercialCalendar)),
|
||||||
|
|
||||||
@@ -249,9 +258,39 @@ test_ensure_default_calendar() ->
|
|||||||
?assertEqual(1, length(Calendars)),
|
?assertEqual(1, length(Calendars)),
|
||||||
Cal = hd(Calendars),
|
Cal = hd(Calendars),
|
||||||
?assertEqual(personal, Cal#calendar.type),
|
?assertEqual(personal, Cal#calendar.type),
|
||||||
|
?assertEqual(<<"Default">>, Cal#calendar.title),
|
||||||
?assertEqual(<<>>, Cal#calendar.short_name),
|
?assertEqual(<<>>, Cal#calendar.short_name),
|
||||||
?assertEqual(active, Cal#calendar.status),
|
?assertEqual(active, Cal#calendar.status),
|
||||||
?assert(byte_size(Cal#calendar.title) > 0),
|
|
||||||
ok = logic_calendar:ensure_default_calendar(UserId),
|
ok = logic_calendar:ensure_default_calendar(UserId),
|
||||||
{ok, Calendars2} = logic_calendar:list_calendars(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).
|
||||||
Reference in New Issue
Block a user