Skip to content

Commit

Permalink
Merge pull request #3118 from esl/multi-tenancy-iq_handlers
Browse files Browse the repository at this point in the history
Multi tenancy iq handlers
  • Loading branch information
arcusfelis committed May 19, 2021
2 parents 0dc6e59 + ca2fcc6 commit eae2da0
Show file tree
Hide file tree
Showing 31 changed files with 1,304 additions and 471 deletions.
1 change: 1 addition & 0 deletions big_tests/default.spec
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
{suites, "tests", domain_isolation_SUITE}.
{suites, "tests", domain_removal_SUITE}.
{suites, "tests", mam_send_message_SUITE}.
{suites, "tests", dynamic_domains_SUITE}.

{config, ["test.config"]}.
{logdir, "ct_report"}.
Expand Down
4 changes: 2 additions & 2 deletions big_tests/test.config
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@
{starttls, required},
{tls_module, fast_tls}
]},
{alice3, [ %% used in dynamic_domains_pm_SUITE
{alice3, [ %% used in dynamic_domains_SUITE
{username, <<"alice">>},
{server, <<"example.com">>},
{host, <<"localhost">>},
{password, <<"makota2">>}]},
{bob3, [
{bob3, [ %% used in dynamic_domains_SUITE
{username, <<"bob">>},
{server, <<"example.org">>},
{host, <<"localhost">>},
Expand Down
216 changes: 216 additions & 0 deletions big_tests/tests/dynamic_domains_SUITE.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
-module(dynamic_domains_SUITE).

-include_lib("exml/include/exml.hrl").

%% API
-compile(export_all).
-import(distributed_helper, [mim/0, mim2/0, rpc/4,
require_rpc_nodes/1,
subhost_pattern/1]).

-define(TEST_NODES, [mim() | ?CLUSTER_NODES]).
-define(CLUSTER_NODES, [mim2()]).
-define(DOMAINS, [<<"example.com">>, <<"example.org">>]).
-define(HOST_TYPE, <<"test type">>). %% preconfigured in the toml file

suite() ->
require_rpc_nodes([mim, mim2]).

all() ->
[can_authenticate,
pm_messages,
disconnected_on_domain_disabling,
auth_domain_removal_is_triggered_on_hook,
{group, with_mod_dynamic_domains_test}].

groups() ->
[{with_mod_dynamic_domains_test, [], [packet_handling_for_subdomain,
iq_handling_for_subdomain,
iq_handling_for_domain]}].

init_per_suite(Config0) ->
Config = cluster_nodes(?CLUSTER_NODES, Config0),
insert_domains(?TEST_NODES, ?DOMAINS),
escalus:init_per_suite(Config).

end_per_suite(Config0) ->
Config = escalus:end_per_suite(Config0),
remove_domains(?TEST_NODES, ?DOMAINS),
uncluster_nodes(?CLUSTER_NODES, Config).

init_per_group(with_mod_dynamic_domains_test, Config) ->
MockedModules = [mod_dynamic_domains_test, ejabberd_router],
[ok = rpc(mim(), meck, new, [Module, [passthrough, no_link]])
|| Module <- MockedModules],
dynamic_modules:start(?HOST_TYPE, mod_dynamic_domains_test,
[{host1, subhost_pattern("subdomain1.@HOST@")},
{host2, subhost_pattern("subdomain2.@HOST@")},
{namespace, <<"dummy.namespace">>}]),
[{reset_meck, MockedModules} | Config];
init_per_group(_, Config) ->
Config.

end_per_group(with_mod_dynamic_domains_test, Config) ->
dynamic_modules:stop(?HOST_TYPE, mod_dynamic_domains_test),
rpc(mim(), meck, unload, []),
Config;
end_per_group(_, Config) ->
Config.

init_per_testcase(CN, Config) ->
Modules = proplists:get_value(reset_meck, Config, []),
[rpc(mim(), meck, reset, [M]) || M <- Modules],
escalus:init_per_testcase(CN, Config).

end_per_testcase(CN, Config) ->
escalus:end_per_testcase(CN, Config).

can_authenticate(Config) ->
UserSpecA = escalus_users:get_userspec(Config, alice3),
{ok, ClientA, _} = escalus_connection:start(UserSpecA),
UserSpecB = escalus_users:get_userspec(Config, bob3),
{ok, ClientB, _} = escalus_connection:start(UserSpecB),
escalus_connection:stop(ClientA),
escalus_connection:stop(ClientB).

pm_messages(Config) ->
StoryFn =
fun(Alice, Bob) ->
escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)),
escalus:assert(is_chat_message, [<<"OH, HAI!">>], escalus:wait_for_stanza(Bob)),
escalus:send(Bob, escalus_stanza:chat_to(Alice, <<"Hello there!">>)),
escalus:assert(is_chat_message, [<<"Hello there!">>], escalus:wait_for_stanza(Alice))
end,
escalus:story(Config, [{alice3, 1}, {bob3, 1}], StoryFn).

disconnected_on_domain_disabling(Config) ->
StoryFn =
fun(Alice, Bob) ->
remove_domains(?TEST_NODES, ?DOMAINS),
escalus_connection:wait_for_close(Alice, timer:seconds(5)),
escalus_connection:wait_for_close(Bob, timer:seconds(5)),
insert_domains(?TEST_NODES, ?DOMAINS)
end,
escalus:story(Config, [{alice3, 1}, {bob3, 1}], StoryFn).

auth_domain_removal_is_triggered_on_hook(_Config) ->
ok = rpc(mim(), meck, new, [ejabberd_auth_dummy, [passthrough, no_link]]),
Params = [?HOST_TYPE, <<"dummy.domain.name">>],
rpc(mim(), mongoose_hooks, remove_domain, Params),
1 = rpc(mim(), meck, num_calls, [ejabberd_auth_dummy, remove_domain, Params]),
rpc(mim(), meck, unload, [ejabberd_auth_dummy]).

packet_handling_for_subdomain(Config) ->
StoryFn =
fun(Alice) ->
NewDomain = <<"example.test">>,
insert_domains(?TEST_NODES, [NewDomain]),
Domains = [NewDomain | ?DOMAINS],
Subdomains = [<<"subdomain1.", Domain/binary>> || Domain <- Domains],
[NewSubdomain | _] = Subdomains,
[escalus:send(Alice, escalus_stanza:chat_to(Subdomain, <<"OH, HAI!">>))
|| Subdomain <- Subdomains],
rpc(mim(), meck, wait, [3, mod_dynamic_domains_test, process_packet, 5, 500]),
rpc(mim(), meck, reset, [mod_dynamic_domains_test]),

QueryEl = escalus_stanza:query_el(<<"dummy.namespace">>, []),
[begin
IQ = escalus_stanza:iq(Subdomain, <<"get">>, [QueryEl]),
escalus:send(Alice, IQ)
end || Subdomain <- Subdomains],
%% check that all the IQs to any of Subdomains1 landed at process_packet/5
%% and no stanzas received in response
rpc(mim(), meck, wait, [3, mod_dynamic_domains_test, process_packet, 5, 500]),
0 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_iq, 5]),
[] = escalus:wait_for_stanzas(Alice, 5, 500),
rpc(mim(), meck, reset, [mod_dynamic_domains_test]),

%% check that subdomain is not served after the parent domain removal
remove_domains(?TEST_NODES, [NewDomain]),
rpc(mim(), meck, wait, [ejabberd_router, unregister_route, [NewSubdomain], 500]),
IQ = escalus_stanza:iq(NewSubdomain, <<"get">>, [QueryEl]),
escalus:send(Alice, IQ),
Stanza = escalus:wait_for_stanza(Alice, 10000),
escalus:assert(is_error, [<<"cancel">>, <<"remote-server-not-found">>], Stanza),
0 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_packet, 5])
end,
escalus:story(Config, [{alice3, 1}], StoryFn).

iq_handling_for_domain(Config) ->
StoryFn =
fun(Alice) ->
NewDomain = <<"example.test">>,
insert_domains(?TEST_NODES, [NewDomain]),
Domains = [NewDomain | ?DOMAINS],
QueryEl = escalus_stanza:query_el(<<"dummy.namespace">>, []),
[begin
IQ = escalus_stanza:iq(Domain, <<"get">>, [QueryEl]),
escalus:send_iq_and_wait_for_result(Alice, IQ)
end || Domain <- Domains],
3 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_iq, 5]),
0 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_packet, 5]),
%% check that process_iq/5 is called from one and the same worker process
History = rpc(mim(), meck, history, [mod_dynamic_domains_test]),
rpc(mim(), meck, reset, [mod_dynamic_domains_test]),
Pids = [Pid || {Pid, {_, process_iq, _}, _} <- History],
[_] = (lists:usort(Pids)),

%% check that domain is not served removal
remove_domains(?TEST_NODES, [NewDomain]),
rpc(mim(), meck, wait, [ejabberd_router, unregister_route, [NewDomain], 500]),
IQ = escalus_stanza:iq(NewDomain, <<"get">>, [QueryEl]),
escalus:send(Alice, IQ),
Stanza = escalus:wait_for_stanza(Alice, 10000),
escalus:assert(is_error, [<<"cancel">>, <<"remote-server-not-found">>], Stanza),
0 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_iq, 5])
end,
escalus:story(Config, [{alice3, 1}], StoryFn).

iq_handling_for_subdomain(Config) ->
StoryFn =
fun(Alice) ->
NewDomain = <<"example.test">>,
insert_domains(?TEST_NODES, [NewDomain]),
Domains = [NewDomain | ?DOMAINS],
Subdomains = [<<"subdomain2.", Domain/binary>> || Domain <- Domains],
[NewSubdomain | _] = Subdomains,
QueryEl = escalus_stanza:query_el(<<"dummy.namespace">>, []),
[begin
IQ = escalus_stanza:iq(Subdomain, <<"get">>, [QueryEl]),
escalus:send_iq_and_wait_for_result(Alice, IQ)
end || Subdomain <- Subdomains],
3 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_iq, 5]),
0 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_packet, 5]),
%% check that process_iq/5 is called from one and the same worker process
History = rpc(mim(), meck, history, [mod_dynamic_domains_test]),
rpc(mim(), meck, reset, [mod_dynamic_domains_test]),
Pids = [Pid || {Pid, {_, process_iq, _}, _} <- History],
[_] = (lists:usort(Pids)),

%% check that subdomain is not served after the parent domain removal
remove_domains(?TEST_NODES, [NewDomain]),
rpc(mim(), meck, wait, [ejabberd_router, unregister_route, [NewSubdomain], 500]),
IQ = escalus_stanza:iq(NewSubdomain, <<"get">>, [QueryEl]),
escalus:send(Alice, IQ),
Stanza = escalus:wait_for_stanza(Alice, 10000),
escalus:assert(is_error, [<<"cancel">>, <<"remote-server-not-found">>], Stanza),
0 = rpc(mim(), meck, num_calls, [mod_dynamic_domains_test, process_iq, 5])
end,
escalus:story(Config, [{alice3, 1}], StoryFn).

%% helper functions
insert_domains(Nodes, Domains) ->
[domain_helper:insert_domain(Node, Domain) || Node <- Nodes, Domain <- Domains].

remove_domains(Nodes, Domains) ->
[domain_helper:delete_domain(Node, Domain) || Node <- Nodes, Domain <- Domains].

cluster_nodes([], Config) -> Config;
cluster_nodes([Node | T], Config) ->
NewConfig = distributed_helper:add_node_to_cluster(Node, Config),
cluster_nodes(T, NewConfig).

uncluster_nodes([], Config) -> Config;
uncluster_nodes([Node | T], Config) ->
NewConfig = distributed_helper:remove_node_from_cluster(Node, Config),
cluster_nodes(T, NewConfig).
87 changes: 0 additions & 87 deletions big_tests/tests/dynamic_domains_pm_SUITE.erl

This file was deleted.

4 changes: 2 additions & 2 deletions big_tests/tests/push_SUITE.erl
Original file line number Diff line number Diff line change
Expand Up @@ -690,11 +690,11 @@ start_route_listener(Config) ->
State = #{ pid => self(),
pub_options_ns => push_helper:ns_pubsub_pub_options(),
push_form_ns => push_helper:push_form_type() },
Handler = rpc(mongoose_packet_handler, new, [?MODULE, State]),
Handler = rpc(mongoose_packet_handler, new, [?MODULE, #{state => State}]),
Domain = pubsub_domain(Config),
rpc(ejabberd_router, register_route, [Domain, Handler]).

process_packet(_Acc, _From, To, El, State) ->
process_packet(_Acc, _From, To, El, #{state := State}) ->
#{ pid := TestCasePid, pub_options_ns := PubOptionsNS, push_form_ns := PushFormNS } = State,
PublishXML = exml_query:path(El, [{element, <<"pubsub">>},
{element, <<"publish-options">>},
Expand Down
Loading

0 comments on commit eae2da0

Please sign in to comment.