mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(gateway): always clear expired custom statuses (#2807)
This commit is contained in:
@@ -269,60 +269,45 @@ dispatch_to_sessions(Payload, State) ->
|
|||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
expiry_timer_not_armed_while_disabled_test() ->
|
|
||||||
application:unset_env(fluxer_gateway, custom_status_expiry_enabled),
|
|
||||||
State = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
|
||||||
?assertNot(maps:is_key(?CUSTOM_STATUS_EXPIRY_TIMER, State)).
|
|
||||||
|
|
||||||
expiry_timer_not_armed_without_expires_at_test() ->
|
expiry_timer_not_armed_without_expires_at_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
?assertNot(
|
||||||
?assertNot(
|
maps:is_key(
|
||||||
maps:is_key(
|
?CUSTOM_STATUS_EXPIRY_TIMER,
|
||||||
?CUSTOM_STATUS_EXPIRY_TIMER,
|
refresh_custom_status_expiry_timer(#{custom_status => null})
|
||||||
refresh_custom_status_expiry_timer(#{custom_status => null})
|
|
||||||
)
|
|
||||||
),
|
|
||||||
?assertNot(
|
|
||||||
maps:is_key(
|
|
||||||
?CUSTOM_STATUS_EXPIRY_TIMER,
|
|
||||||
refresh_custom_status_expiry_timer(#{
|
|
||||||
custom_status => #{<<"text">> => <<"hi">>}
|
|
||||||
})
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
end).
|
),
|
||||||
|
?assertNot(
|
||||||
|
maps:is_key(
|
||||||
|
?CUSTOM_STATUS_EXPIRY_TIMER,
|
||||||
|
refresh_custom_status_expiry_timer(#{
|
||||||
|
custom_status => #{<<"text">> => <<"hi">>}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
expiry_timer_armed_for_a_future_expiry_test() ->
|
expiry_timer_armed_for_a_future_expiry_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
State = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
||||||
State = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
?assert(is_reference(maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, State))),
|
||||||
?assert(is_reference(maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, State))),
|
ok = cancel_expiry_timer(maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, State)).
|
||||||
ok = cancel_expiry_timer(maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, State))
|
|
||||||
end).
|
|
||||||
|
|
||||||
expiry_timer_not_armed_for_an_already_expired_status_test() ->
|
expiry_timer_not_armed_for_an_already_expired_status_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
Expired = #{<<"expires_at">> => <<"2020-01-01T00:00:00.000Z">>},
|
||||||
Expired = #{<<"expires_at">> => <<"2020-01-01T00:00:00.000Z">>},
|
State = refresh_custom_status_expiry_timer(#{custom_status => Expired}),
|
||||||
State = refresh_custom_status_expiry_timer(#{custom_status => Expired}),
|
?assertNot(maps:is_key(?CUSTOM_STATUS_EXPIRY_TIMER, State)).
|
||||||
?assertNot(maps:is_key(?CUSTOM_STATUS_EXPIRY_TIMER, State))
|
|
||||||
end).
|
|
||||||
|
|
||||||
expiry_timer_replaces_the_previous_one_test() ->
|
expiry_timer_replaces_the_previous_one_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
First = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
||||||
First = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
Second = refresh_custom_status_expiry_timer(First),
|
||||||
Second = refresh_custom_status_expiry_timer(First),
|
?assertNotEqual(
|
||||||
?assertNotEqual(
|
maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, First),
|
||||||
maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, First),
|
maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, Second)
|
||||||
maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, Second)
|
),
|
||||||
),
|
ok = cancel_expiry_timer(maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, Second)).
|
||||||
ok = cancel_expiry_timer(maps:get(?CUSTOM_STATUS_EXPIRY_TIMER, Second))
|
|
||||||
end).
|
|
||||||
|
|
||||||
expiry_timer_is_dropped_when_the_status_is_cleared_test() ->
|
expiry_timer_is_dropped_when_the_status_is_cleared_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
Armed = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
||||||
Armed = refresh_custom_status_expiry_timer(#{custom_status => future_custom_status()}),
|
Cleared = refresh_custom_status_expiry_timer(Armed#{custom_status => null}),
|
||||||
Cleared = refresh_custom_status_expiry_timer(Armed#{custom_status => null}),
|
?assertNot(maps:is_key(?CUSTOM_STATUS_EXPIRY_TIMER, Cleared)).
|
||||||
?assertNot(maps:is_key(?CUSTOM_STATUS_EXPIRY_TIMER, Cleared))
|
|
||||||
end).
|
|
||||||
|
|
||||||
expiry_timer_delivers_a_reconcile_cast_test() ->
|
expiry_timer_delivers_a_reconcile_cast_test() ->
|
||||||
State = arm_expiry_timer({ok, 1}, #{}),
|
State = arm_expiry_timer({ok, 1}, #{}),
|
||||||
@@ -343,19 +328,4 @@ future_custom_status() ->
|
|||||||
),
|
),
|
||||||
#{<<"text">> => <<"brb">>, <<"expires_at">> => list_to_binary(ExpiresAt)}.
|
#{<<"text">> => <<"brb">>, <<"expires_at">> => list_to_binary(ExpiresAt)}.
|
||||||
|
|
||||||
with_expiry_enabled(Fun) ->
|
|
||||||
Key = custom_status_expiry_enabled,
|
|
||||||
Previous = application:get_env(fluxer_gateway, Key),
|
|
||||||
application:set_env(fluxer_gateway, Key, true),
|
|
||||||
try
|
|
||||||
Fun()
|
|
||||||
after
|
|
||||||
restore_expiry_env(Key, Previous)
|
|
||||||
end.
|
|
||||||
|
|
||||||
restore_expiry_env(Key, undefined) ->
|
|
||||||
application:unset_env(fluxer_gateway, Key);
|
|
||||||
restore_expiry_env(Key, {ok, Value}) ->
|
|
||||||
application:set_env(fluxer_gateway, Key, Value).
|
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|||||||
@@ -96,46 +96,30 @@ build_invisible_binary_normalized_to_offline_test() ->
|
|||||||
?assertEqual(null, maps:get(<<"custom_status">>, Result)).
|
?assertEqual(null, maps:get(<<"custom_status">>, Result)).
|
||||||
|
|
||||||
build_clears_expired_custom_status_test() ->
|
build_clears_expired_custom_status_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
Result = build(test_user(), online, false, false, expired_custom_status()),
|
||||||
Result = build(test_user(), online, false, false, expired_custom_status()),
|
?assertEqual(<<"online">>, maps:get(<<"status">>, Result)),
|
||||||
?assertEqual(<<"online">>, maps:get(<<"status">>, Result)),
|
?assertEqual(null, maps:get(<<"custom_status">>, Result)).
|
||||||
?assertEqual(null, maps:get(<<"custom_status">>, Result))
|
|
||||||
end).
|
|
||||||
|
|
||||||
build_coalesces_an_expired_status_with_no_status_test() ->
|
build_coalesces_an_expired_status_with_no_status_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
?assertEqual(
|
||||||
?assertEqual(
|
build(test_user(), online, false, false, null),
|
||||||
build(test_user(), online, false, false, null),
|
build(test_user(), online, false, false, expired_custom_status())
|
||||||
build(test_user(), online, false, false, expired_custom_status())
|
).
|
||||||
)
|
|
||||||
end).
|
|
||||||
|
|
||||||
build_keeps_unexpired_custom_status_test() ->
|
build_keeps_unexpired_custom_status_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
Live = future_custom_status(),
|
||||||
Live = future_custom_status(),
|
Result = build(test_user(), dnd, false, false, Live),
|
||||||
Result = build(test_user(), dnd, false, false, Live),
|
?assertEqual(Live, maps:get(<<"custom_status">>, Result)).
|
||||||
?assertEqual(Live, maps:get(<<"custom_status">>, Result))
|
|
||||||
end).
|
|
||||||
|
|
||||||
build_keeps_custom_status_without_expires_at_test() ->
|
build_keeps_custom_status_without_expires_at_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
Live = #{<<"text">> => <<"forever">>},
|
||||||
Live = #{<<"text">> => <<"forever">>},
|
Result = build(test_user(), idle, false, false, Live),
|
||||||
Result = build(test_user(), idle, false, false, Live),
|
?assertEqual(Live, maps:get(<<"custom_status">>, Result)).
|
||||||
?assertEqual(Live, maps:get(<<"custom_status">>, Result))
|
|
||||||
end).
|
|
||||||
|
|
||||||
build_keeps_malformed_expires_at_test() ->
|
build_keeps_malformed_expires_at_test() ->
|
||||||
with_expiry_enabled(fun() ->
|
Live = #{<<"text">> => <<"hi">>, <<"expires_at">> => <<"not-a-date">>},
|
||||||
Live = #{<<"text">> => <<"hi">>, <<"expires_at">> => <<"not-a-date">>},
|
Result = build(test_user(), online, false, false, Live),
|
||||||
Result = build(test_user(), online, false, false, Live),
|
?assertEqual(Live, maps:get(<<"custom_status">>, Result)).
|
||||||
?assertEqual(Live, maps:get(<<"custom_status">>, Result))
|
|
||||||
end).
|
|
||||||
|
|
||||||
build_leaves_expired_status_alone_while_disabled_test() ->
|
|
||||||
application:unset_env(fluxer_gateway, custom_status_expiry_enabled),
|
|
||||||
Expired = expired_custom_status(),
|
|
||||||
Result = build(test_user(), online, false, false, Expired),
|
|
||||||
?assertEqual(Expired, maps:get(<<"custom_status">>, Result)).
|
|
||||||
|
|
||||||
test_user() ->
|
test_user() ->
|
||||||
#{<<"id">> => <<"1">>, <<"username">> => <<"Test">>}.
|
#{<<"id">> => <<"1">>, <<"username">> => <<"Test">>}.
|
||||||
@@ -153,19 +137,4 @@ future_custom_status() ->
|
|||||||
erlang:system_time(millisecond) + 3600000, [{unit, millisecond}, {offset, "Z"}]
|
erlang:system_time(millisecond) + 3600000, [{unit, millisecond}, {offset, "Z"}]
|
||||||
),
|
),
|
||||||
#{<<"text">> => <<"brb">>, <<"expires_at">> => list_to_binary(ExpiresAt)}.
|
#{<<"text">> => <<"brb">>, <<"expires_at">> => list_to_binary(ExpiresAt)}.
|
||||||
|
|
||||||
with_expiry_enabled(Fun) ->
|
|
||||||
Key = custom_status_expiry_enabled,
|
|
||||||
Previous = application:get_env(fluxer_gateway, Key),
|
|
||||||
application:set_env(fluxer_gateway, Key, true),
|
|
||||||
try
|
|
||||||
Fun()
|
|
||||||
after
|
|
||||||
restore_expiry_env(Key, Previous)
|
|
||||||
end.
|
|
||||||
|
|
||||||
restore_expiry_env(Key, undefined) ->
|
|
||||||
application:unset_env(fluxer_gateway, Key);
|
|
||||||
restore_expiry_env(Key, {ok, Value}) ->
|
|
||||||
application:set_env(fluxer_gateway, Key, Value).
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
-typing([eqwalizer]).
|
-typing([eqwalizer]).
|
||||||
|
|
||||||
-export([
|
-export([
|
||||||
enabled/0,
|
|
||||||
clear_if_expired/1,
|
clear_if_expired/1,
|
||||||
clear_if_expired/2,
|
clear_if_expired/2,
|
||||||
remaining_ms/2,
|
remaining_ms/2,
|
||||||
@@ -20,26 +19,13 @@
|
|||||||
-type wakeup() :: {ok, pos_integer()} | none.
|
-type wakeup() :: {ok, pos_integer()} | none.
|
||||||
-type offset() :: {ok, integer()} | none.
|
-type offset() :: {ok, integer()} | none.
|
||||||
|
|
||||||
-define(ENABLED_KEY, custom_status_expiry_enabled).
|
|
||||||
-define(MAX_REPAIR_WINDOW_SECONDS, 86400).
|
-define(MAX_REPAIR_WINDOW_SECONDS, 86400).
|
||||||
-define(MAX_TIMER_MS, 86400000).
|
-define(MAX_TIMER_MS, 86400000).
|
||||||
-define(WAKEUP_JITTER_MS, 5000).
|
-define(WAKEUP_JITTER_MS, 5000).
|
||||||
|
|
||||||
-spec enabled() -> boolean().
|
|
||||||
enabled() ->
|
|
||||||
application:get_env(fluxer_gateway, ?ENABLED_KEY, false) =:= true.
|
|
||||||
|
|
||||||
-spec clear_if_expired(term()) -> custom_status().
|
-spec clear_if_expired(term()) -> custom_status().
|
||||||
clear_if_expired(CustomStatus) ->
|
clear_if_expired(CustomStatus) ->
|
||||||
clear_when_enabled(enabled(), CustomStatus).
|
clear_if_expired(CustomStatus, erlang:system_time(millisecond)).
|
||||||
|
|
||||||
-spec clear_when_enabled(boolean(), term()) -> custom_status().
|
|
||||||
clear_when_enabled(true, CustomStatus) ->
|
|
||||||
clear_if_expired(CustomStatus, erlang:system_time(millisecond));
|
|
||||||
clear_when_enabled(false, CustomStatus) when is_map(CustomStatus) ->
|
|
||||||
CustomStatus;
|
|
||||||
clear_when_enabled(false, _CustomStatus) ->
|
|
||||||
null.
|
|
||||||
|
|
||||||
-spec clear_if_expired(term(), term()) -> custom_status().
|
-spec clear_if_expired(term(), term()) -> custom_status().
|
||||||
clear_if_expired(CustomStatus, NowMs) when is_map(CustomStatus), is_integer(NowMs) ->
|
clear_if_expired(CustomStatus, NowMs) when is_map(CustomStatus), is_integer(NowMs) ->
|
||||||
@@ -88,12 +74,6 @@ parse_rfc3339(Chars) ->
|
|||||||
|
|
||||||
-spec next_wakeup_ms(term()) -> wakeup().
|
-spec next_wakeup_ms(term()) -> wakeup().
|
||||||
next_wakeup_ms(CustomStatus) ->
|
next_wakeup_ms(CustomStatus) ->
|
||||||
next_wakeup_ms(enabled(), CustomStatus).
|
|
||||||
|
|
||||||
-spec next_wakeup_ms(boolean(), term()) -> wakeup().
|
|
||||||
next_wakeup_ms(false, _CustomStatus) ->
|
|
||||||
none;
|
|
||||||
next_wakeup_ms(true, CustomStatus) ->
|
|
||||||
wakeup_delay(remaining_ms(CustomStatus, erlang:system_time(millisecond))).
|
wakeup_delay(remaining_ms(CustomStatus, erlang:system_time(millisecond))).
|
||||||
|
|
||||||
-spec wakeup_delay(offset()) -> wakeup().
|
-spec wakeup_delay(offset()) -> wakeup().
|
||||||
@@ -125,8 +105,7 @@ repair(WindowSeconds) when
|
|||||||
#{
|
#{
|
||||||
scheduled => Total,
|
scheduled => Total,
|
||||||
considered => length(Considered),
|
considered => length(Considered),
|
||||||
window_seconds => WindowSeconds,
|
window_seconds => WindowSeconds
|
||||||
enabled => enabled()
|
|
||||||
};
|
};
|
||||||
repair(_WindowSeconds) ->
|
repair(_WindowSeconds) ->
|
||||||
#{error => invalid_window_seconds}.
|
#{error => invalid_window_seconds}.
|
||||||
@@ -334,21 +313,11 @@ wakeup_clamps_to_the_timer_limit_test() ->
|
|||||||
?assert(DelayMs > ?MAX_TIMER_MS),
|
?assert(DelayMs > ?MAX_TIMER_MS),
|
||||||
?assert(DelayMs =< ?MAX_TIMER_MS + ?WAKEUP_JITTER_MS).
|
?assert(DelayMs =< ?MAX_TIMER_MS + ?WAKEUP_JITTER_MS).
|
||||||
|
|
||||||
disabled_by_default_test() ->
|
clears_expired_and_arms_future_test() ->
|
||||||
application:unset_env(fluxer_gateway, ?ENABLED_KEY),
|
|
||||||
Status = status(?LIVE_EXPIRES_AT),
|
|
||||||
?assertEqual(false, enabled()),
|
|
||||||
?assertEqual(Status, clear_if_expired(Status)),
|
|
||||||
?assertEqual(none, next_wakeup_ms(future_status())).
|
|
||||||
|
|
||||||
enabled_clears_expired_and_arms_future_test() ->
|
|
||||||
Live = future_status(),
|
Live = future_status(),
|
||||||
with_expiry_enabled(fun() ->
|
?assertEqual(null, clear_if_expired(status(?LIVE_EXPIRES_AT))),
|
||||||
?assertEqual(true, enabled()),
|
?assertEqual(Live, clear_if_expired(Live)),
|
||||||
?assertEqual(null, clear_if_expired(status(?LIVE_EXPIRES_AT))),
|
?assertMatch({ok, _}, next_wakeup_ms(Live)).
|
||||||
?assertEqual(Live, clear_if_expired(Live)),
|
|
||||||
?assertMatch({ok, _}, next_wakeup_ms(Live))
|
|
||||||
end).
|
|
||||||
|
|
||||||
future_status() ->
|
future_status() ->
|
||||||
status(rfc3339(erlang:system_time(millisecond) + 3600000)).
|
status(rfc3339(erlang:system_time(millisecond) + 3600000)).
|
||||||
@@ -371,8 +340,7 @@ repair_reports_what_it_scheduled_test() ->
|
|||||||
Result = repair(0),
|
Result = repair(0),
|
||||||
?assert(is_integer(maps:get(scheduled, Result))),
|
?assert(is_integer(maps:get(scheduled, Result))),
|
||||||
?assert(maps:get(scheduled, Result) >= 0),
|
?assert(maps:get(scheduled, Result) >= 0),
|
||||||
?assertEqual(0, maps:get(window_seconds, Result)),
|
?assertEqual(0, maps:get(window_seconds, Result)).
|
||||||
?assertEqual(enabled(), maps:get(enabled, Result)).
|
|
||||||
|
|
||||||
schedule_reconciles_delivers_the_cast_test() ->
|
schedule_reconciles_delivers_the_cast_test() ->
|
||||||
flush_mailbox(),
|
flush_mailbox(),
|
||||||
@@ -388,18 +356,4 @@ flush_mailbox() ->
|
|||||||
after 0 -> ok
|
after 0 -> ok
|
||||||
end.
|
end.
|
||||||
|
|
||||||
with_expiry_enabled(Fun) ->
|
|
||||||
Previous = application:get_env(fluxer_gateway, ?ENABLED_KEY),
|
|
||||||
application:set_env(fluxer_gateway, ?ENABLED_KEY, true),
|
|
||||||
try
|
|
||||||
Fun()
|
|
||||||
after
|
|
||||||
restore_expiry_env(Previous)
|
|
||||||
end.
|
|
||||||
|
|
||||||
restore_expiry_env(undefined) ->
|
|
||||||
application:unset_env(fluxer_gateway, ?ENABLED_KEY);
|
|
||||||
restore_expiry_env({ok, Value}) ->
|
|
||||||
application:set_env(fluxer_gateway, ?ENABLED_KEY, Value).
|
|
||||||
|
|
||||||
-endif.
|
-endif.
|
||||||
|
|||||||
Reference in New Issue
Block a user