1: %%==============================================================================
    2: %% Copyright 2014 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: 
   17: -module(component_SUITE).
   18: -compile([export_all, nowarn_export_all]).
   19: 
   20: -include_lib("escalus/include/escalus.hrl").
   21: -include_lib("common_test/include/ct.hrl").
   22: -include_lib("exml/include/exml.hrl").
   23: -include_lib("exml/include/exml_stream.hrl").
   24: -include_lib("eunit/include/eunit.hrl").
   25: 
   26: -import(distributed_helper, [add_node_to_cluster/1,
   27:                              mim/0,
   28:                              remove_node_from_cluster/1,
   29:                              require_rpc_nodes/1,
   30:                              start_node/2,
   31:                              stop_node/2]).
   32: 
   33: -import(component_helper, [connect_component/1,
   34:                            connect_component/2,
   35:                            disconnect_component/2,
   36:                            disconnect_components/2,
   37:                            connect_component_subdomain/1,
   38:                            spec/2,
   39:                            get_components/1]).
   40: 
   41: %%--------------------------------------------------------------------
   42: %% Suite configuration
   43: %%--------------------------------------------------------------------
   44: 
   45: all() ->
   46:     [
   47:      {group, xep0114},
   48:      {group, subdomain},
   49:      {group, hidden_components},
   50:      {group, distributed}
   51:     ].
   52: 
   53: groups() ->
   54:     [{xep0114, [], xep0114_tests()},
   55:      {subdomain, [], [register_subdomain]},
   56:      {hidden_components, [], [disco_with_hidden_component]},
   57:      {distributed, [], [register_in_cluster,
   58:                         register_same_on_both
   59:                         %clear_on_node_down TODO: Breaks cover
   60:                        ]}].
   61: 
   62: suite() ->
   63:     require_rpc_nodes([mim]) ++ escalus:suite().
   64: 
   65: xep0114_tests() ->
   66:     [register_one_component,
   67:      dirty_disconnect,
   68:      register_two_components,
   69:      intercomponent_communication,
   70:      try_registering_with_wrong_password,
   71:      try_registering_component_twice,
   72:      try_registering_existing_host,
   73:      disco_components,
   74:      kick_old_component_on_conflict
   75:      ].
   76: 
   77: %%--------------------------------------------------------------------
   78: %% Init & teardown
   79: %%--------------------------------------------------------------------
   80: 
   81: init_per_suite(Config) ->
   82:     Config1 = escalus:init_per_suite(Config),
   83:     ejabberd_node_utils:init(Config1).
   84: 
   85: end_per_suite(Config) ->
   86:     escalus:end_per_suite(Config).
   87: 
   88: init_per_group(xep0114, Config) ->
   89:     Config1 = get_components(Config),
   90:     escalus:create_users(Config1, escalus:get_users([alice, bob]));
   91: init_per_group(subdomain, Config) ->
   92:     Config1 = get_components(Config),
   93:     add_domain(Config1),
   94:     escalus:create_users(Config1, escalus:get_users([alice, astrid]));
   95: init_per_group(hidden_components, Config) ->
   96:     Config1 = get_components(Config),
   97:     escalus:create_users(Config1, escalus:get_users([alice, bob]));
   98: init_per_group(distributed, Config) ->
   99:     Config1 = get_components(Config),
  100:     Config2 = add_node_to_cluster(Config1),
  101:     escalus:create_users(Config2, escalus:get_users([alice, clusterguy]));
  102: init_per_group(_GroupName, Config) ->
  103:     escalus:create_users(Config, escalus:get_users([alice, bob])).
  104: 
  105: end_per_group(subdomain, Config) ->
  106:     escalus:delete_users(Config, escalus:get_users([alice, astrid])),
  107:     restore_domain(Config);
  108: end_per_group(distributed, Config) ->
  109:     escalus:delete_users(Config, escalus:get_users([alice, clusterguy])),
  110:     remove_node_from_cluster(Config);
  111: end_per_group(_GroupName, Config) ->
  112:     escalus:delete_users(Config, escalus:get_users([alice, bob])).
  113: 
  114: init_per_testcase(CaseName, Config) ->
  115:     escalus:init_per_testcase(CaseName, Config).
  116: 
  117: end_per_testcase(CaseName, Config) ->
  118:     escalus:end_per_testcase(CaseName, Config).
  119: 
  120: 
  121: %%--------------------------------------------------------------------
  122: %% Tests
  123: %%--------------------------------------------------------------------
  124: dirty_disconnect(Config) ->
  125:     %% Given one connected component, kill the connection and reconnect
  126:     CompOpts = ?config(component1, Config),
  127:     {Component, Addr, _} = connect_component(CompOpts),
  128:     disconnect_component(Component, Addr),
  129:     {Component1, Addr, _} = connect_component(CompOpts),
  130:     disconnect_component(Component1, Addr).
  131: 
  132: register_one_component(Config) ->
  133:     MongooseMetrics = [{[global, data, xmpp, received, component], changed},
  134:                        {[global, data, xmpp, sent, component], changed}],
  135:     PreStoryData = escalus_mongooseim:pre_story([{mongoose_metrics, MongooseMetrics}]),
  136:     %% Given one connected component
  137:     CompOpts = ?config(component1, Config),
  138:     {Component, ComponentAddr, _} = connect_component(CompOpts),
  139:     escalus_mongooseim:post_story(PreStoryData),
  140:     verify_component(Config, Component, ComponentAddr),
  141:     disconnect_component(Component, ComponentAddr).
  142: 
  143: verify_component(Config, Component, ComponentAddr) ->
  144:     escalus:story(Config, [{alice, 1}], fun(Alice) ->
  145:                 %% When Alice sends a message to the component
  146:                 Msg1 = escalus_stanza:chat_to(ComponentAddr, <<"Hi!">>),
  147:                 escalus:send(Alice, Msg1),
  148:                 %% Then component receives it
  149:                 Reply1 = escalus:wait_for_stanza(Component),
  150:                 escalus:assert(is_chat_message, [<<"Hi!">>], Reply1),
  151: 
  152:                 %% When component sends a reply
  153:                 Msg2 = escalus_stanza:chat_to(Alice, <<"Oh hi!">>),
  154:                 escalus:send(Component, escalus_stanza:from(Msg2, ComponentAddr)),
  155: 
  156:                 %% Then Alice receives it
  157:                 Reply2 = escalus:wait_for_stanza(Alice),
  158:                 escalus:assert(is_chat_message, [<<"Oh hi!">>], Reply2),
  159:                 escalus:assert(is_stanza_from, [ComponentAddr], Reply2)
  160:         end).
  161: 
  162: intercomponent_communication(Config) ->
  163:     %% Given two connected components
  164:     CompOpts1 = ?config(component1, Config),
  165:     CompOpts2 = ?config(component2, Config),
  166:     {Comp1, CompAddr1, _} = connect_component(CompOpts1),
  167:     {Comp2, CompAddr2, _} = connect_component(CompOpts2),
  168:     MongooseMetrics = [{[global, data, xmpp, received, component], changed},
  169:                        {[global, data, xmpp, sent, component], changed},
  170:                        {[global, data, xmpp, received, xml_stanza_size], changed},
  171:                        {[global, data, xmpp, sent, xml_stanza_size], changed}],
  172: 
  173:     PreStoryData = escalus_mongooseim:pre_story([{mongoose_metrics, MongooseMetrics}]),
  174:     %% note that there is no c2s communication happens and 
  175:     %% data.xmpp.*.xml_stanza_size metrics are bounced
  176:     %% for the components communication
  177: 
  178:     %% When the first component sends a message the second component
  179:     Msg0 = escalus_stanza:chat_to(CompAddr2, <<"intercomponent msg">>),
  180:     escalus:send(Comp1, escalus_stanza:from(Msg0, CompAddr1)),
  181:     %% Then the second component receives it
  182:     Reply0 = escalus:wait_for_stanza(Comp2),
  183:     escalus:assert(is_chat_message, [<<"intercomponent msg">>], Reply0),
  184: 
  185:     escalus_mongooseim:post_story(PreStoryData),
  186: 
  187:     disconnect_component(Comp1, CompAddr1),
  188:     disconnect_component(Comp2, CompAddr2).
  189: 
  190: 
  191: register_two_components(Config) ->
  192:     %% Given two connected components
  193:     CompOpts1 = ?config(component1, Config),
  194:     CompOpts2 = ?config(component2, Config),
  195:     {Comp1, CompAddr1, _} = connect_component(CompOpts1),
  196:     {Comp2, CompAddr2, _} = connect_component(CompOpts2),
  197:     MongooseMetrics = [{[global, data, xmpp, received, component], changed},
  198:                        {[global, data, xmpp, sent, component], changed},
  199:                        {[global, data, xmpp, received, xml_stanza_size], changed},
  200:                        {[global, data, xmpp, sent, xml_stanza_size], changed}],
  201: 
  202:     escalus:story([{mongoose_metrics, MongooseMetrics} | Config],
  203:                   [{alice, 1}, {bob, 1}], fun(Alice, Bob) ->
  204:             %% When the first component sends a message to Alice
  205:             Msg1 = escalus_stanza:chat_to(Alice, <<"Comp1-2-Alice msg">>),
  206:             escalus:send(Comp1, escalus_stanza:from(Msg1, CompAddr1)),
  207:             %% Then she receives it
  208:             Reply1 = escalus:wait_for_stanza(Alice),
  209:             escalus:assert(is_chat_message, [<<"Comp1-2-Alice msg">>], Reply1),
  210:             escalus:assert(is_stanza_from, [CompAddr1], Reply1),
  211: 
  212:             %% When the second component sends a message to Bob
  213:             Msg2 = escalus_stanza:chat_to(Bob, <<"Comp2-2-Bob msg">>),
  214:             escalus:send(Comp2, escalus_stanza:from(Msg2, CompAddr2)),
  215:             %% Then he receives it
  216:             Reply2 = escalus:wait_for_stanza(Bob),
  217:             escalus:assert(is_chat_message, [<<"Comp2-2-Bob msg">>], Reply2),
  218:             escalus:assert(is_stanza_from, [CompAddr2], Reply2),
  219: 
  220:             %% When Bob sends a reply to the second component
  221:             Msg3 = escalus_stanza:chat_to(CompAddr2, <<"Bob-2-Comp2 msg">>),
  222:             escalus:send(Bob, Msg3),
  223:             %% Then the second component receives it
  224:             Reply3 = escalus:wait_for_stanza(Comp2),
  225:             escalus:assert(is_chat_message, [<<"Bob-2-Comp2 msg">>], Reply3),
  226: 
  227:             %% WHen Alice sends a reply to the first component
  228:             Msg4 = escalus_stanza:chat_to(CompAddr1, <<"Alice-2-Comp1 msg">>),
  229:             escalus:send(Alice, Msg4),
  230:             %% Then the first component receives it
  231:             Reply4 = escalus:wait_for_stanza(Comp1),
  232:             escalus:assert(is_chat_message, [<<"Alice-2-Comp1 msg">>], Reply4)
  233:         end),
  234: 
  235:     disconnect_component(Comp1, CompAddr1),
  236:     disconnect_component(Comp2, CompAddr2).
  237: 
  238: try_registering_with_wrong_password(Config) ->
  239:     %% Given a component with a wrong password
  240:     CompOpts1 = ?config(component1, Config),
  241:     CompOpts2 = lists:keyreplace(password, 1, CompOpts1,
  242:                                  {password, <<"wrong_one">>}),
  243:     try
  244:         %% When trying to connect it
  245:         {Comp, Addr, _} = connect_component(CompOpts2),
  246:         disconnect_component(Comp, Addr),
  247:         ct:fail("component connected successfully with wrong password")
  248:     catch {stream_error, _E} ->
  249:         %% Then it should fail to do so
  250:         ok
  251:     end.
  252: 
  253: try_registering_component_twice(Config) ->
  254:     %% Given two components with the same name
  255:     CompOpts1 = ?config(component1, Config),
  256:     {Comp1, Addr, _} = connect_component(CompOpts1),
  257: 
  258:     try
  259:         %% When trying to connect the second one
  260:         {Comp2, Addr, _} = connect_component(CompOpts1),
  261:         disconnect_component(Comp2, Addr),
  262:         ct:fail("second component connected successfully")
  263:     catch {stream_error, _} ->
  264:         %% Then it should fail to do so
  265:         ok
  266:     end,
  267: 
  268:     disconnect_component(Comp1, Addr).
  269: 
  270: try_registering_existing_host(Config) ->
  271:     %% Given a external vjud component
  272:     Component = ?config(vjud_component, Config),
  273: 
  274:     try
  275:         %% When trying to connect it to the server
  276:         {Comp, Addr, _} = connect_component(Component),
  277:         disconnect_component(Comp, Addr),
  278:         ct:fail("vjud component connected successfully")
  279:     catch {stream_error, _} ->
  280:         %% Then it should fail since vjud service already exists on the server
  281:         ok
  282:     end.
  283: 
  284: %% When conflict_behaviour is kick_old, then:
  285: %% - stop old connections by sending stream:error with reason "conflict"
  286: kick_old_component_on_conflict(Config) ->
  287:     CompOpts1 = spec(kicking_component, Config),
  288:     {Comp1, Addr, _} = connect_component(CompOpts1),
  289: 
  290:     %% When trying to connect the second one
  291:     {Comp2, Addr, _} = connect_component(CompOpts1),
  292: 
  293:     %% First connection is disconnected
  294:     Stanza = escalus:wait_for_stanza(Comp1),
  295:     escalus:assert(is_stream_error, [<<"conflict">>, <<"">>], Stanza),
  296: 
  297:     %% New connection is usable
  298:     verify_component(Config, Comp2, Addr),
  299: 
  300:     disconnect_component(Comp2, Addr).
  301: 
  302: disco_components(Config) ->
  303:     %% Given two connected components
  304:     CompOpts1 = ?config(component1, Config),
  305:     CompOpts2 = ?config(component2, Config),
  306:     {Comp1, Addr1, _} = connect_component(CompOpts1),
  307:     {Comp2, Addr2, _} = connect_component(CompOpts2),
  308: 
  309:     escalus:story(Config, [{alice, 1}], fun(Alice) ->
  310:                 %% When server asked for the disco features
  311:                 Server = escalus_client:server(Alice),
  312:                 Disco = escalus_stanza:service_discovery(Server),
  313:                 escalus:send(Alice, Disco),
  314: 
  315:                 %% Then it contains hosts of 2 components
  316:                 DiscoReply = escalus:wait_for_stanza(Alice),
  317:                 escalus:assert(has_service, [Addr1], DiscoReply),
  318:                 escalus:assert(has_service, [Addr2], DiscoReply)
  319:         end),
  320: 
  321:     disconnect_component(Comp1, Addr1),
  322:     disconnect_component(Comp2, Addr2).
  323: 
  324: %% Verifies that a component connected to the "hidden components" endpoint
  325: %% is not discoverable.
  326: %% Assumes mod_disco with `{users_can_see_hidden_services, false}` option
  327: disco_with_hidden_component(Config) ->
  328:     %% Given two connected components
  329:     CompOpts1 = ?config(component1, Config),
  330:     HCompOpts = spec(hidden_component, Config),
  331:     {Comp1, Addr1, _} = connect_component(CompOpts1),
  332:     {HComp, HAddr, _} = connect_component(HCompOpts),
  333: 
  334:     escalus:story(Config, [{alice, 1}], fun(Alice) ->
  335:                 %% When server asked for the disco features
  336:                 Server = escalus_client:server(Alice),
  337:                 Disco = escalus_stanza:service_discovery(Server),
  338:                 escalus:send(Alice, Disco),
  339: 
  340:                 %% Then it contains hosts of component1 and hidden_component is missing
  341:                 DiscoReply = escalus:wait_for_stanza(Alice),
  342:                 escalus:assert(has_service, [Addr1], DiscoReply),
  343:                 escalus:assert(fun(Stanza) ->
  344:                                        not escalus_pred:has_service(HAddr, Stanza)
  345:                                end, DiscoReply)
  346:         end),
  347: 
  348:     disconnect_component(Comp1, Addr1),
  349:     disconnect_component(HComp, HAddr).
  350: 
  351: register_subdomain(Config) ->
  352:     %% Given one connected component
  353:     CompOpts1 = ?config(component1, Config),
  354:     {Comp, Addr, Name} = connect_component_subdomain(CompOpts1),
  355: 
  356:     escalus:story(Config, [{alice, 1}, {astrid, 1}], fun(Alice, Astrid) ->
  357:                 %% When Alice asks for service discovery on the server
  358:                 Server1 = escalus_client:server(Alice),
  359:                 Disco1 = escalus_stanza:service_discovery(Server1),
  360:                 escalus:send(Alice, Disco1),
  361: 
  362:                 %% Then it contains the registered route
  363:                 DiscoReply1 = escalus:wait_for_stanza(Alice),
  364:                 ComponentHost1 = <<Name/binary, ".", Server1/binary>>,
  365:                 escalus:assert(has_service, [ComponentHost1], DiscoReply1),
  366: 
  367:                 %% When Astrid ask for service discovery on her server
  368:                 Server2 = escalus_client:server(Astrid),
  369:                 false = (Server1 =:= Server2),
  370:                 Disco2 = escalus_stanza:service_discovery(Server2),
  371:                 escalus:send(Astrid, Disco2),
  372: 
  373:                 %% Then it also contains the registered route
  374:                 DiscoReply2 = escalus:wait_for_stanza(Astrid),
  375:                 ComponentHost2 = <<Name/binary, ".", Server2/binary>>,
  376:                 escalus:assert(has_service, [ComponentHost2], DiscoReply2)
  377: 
  378:         end),
  379: 
  380:     disconnect_component(Comp, Addr).
  381: 
  382: 
  383: register_in_cluster(Config) ->
  384:     %% Given one component connected to the cluster
  385:     CompOpts1 = ?config(component1, Config),
  386:     Component1 = connect_component(CompOpts1),
  387:     {Comp1, Addr1, _} = Component1,
  388:     CompOpts2 = ?config(component2, Config),
  389:     Component2 = connect_component(CompOpts2),
  390:     {Comp2, Addr2, _} = Component2,
  391:     CompOpts_on_2 = spec(component_on_2, Config),
  392:     Component_on_2 = connect_component(CompOpts_on_2),
  393:     {Comp_on_2, Addr_on_2, _} = Component_on_2,
  394: 
  395:     escalus:story(Config, [{alice, 1}, {clusterguy, 1}], fun(Alice, ClusterGuy) ->
  396:                 do_chat_with_component(Alice, ClusterGuy, Component1),
  397:                 do_chat_with_component(Alice, ClusterGuy, Component2),
  398:                 do_chat_with_component(Alice, ClusterGuy, Component_on_2)
  399:         end),
  400: 
  401:     disconnect_component(Comp1, Addr1),
  402:     disconnect_component(Comp2, Addr2),
  403:     disconnect_component(Comp_on_2, Addr_on_2),
  404:     ok.
  405: 
  406: clear_on_node_down(Config) ->
  407:     CompOpts = ?config(component1, Config),
  408:     ?assertMatch({_, _, _}, connect_component(CompOpts)),
  409:     ?assertThrow({stream_error, _}, connect_component(CompOpts)),
  410: 
  411:     stop_node(mim(), Config),
  412:     start_node(mim(), Config),
  413: 
  414:     {Comp, Addr, _} = connect_component(CompOpts),
  415:     disconnect_component(Comp, Addr).
  416: 
  417: do_chat_with_component(Alice, ClusterGuy, Component1) ->
  418:     {Comp, Addr, Name} = Component1,
  419: 
  420:     %% When Alice sends a message to the component
  421:     Msg1 = escalus_stanza:chat_to(Addr, <<"Hi!">>),
  422:     escalus:send(Alice, Msg1),
  423:     %% Then component receives it
  424:     Reply1 = escalus:wait_for_stanza(Comp),
  425:     escalus:assert(is_chat_message, [<<"Hi!">>], Reply1),
  426: 
  427:     %% When components sends a reply
  428:     Msg2 = escalus_stanza:chat_to(Alice, <<"Oh hi!">>),
  429:     escalus:send(Comp, escalus_stanza:from(Msg2, Addr)),
  430: 
  431:     %% Then Alice receives it
  432:     Reply2 = escalus:wait_for_stanza(Alice),
  433:     escalus:assert(is_chat_message, [<<"Oh hi!">>], Reply2),
  434:     escalus:assert(is_stanza_from, [Addr], Reply2),
  435: 
  436:     %% When ClusterGuy (connected to the other node than component)
  437:     %% sends a message
  438:     Msg3 = escalus_stanza:chat_to(Addr, <<"Hello!">>),
  439:     escalus:send(ClusterGuy, Msg3),
  440:     %% Then component receives it
  441:     Reply3 = escalus:wait_for_stanza(Comp),
  442:     escalus:assert(is_chat_message, [<<"Hello!">>], Reply3),
  443: 
  444:     %% When components sends a reply
  445:     Msg4 = escalus_stanza:chat_to(ClusterGuy, <<"Hola!">>),
  446:     escalus:send(Comp, escalus_stanza:from(Msg4, Addr)),
  447: 
  448:     %% Then ClusterGuy receives it
  449:     Reply4 = escalus:wait_for_stanza(ClusterGuy),
  450:     escalus:assert(is_chat_message, [<<"Hola!">>], Reply4),
  451:     escalus:assert(is_stanza_from, [Addr], Reply4),
  452: 
  453:     %% When Alice asks for the disco features
  454:     Server1 = escalus_client:server(Alice),
  455:     Disco1 = escalus_stanza:service_discovery(Server1),
  456:     escalus:send(Alice, Disco1),
  457: 
  458:     %% Then it contains host of the service
  459:     DiscoReply1 = escalus:wait_for_stanza(Alice),
  460:     escalus:assert(has_service, [Addr], DiscoReply1),
  461: 
  462:     %% When ClusterGuy asks for the disco features on her server
  463:     Server2 = escalus_client:server(ClusterGuy),
  464:     Disco2 = escalus_stanza:service_discovery(Server2),
  465:     escalus:send(ClusterGuy, Disco2),
  466: 
  467:     %% Then it also contains the service (with the other address though)
  468:     DiscoReply2 = escalus:wait_for_stanza(ClusterGuy),
  469:     DistributedAddr = <<Name/binary, ".", Server2/binary>>,
  470:     escalus:assert(has_service, [DistributedAddr], DiscoReply2).
  471: 
  472: 
  473: register_same_on_both(Config) ->
  474:     %% Given two components with the same name
  475:     %% but not on the same host
  476:     %% we should be able to register
  477:     %% and we get two components having the same name and address
  478:     CompOpts2 = ?config(component2, Config),
  479:     Component2 = connect_component(CompOpts2),
  480:     {Comp2, Addr, Name} = Component2,
  481:     CompOpts_d = spec(component_duplicate, Config),
  482:     Component_d = connect_component(CompOpts_d),
  483:     {Comp_d, Addr, Name} = Component_d,
  484: 
  485:     escalus:story(Config, [{alice, 1}, {clusterguy, 1}], fun(Alice, ClusterGuy) ->
  486:         %% When Alice sends a message to the component
  487:         Msg1 = escalus_stanza:chat_to(Addr, <<"Hi!">>),
  488:         escalus:send(Alice, Msg1),
  489:         %% Then component receives it (on the same node)
  490:         Reply1 = escalus:wait_for_stanza(Comp2),
  491:         escalus:assert(is_chat_message, [<<"Hi!">>], Reply1),
  492: 
  493:         %% When components sends a reply
  494:         Msg2 = escalus_stanza:chat_to(Alice, <<"Oh hi!">>),
  495:         escalus:send(Comp2, escalus_stanza:from(Msg2, Addr)),
  496: 
  497:         %% Then Alice receives it
  498:         Reply2 = escalus:wait_for_stanza(Alice),
  499:         escalus:assert(is_chat_message, [<<"Oh hi!">>], Reply2),
  500:         escalus:assert(is_stanza_from, [Addr], Reply2),
  501: 
  502:         %% When ClusterGuy (connected to the other node than component)
  503:         %% sends a message
  504:         Msg3 = escalus_stanza:chat_to(Addr, <<"Hello!">>),
  505:         escalus:send(ClusterGuy, Msg3),
  506:         %% Then component on his node receives it
  507:         Reply3 = escalus:wait_for_stanza(Comp_d),
  508:         escalus:assert(is_chat_message, [<<"Hello!">>], Reply3),
  509: 
  510:         %% When components sends a reply
  511:         Msg4 = escalus_stanza:chat_to(ClusterGuy, <<"Hola!">>),
  512:         escalus:send(Comp_d, escalus_stanza:from(Msg4, Addr)),
  513: 
  514:         %% Then ClusterGuy receives it
  515:         Reply4 = escalus:wait_for_stanza(ClusterGuy),
  516:         escalus:assert(is_chat_message, [<<"Hola!">>], Reply4),
  517:         escalus:assert(is_stanza_from, [Addr], Reply4),
  518: 
  519:         %% When Alice asks for the disco features
  520:         Server1 = escalus_client:server(Alice),
  521:         Disco1 = escalus_stanza:service_discovery(Server1),
  522:         escalus:send(Alice, Disco1),
  523: 
  524:         %% Then it contains host of the service
  525:         DiscoReply1 = escalus:wait_for_stanza(Alice),
  526:         escalus:assert(has_service, [Addr], DiscoReply1),
  527: 
  528:         %% When ClusterGuy asks for the disco features on her server
  529:         Server2 = escalus_client:server(ClusterGuy),
  530:         Disco2 = escalus_stanza:service_discovery(Server2),
  531:         escalus:send(ClusterGuy, Disco2),
  532: 
  533:         %% Then it also contains the same service
  534:         DiscoReply2 = escalus:wait_for_stanza(ClusterGuy),
  535:         escalus:assert(has_service, [Addr], DiscoReply2)
  536: 
  537:     end),
  538:     disconnect_components([Comp2, Comp_d], Addr),
  539:     ok.
  540: 
  541: %%--------------------------------------------------------------------
  542: %% Helpers
  543: %%--------------------------------------------------------------------
  544: add_domain(Config) ->
  545:     Hosts = {hosts, "\"localhost\", \"sogndal\""},
  546:     ejabberd_node_utils:backup_config_file(Config),
  547:     ejabberd_node_utils:modify_config_file([Hosts], Config),
  548:     ejabberd_node_utils:restart_application(mongooseim),
  549:     ok.
  550: 
  551: restore_domain(Config) ->
  552:     ejabberd_node_utils:restore_config_file(Config),
  553:     ejabberd_node_utils:restart_application(mongooseim),
  554:     Config.
  555: 
  556: 
  557: %%--------------------------------------------------------------------
  558: %% Stanzas
  559: %%--------------------------------------------------------------------
  560: 
  561: 
  562: cluster_users() ->
  563:     AllUsers = ct:get_config(escalus_users),
  564:     [proplists:lookup(alice, AllUsers), proplists:lookup(clusterguy, AllUsers)].