mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-18 05:25:40 +09:00
feat(threads): gateway, messages and push thread support (#3250)
This commit is contained in:
1 parent
4e730832c7
commit
5f4295e399
108 files changed
+15857
-176
No files matched your search
@@ -0,0 +1,194 @@
|
||||
%% SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
-module(guild_thread_push_tests).
|
||||
|
||||
-include_lib("eunit/include/eunit.hrl").
|
||||
|
||||
-define(G, 1).
|
||||
-define(A, 10).
|
||||
-define(B, 11).
|
||||
-define(C, 12).
|
||||
-define(OUT, 13).
|
||||
-define(AUTHOR, 14).
|
||||
-define(TEXT, 100).
|
||||
-define(THREAD, 500).
|
||||
-define(VIEW, 1024).
|
||||
-define(SEND, 2048).
|
||||
-define(CONFIG_KEY, channel_threads_config).
|
||||
|
||||
with_config(Fun) ->
|
||||
Previous = persistent_term:get(?CONFIG_KEY, undefined),
|
||||
Config = (channel_threads_config:default_config())#{
|
||||
enabled => true,
|
||||
included_users =>
|
||||
#{integer_to_binary(Id) => true || Id <- [?A, ?B, ?C, ?AUTHOR]}
|
||||
},
|
||||
persistent_term:put(?CONFIG_KEY, Config),
|
||||
try
|
||||
Fun()
|
||||
after
|
||||
case Previous of
|
||||
undefined -> persistent_term:erase(?CONFIG_KEY);
|
||||
_ -> persistent_term:put(?CONFIG_KEY, Previous)
|
||||
end
|
||||
end.
|
||||
|
||||
raw_data() ->
|
||||
Gid = integer_to_binary(?G),
|
||||
Member = fun(Id) ->
|
||||
#{
|
||||
<<"user">> => #{<<"id">> => integer_to_binary(Id)},
|
||||
<<"roles">> => [],
|
||||
<<"joined_at">> => <<"2026-01-01T00:00:00Z">>
|
||||
}
|
||||
end,
|
||||
#{
|
||||
<<"guild">> => #{<<"id">> => Gid, <<"owner_id">> => <<"999">>, <<"features">> => []},
|
||||
<<"roles">> => [
|
||||
#{
|
||||
<<"id">> => Gid,
|
||||
<<"permissions">> => integer_to_binary(?VIEW bor ?SEND),
|
||||
<<"position">> => 0
|
||||
}
|
||||
],
|
||||
<<"channels">> => [
|
||||
#{<<"id">> => <<"100">>, <<"type">> => 0, <<"permission_overwrites">> => []}
|
||||
],
|
||||
<<"members">> => [Member(Id) || Id <- [?A, ?B, ?C, ?OUT, ?AUTHOR]],
|
||||
<<"emojis">> => [],
|
||||
<<"stickers">> => []
|
||||
}.
|
||||
|
||||
thread_member(UserId) ->
|
||||
#{
|
||||
<<"id">> => integer_to_binary(?THREAD),
|
||||
<<"user_id">> => integer_to_binary(UserId),
|
||||
<<"join_timestamp">> => <<"2026-01-01T00:00:00.000Z">>,
|
||||
<<"flags">> => 1
|
||||
}.
|
||||
|
||||
state(Active) ->
|
||||
Payload = guild_thread_load:load_payload(#{
|
||||
<<"threads">> => [
|
||||
#{
|
||||
<<"id">> => integer_to_binary(?THREAD),
|
||||
<<"guild_id">> => integer_to_binary(?G),
|
||||
<<"parent_id">> => integer_to_binary(?TEXT),
|
||||
<<"type">> => 11,
|
||||
<<"name">> => <<"ideas">>,
|
||||
<<"thread_metadata">> => #{<<"archived">> => false, <<"locked">> => false}
|
||||
}
|
||||
],
|
||||
<<"thread_members">> => [thread_member(?A), thread_member(?OUT)]
|
||||
}),
|
||||
Base = #{
|
||||
id => ?G,
|
||||
member_count => 5,
|
||||
data => guild_data_index:normalize_map(raw_data()),
|
||||
sessions => #{
|
||||
<<"b">> => #{session_id => <<"b">>, user_id => ?B, pid => self()},
|
||||
<<"a">> => #{session_id => <<"a">>, user_id => ?A, pid => self()}
|
||||
}
|
||||
},
|
||||
Gate =
|
||||
case Active of
|
||||
true ->
|
||||
#{
|
||||
thread_gate => #{active => true, version => 1},
|
||||
thread_tainted => true,
|
||||
thread_load => {ok, Payload}
|
||||
};
|
||||
false ->
|
||||
#{}
|
||||
end,
|
||||
guild_thread_load:install(Gate, Base).
|
||||
|
||||
with_state(Active, Fun) ->
|
||||
with_config(fun() ->
|
||||
State = state(Active),
|
||||
try
|
||||
Fun(State)
|
||||
after
|
||||
guild_thread_store:destroy(guild_thread_gate:store(State))
|
||||
end
|
||||
end).
|
||||
|
||||
mention_request(ChannelId, Overrides) ->
|
||||
maps:merge(
|
||||
#{
|
||||
channel_id => ChannelId,
|
||||
author_id => ?AUTHOR,
|
||||
mention_everyone => false,
|
||||
mention_here => false,
|
||||
role_ids => [],
|
||||
user_ids => []
|
||||
},
|
||||
Overrides
|
||||
).
|
||||
|
||||
everyone(Request, State) ->
|
||||
{reply, #{everyone_user_ids := Ids}, _} =
|
||||
guild_members_mutation:resolve_mention_sources(Request, State),
|
||||
lists:sort(Ids).
|
||||
|
||||
everyone_in_a_thread_reaches_recipient_active_members_only_test() ->
|
||||
with_state(true, fun(State) ->
|
||||
Request = mention_request(?THREAD, #{mention_everyone => true}),
|
||||
?assertEqual([?A], everyone(Request, State)),
|
||||
?assertEqual(
|
||||
[?A, ?B, ?C, ?OUT],
|
||||
everyone(mention_request(?TEXT, #{mention_everyone => true}), State)
|
||||
)
|
||||
end).
|
||||
|
||||
here_in_a_thread_needs_membership_and_a_connection_test() ->
|
||||
with_state(true, fun(State) ->
|
||||
Request = mention_request(?THREAD, #{mention_here => true}),
|
||||
?assertEqual([?A], everyone(Request, State)),
|
||||
?assertEqual([?A, ?B], everyone(mention_request(?TEXT, #{mention_here => true}), State))
|
||||
end).
|
||||
|
||||
direct_mentions_in_a_thread_skip_inactive_recipients_test() ->
|
||||
with_state(true, fun(State) ->
|
||||
{reply, #{direct_user_ids := Ids}, _} =
|
||||
guild_members_mutation:resolve_mention_sources(
|
||||
mention_request(?THREAD, #{user_ids => [?B, ?OUT]}), State
|
||||
),
|
||||
?assertEqual([?B], lists:sort(Ids))
|
||||
end).
|
||||
|
||||
paged_everyone_in_a_thread_lists_members_only_test() ->
|
||||
with_state(true, fun(State) ->
|
||||
Request = (mention_request(?THREAD, #{mention_everyone => true}))#{
|
||||
limit => 100, cursor => undefined
|
||||
},
|
||||
{reply, #{mentions := Entries}, _} =
|
||||
guild_members_mutation:resolve_mention_sources_page(Request, State),
|
||||
?assertEqual([?A], lists:sort([entry_user_id(E) || E <- Entries]))
|
||||
end).
|
||||
|
||||
inactive_guild_mentions_are_unchanged_test() ->
|
||||
with_state(false, fun(State) ->
|
||||
?assertEqual(undefined, guild_thread_push:mention_universe(?THREAD, State)),
|
||||
?assertEqual(
|
||||
[?A, ?B, ?C, ?OUT],
|
||||
everyone(mention_request(?TEXT, #{mention_everyone => true}), State)
|
||||
)
|
||||
end).
|
||||
|
||||
passive_threads_follow_membership_test() ->
|
||||
with_state(true, fun(State) ->
|
||||
?assertMatch(
|
||||
[#{<<"name">> := <<"ideas">>}],
|
||||
guild_thread_push:passive_threads(#{thread_viewer => true}, ?A, State)
|
||||
),
|
||||
?assertEqual(
|
||||
[], guild_thread_push:passive_threads(#{thread_viewer => true}, ?B, State)
|
||||
),
|
||||
?assertEqual([], guild_thread_push:passive_threads(#{}, ?A, State))
|
||||
end).
|
||||
|
||||
entry_user_id(#{user_id := UserId}) -> UserId;
|
||||
entry_user_id(#{<<"user_id">> := UserId}) -> snowflake_id:parse_optional(UserId);
|
||||
entry_user_id(UserId) when is_integer(UserId) -> UserId;
|
||||
entry_user_id(Other) -> Other.
|
||||
Reference in new issue
Block a user