Files
EventHubBack/test/unit/infra_cleanup_tests.erl
T
aleksey 6c0775ee5e
CI / test (push) Failing after 5m58s
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
feat(infra): add TTL cleanup worker for append-only tables
Periodically prune old read notifications, revoked/expired auth sessions,
and admin_audit in chunked transactions; wire under eventhub config.
2026-08-03 22:57:41 +03:00

133 lines
4.5 KiB
Erlang

-module(infra_cleanup_tests).
-include_lib("eunit/include/eunit.hrl").
-include("records.hrl").
-define(TABLES, [notification, auth_session, admin_audit]).
setup() ->
eh_test_support:start_mnesia(),
eh_test_support:ensure_tables(?TABLES),
ok.
cleanup(_) ->
eh_test_support:clear_tables(?TABLES),
eh_test_support:delete_tables(?TABLES),
eh_test_support:stop_mnesia(),
ok.
infra_cleanup_test_() ->
{foreach, fun setup/0, fun cleanup/1, [
{"cutoff_datetime is in the past", fun test_cutoff_datetime/0},
{"keeps unread notifications", fun test_keeps_unread_notifications/0},
{"deletes old read notifications", fun test_deletes_old_read_notifications/0},
{"keeps active non-expired sessions", fun test_keeps_active_sessions/0},
{"deletes old revoked sessions", fun test_deletes_old_revoked_sessions/0},
{"deletes old admin_audit", fun test_deletes_old_admin_audit/0}
]}.
test_cutoff_datetime() ->
Cutoff = infra_cleanup:cutoff_datetime(30),
NowSecs = calendar:datetime_to_gregorian_seconds(calendar:universal_time()),
CutoffSecs = calendar:datetime_to_gregorian_seconds(Cutoff),
?assert(CutoffSecs < NowSecs),
%% ~30 days (± a few seconds for test runtime)
DiffDays = (NowSecs - CutoffSecs) / 86400,
?assert(DiffDays > 29.9),
?assert(DiffDays < 30.1).
test_keeps_unread_notifications() ->
Old = infra_cleanup:cutoff_datetime(60),
write_notification(<<"n_unread">>, false, Old),
write_notification(<<"n_read_recent">>, true, calendar:universal_time()),
#{notifications := N} = infra_cleanup:run_once(),
?assertEqual(0, N),
?assertMatch([_], mnesia:dirty_read(notification, <<"n_unread">>)),
?assertMatch([_], mnesia:dirty_read(notification, <<"n_read_recent">>)).
test_deletes_old_read_notifications() ->
Old = infra_cleanup:cutoff_datetime(60),
write_notification(<<"n_old_read">>, true, Old),
write_notification(<<"n_unread">>, false, Old),
#{notifications := N} = infra_cleanup:run_once(),
?assertEqual(1, N),
?assertEqual([], mnesia:dirty_read(notification, <<"n_old_read">>)),
?assertMatch([_], mnesia:dirty_read(notification, <<"n_unread">>)).
test_keeps_active_sessions() ->
Now = calendar:universal_time(),
Future = days_offset(Now, 7),
Recent = days_offset(Now, -1),
write_auth_session(<<"s_active">>, false, Future, Recent),
#{auth_sessions := N} = infra_cleanup:run_once(),
?assertEqual(0, N),
?assertMatch([_], mnesia:dirty_read(auth_session, <<"s_active">>)).
test_deletes_old_revoked_sessions() ->
Now = calendar:universal_time(),
Old = days_offset(Now, -14),
Future = days_offset(Now, 7),
write_auth_session(<<"s_revoked_old">>, true, Future, Old),
write_auth_session(<<"s_active">>, false, Future, Old),
#{auth_sessions := N} = infra_cleanup:run_once(),
?assert(N >= 1),
?assertEqual([], mnesia:dirty_read(auth_session, <<"s_revoked_old">>)),
%% Active but old created_at with future expiry must stay
?assertMatch([_], mnesia:dirty_read(auth_session, <<"s_active">>)).
test_deletes_old_admin_audit() ->
Now = calendar:universal_time(),
Old = days_offset(Now, -120),
write_admin_audit(<<"a_old">>, Old),
write_admin_audit(<<"a_new">>, Now),
#{admin_audit := N} = infra_cleanup:run_once(),
?assertEqual(1, N),
?assertEqual([], mnesia:dirty_read(admin_audit, <<"a_old">>)),
?assertMatch([_], mnesia:dirty_read(admin_audit, <<"a_new">>)).
%%%===================================================================
%%% Helpers
%%%===================================================================
write_notification(Id, IsRead, CreatedAt) ->
mnesia:dirty_write(#notification{
id = Id,
user_id = <<"u1">>,
type = custom,
title = <<"t">>,
body = <<"b">>,
is_read = IsRead,
created_at = CreatedAt
}).
write_auth_session(Id, Revoked, ExpiresAt, CreatedAt) ->
mnesia:dirty_write(#auth_session{
session_id = Id,
family_id = <<"fam">>,
subject_id = <<"subj">>,
subject_type = user,
client_type = <<"web">>,
current_jti = <<"jti">>,
expires_at = ExpiresAt,
revoked = Revoked,
created_at = CreatedAt,
updated_at = CreatedAt
}).
write_admin_audit(Id, Timestamp) ->
mnesia:dirty_write(#admin_audit{
id = Id,
admin_id = <<"adm">>,
email = <<"a@test.local">>,
role = admin,
action = <<"test">>,
entity_type = <<"user">>,
entity_id = <<"u1">>,
timestamp = Timestamp,
ip = <<"127.0.0.1">>,
reason = <<>>
}).
days_offset(Datetime, Days) ->
Secs = calendar:datetime_to_gregorian_seconds(Datetime),
calendar:gregorian_seconds_to_datetime(Secs + Days * 86400).