fix(ct): teardown litter users/calendars after API suites
Track users/calendars created via register_and_login and create_calendar; soft-delete via admin API in end_per_suite. Skip with CT_KEEP_DATA=1. Safety-net soft-deletes remaining @test.local users. Fixes EventHub/EventHubBack#65
This commit is contained in:
@@ -52,6 +52,13 @@
|
||||
-export([verify_user/2]).
|
||||
-export([future_date_iso8601/0, iso8601_utc/1, future_month_range_iso8601/0,
|
||||
next_month_start_iso8601/0, next_month_range_iso8601/0]).
|
||||
-export([
|
||||
track_user/1,
|
||||
track_calendar/1,
|
||||
cleanup_tracked/0,
|
||||
keep_ct_data/0,
|
||||
reset_litter_tracker/0
|
||||
]).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Конфигурация окружения (CT_MODE, ...)
|
||||
@@ -125,6 +132,127 @@ clear_cached_admin_tokens() ->
|
||||
[admin, superadmin, moderator, support]
|
||||
).
|
||||
|
||||
%%%===================================================================
|
||||
%%% Litter tracker (users/calendars created during CT)
|
||||
%%% persistent_term — survives CT testcase process exits (unlike ETS owner).
|
||||
%%%===================================================================
|
||||
|
||||
-define(LITTER_USERS, {?MODULE, tracked_users}).
|
||||
-define(LITTER_CALS, {?MODULE, tracked_calendars}).
|
||||
|
||||
%% @doc CT_KEEP_DATA=1 — skip suite teardown soft-delete (default: clean up).
|
||||
-spec keep_ct_data() -> boolean().
|
||||
keep_ct_data() ->
|
||||
case os:getenv("CT_KEEP_DATA") of
|
||||
"1" -> true;
|
||||
"true" -> true;
|
||||
"yes" -> true;
|
||||
_ -> false
|
||||
end.
|
||||
|
||||
-spec reset_litter_tracker() -> ok.
|
||||
reset_litter_tracker() ->
|
||||
persistent_term:put(?LITTER_USERS, []),
|
||||
persistent_term:put(?LITTER_CALS, []),
|
||||
ok.
|
||||
|
||||
-spec track_user(binary()) -> ok.
|
||||
track_user(UserId) when is_binary(UserId), UserId =/= <<>> ->
|
||||
track_id(?LITTER_USERS, UserId);
|
||||
track_user(_) ->
|
||||
ok.
|
||||
|
||||
-spec track_calendar(binary()) -> ok.
|
||||
track_calendar(CalId) when is_binary(CalId), CalId =/= <<>> ->
|
||||
track_id(?LITTER_CALS, CalId);
|
||||
track_calendar(_) ->
|
||||
ok.
|
||||
|
||||
track_id(Key, Id) ->
|
||||
Old = persistent_term:get(Key, []),
|
||||
case lists:member(Id, Old) of
|
||||
true -> ok;
|
||||
false -> persistent_term:put(Key, [Id | Old]), ok
|
||||
end.
|
||||
|
||||
%% @doc Soft-delete tracked calendars then users via admin API.
|
||||
%% Best-effort: never fails the suite. No-op when CT_KEEP_DATA=1.
|
||||
-spec cleanup_tracked() -> ok.
|
||||
cleanup_tracked() ->
|
||||
case keep_ct_data() of
|
||||
true ->
|
||||
ct:pal("CT_KEEP_DATA set — skip API CT litter cleanup"),
|
||||
ok;
|
||||
false ->
|
||||
do_cleanup_tracked()
|
||||
end.
|
||||
|
||||
do_cleanup_tracked() ->
|
||||
CalIds = lists:usort(persistent_term:get(?LITTER_CALS, [])),
|
||||
UserIds = lists:usort(persistent_term:get(?LITTER_USERS, [])),
|
||||
ct:pal("API CT litter cleanup: ~p calendars, ~p users", [length(CalIds), length(UserIds)]),
|
||||
AdminToken =
|
||||
try get_admin_token()
|
||||
catch
|
||||
_:Reason ->
|
||||
ct:pal("API CT litter cleanup: no admin token (~p) — skip", [Reason]),
|
||||
undefined
|
||||
end,
|
||||
case AdminToken of
|
||||
undefined ->
|
||||
ok;
|
||||
Token ->
|
||||
lists:foreach(
|
||||
fun(Id) ->
|
||||
admin_delete_best_effort(<<"/v1/admin/calendars/", Id/binary>>, Token)
|
||||
end,
|
||||
CalIds
|
||||
),
|
||||
lists:foreach(
|
||||
fun(Id) ->
|
||||
admin_delete_best_effort(<<"/v1/admin/users/", Id/binary>>, Token)
|
||||
end,
|
||||
UserIds
|
||||
),
|
||||
cleanup_test_local_users(Token),
|
||||
reset_litter_tracker(),
|
||||
ok
|
||||
end.
|
||||
|
||||
%% Safety-net: soft-delete users with @test.local (missed by tracker).
|
||||
cleanup_test_local_users(AdminToken) ->
|
||||
case admin_request(get, <<"/v1/admin/users?q=test.local&limit=100">>, AdminToken) of
|
||||
{ok, 200, _, Body} ->
|
||||
Users = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
lists:foreach(
|
||||
fun
|
||||
(#{<<"id">> := Id, <<"email">> := Email}) when is_binary(Email) ->
|
||||
case binary:match(Email, <<"@test.local">>) of
|
||||
nomatch -> ok;
|
||||
_ -> admin_delete_best_effort(<<"/v1/admin/users/", Id/binary>>, AdminToken)
|
||||
end;
|
||||
(_) ->
|
||||
ok
|
||||
end,
|
||||
case Users of
|
||||
L when is_list(L) -> L;
|
||||
_ -> []
|
||||
end
|
||||
);
|
||||
Other ->
|
||||
ct:pal("API CT litter cleanup: list users skip (~p)", [Other]),
|
||||
ok
|
||||
end.
|
||||
|
||||
admin_delete_best_effort(Path, Token) ->
|
||||
case admin_request(delete, Path, Token) of
|
||||
{ok, Status, _, _} when Status >= 200, Status < 300 -> ok;
|
||||
{ok, 404, _, _} -> ok;
|
||||
Other ->
|
||||
ct:pal("API CT litter cleanup: DELETE ~s -> ~p", [Path, Other]),
|
||||
ok
|
||||
end.
|
||||
|
||||
%%%===================================================================
|
||||
%%% Учётные данные администраторов (из переменных окружения)
|
||||
%%%===================================================================
|
||||
@@ -395,6 +523,7 @@ register_and_login(Email, Password) ->
|
||||
jsx:encode(#{email => Email, password => Password})),
|
||||
{ok, 201, _, Body} = Resp,
|
||||
#{<<"user">> := #{<<"id">> := UserId}} = jsx:decode(list_to_binary(Body), [return_maps]),
|
||||
track_user(UserId),
|
||||
|
||||
% 2. Получаем админский токен (используется кеширование)
|
||||
AdminToken = get_admin_token(),
|
||||
@@ -429,6 +558,12 @@ create_calendar(Token, Params) ->
|
||||
case client_request(post, <<"/v1/calendars">>, Token, Body) of
|
||||
{ok, 201, _, RespBody} ->
|
||||
#{<<"id">> := CalId} = jsx:decode(list_to_binary(RespBody), [return_maps]),
|
||||
%% Personal Default is sole — do not track for delete (403 default_calendar).
|
||||
case Type of
|
||||
<<"personal">> -> ok;
|
||||
personal -> ok;
|
||||
_ -> track_calendar(CalId)
|
||||
end,
|
||||
CalId;
|
||||
{ok, 409, _, _} when Type =:= <<"personal">>; Type =:= personal ->
|
||||
%% Sole Default personal already exists — reuse it.
|
||||
|
||||
Reference in New Issue
Block a user