1: %%============================================================================== 2: %% Copyright 2015 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: -module(mod_ping_SUITE). 17: -compile([export_all, nowarn_export_all]). 18: 19: -include_lib("escalus/include/escalus.hrl"). 20: -include_lib("escalus/include/escalus_xmlns.hrl"). 21: -include_lib("exml/include/exml.hrl"). 22: -include_lib("common_test/include/ct.hrl"). 23: 24: -import(domain_helper, [domain/0]). 25: 26: %%-------------------------------------------------------------------- 27: %% Suite configuration 28: %%-------------------------------------------------------------------- 29: all() -> 30: [ 31: {group, client_ping}, 32: {group, server_ping}, 33: {group, server_ping_kill} 34: ]. 35: 36: groups() -> 37: % Don't make these parallel! Metrics tests will most probably fail 38: % and injected hook will most probably won't work as expected. 39: [ 40: {client_ping, [], [disco, ping]}, 41: {server_ping, [], all_tests()}, 42: {server_ping_kill, [], all_tests()} 43: ]. 44: 45: client_ping_test_cases() -> 46: [ 47: ping, 48: wrong_ping 49: ]. 50: 51: all_tests() -> 52: [ 53: disco, 54: ping, 55: wrong_ping, 56: active, 57: active_keep_alive, 58: server_ping_pong, 59: server_ping_pang, 60: service_unavailable_response 61: ]. 62: 63: suite() -> 64: escalus:suite(). 65: 66: ping_interval() -> 67: timer:seconds(3). 68: 69: ping_req_timeout() -> 70: timer:seconds(2). 71: 72: init_per_suite(Config) -> 73: mongoose_helper:inject_module(?MODULE), 74: escalus:init_per_suite(Config). 75: 76: end_per_suite(Config) -> 77: escalus_fresh:clean(), 78: escalus:end_per_suite(Config). 79: 80: init_per_group(client_ping, Config) -> 81: start_mod_ping(#{}), 82: Config; 83: init_per_group(server_ping, Config) -> 84: start_mod_ping(#{send_pings => true, 85: ping_interval => ping_interval(), 86: ping_req_timeout => ping_req_timeout()}), 87: Config; 88: init_per_group(server_ping_kill, Config) -> 89: start_mod_ping(#{send_pings => true, 90: ping_interval => ping_interval(), 91: ping_req_timeout => ping_req_timeout(), 92: timeout_action => kill}), 93: [{timeout_action, kill} | Config]. 94: 95: end_per_group(_GroupName, Config) -> 96: HostType = domain_helper:host_type(mim), 97: dynamic_modules:stop(HostType, mod_ping), 98: Config. 99: 100: init_per_testcase(server_ping_pong = CN, Config) -> 101: NConfig = setup_pong_hook(Config), 102: escalus:init_per_testcase(CN, NConfig); 103: init_per_testcase(CaseName, Config) -> 104: escalus:init_per_testcase(CaseName, Config). 105: 106: end_per_testcase(server_ping_pong = CN, Config) -> 107: NConfig = clear_pong_hook(Config), 108: escalus:init_per_testcase(CN, NConfig); 109: end_per_testcase(CaseName, Config) -> 110: escalus:end_per_testcase(CaseName, Config). 111: 112: start_mod_ping(Opts) -> 113: HostType = domain_helper:host_type(mim), 114: dynamic_modules:start(HostType, mod_ping, config_parser_helper:mod_config(mod_ping, Opts)). 115: 116: setup_pong_hook(Config) -> 117: Pid = self(), 118: HostType = domain_helper:host_type(mim), 119: mongoose_helper:successful_rpc(?MODULE, setup_pong_hook, [HostType, Pid]), 120: [{pid, Pid} | Config]. 121: 122: setup_pong_hook(HostType, Pid) -> 123: gen_hook:add_handler(user_ping_response, HostType, 124: fun ?MODULE:pong_hook_handler/3, 125: #{pid => Pid}, 50). 126: 127: pong_hook_handler(Acc, 128: #{jid := JID} = _Params, 129: #{pid := Pid} = _Extra) -> 130: Pid ! {pong, jid:to_binary(jid:to_lower(JID))}, 131: {ok, Acc}. 132: 133: clear_pong_hook(Config) -> 134: {value, {_, Pid}, NConfig} = lists:keytake(pid, 1, Config), 135: HostType = domain_helper:host_type(mim), 136: mongoose_helper:successful_rpc(?MODULE, clear_pong_hook, [HostType, Pid]), 137: NConfig. 138: 139: clear_pong_hook(HostType, Pid) -> 140: gen_hook:delete_handler(user_ping_response, HostType, 141: fun ?MODULE:pong_hook_handler/3, 142: #{pid => Pid}, 50). 143: 144: %%-------------------------------------------------------------------- 145: %% Ping tests 146: %%-------------------------------------------------------------------- 147: disco(Config) -> 148: escalus:fresh_story( 149: Config, [{alice, 1}], 150: fun(Alice) -> 151: escalus_client:send(Alice, escalus_stanza:disco_info(domain())), 152: Response = escalus_client:wait_for_stanza(Alice), 153: escalus:assert(has_feature, [?NS_PING], Response) 154: end). 155: 156: ping(ConfigIn) -> 157: Domain = domain(), 158: HostType = domain_helper:host_type(mim), 159: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 160: Metrics = [ 161: {[HostTypePrefix, mod_ping, ping_response],0}, 162: {[HostTypePrefix, mod_ping, ping_response_timeout],0} 163: ], 164: Config = [{mongoose_metrics, Metrics} | ConfigIn], 165: escalus:fresh_story(Config, [{alice, 1}], 166: fun(Alice) -> 167: PingReq = escalus_stanza:ping_request(Domain), 168: escalus_client:send(Alice, PingReq), 169: 170: PingResp = escalus_client:wait_for_stanza(Alice), 171: escalus:assert(is_iq_result, [PingReq], PingResp) 172: end). 173: 174: wrong_ping(Config) -> 175: escalus:fresh_story(Config, [{alice, 1}], 176: fun(Alice) -> 177: Domain = domain(), 178: IQ = escalus_stanza:iq(<<"get">>, [#xmlel{name = <<"unsupported">>, 179: attrs = [{<<"xmlns">>, ?NS_PING}] 180: }]), 181: PingReq = escalus_stanza:to(IQ, Domain), 182: escalus_client:send(Alice, PingReq), 183: 184: PingResp = escalus_client:wait_for_stanza(Alice), 185: escalus:assert(is_iq_error, [PingReq], PingResp) 186: end). 187: 188: service_unavailable_response(Config) -> 189: escalus:fresh_story(Config, [{alice, 1}], 190: fun(Alice) -> 191: PingReq = wait_for_ping_req(Alice), 192: PingId = exml_query:attr(PingReq, <<"id">>), 193: 194: ErrorStanzaBody = [#xmlel{name = <<"ping">>, attrs = [{<<"xmlns">>, ?NS_PING}]}, 195: #xmlel{name = <<"error">>, attrs = [{<<"type">>, <<"cancel">>}], 196: children = [#xmlel{name = <<"service-unavailable">>, 197: attrs = [{<<"xmlns">>, ?NS_STANZA_ERRORS}]}]}], 198: ErrorStanza = escalus_stanza:set_id( 199: escalus_stanza:iq(domain(), <<"error">>, ErrorStanzaBody), PingId), 200: escalus_client:send(Alice, ErrorStanza), 201: 202: TimeoutAction = ?config(timeout_action, Config), 203: check_connection(TimeoutAction, Alice), 204: escalus_client:kill_connection(Config, Alice) 205: end). 206: 207: active(ConfigIn) -> 208: Domain = domain(), 209: HostType = domain_helper:host_type(mim), 210: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 211: Metrics = [ 212: {[HostTypePrefix, mod_ping, ping_response],0}, 213: {[HostTypePrefix, mod_ping, ping_response_timeout],0} 214: ], 215: Config = [{mongoose_metrics, Metrics} | ConfigIn], 216: escalus:fresh_story(Config, [{alice, 1}], 217: fun(Alice) -> 218: wait_ping_interval(0.75), 219: escalus_client:send(Alice, escalus_stanza:ping_request(Domain)), 220: escalus:assert(is_iq_result, escalus_client:wait_for_stanza(Alice)), 221: % wait more time and check if connection got ping req 222: wait_ping_interval(0.5), 223: % it shouldn't as the ping was sent 224: false = escalus_client:has_stanzas(Alice) 225: end). 226: 227: active_keep_alive(ConfigIn) -> 228: HostType = domain_helper:host_type(mim), 229: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 230: Metrics = [ 231: {[HostTypePrefix, mod_ping, ping_response],0}, 232: {[HostTypePrefix, mod_ping, ping_response_timeout],0} 233: ], 234: Config = [{mongoose_metrics, Metrics} | ConfigIn], 235: escalus:fresh_story(Config, [{alice, 1}], 236: fun(Alice) -> 237: wait_ping_interval(0.75), 238: escalus_tcp:send(Alice#client.rcv_pid, #xmlcdata{content = "\n"}), 239: wait_ping_interval(0.5), 240: 241: false = escalus_client:has_stanzas(Alice) 242: end). 243: 244: server_ping_pong(ConfigIn) -> 245: HostType = domain_helper:host_type(mim), 246: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 247: Metrics = [ 248: {[HostTypePrefix, mod_ping, ping_response], 5}, 249: {[HostTypePrefix, mod_ping, ping_response_timeout], 0}, 250: {[HostTypePrefix, mod_ping, ping_response_time], changed} 251: ], 252: Config = [{mongoose_metrics, Metrics} | ConfigIn], 253: %% We use 5 Alices because with just 1 sample the histogram may look like it hasn't changed 254: %% due to exometer histogram implementation 255: escalus:fresh_story(Config, [{alice, 5}], 256: fun(Alice1, Alice2, Alice3, Alice4, Alice5) -> 257: lists:foreach(fun(Alice) -> 258: PingReq = wait_for_ping_req(Alice), 259: Pong = escalus_stanza:iq_result(PingReq), 260: escalus_client:send(Alice, Pong) 261: end, [Alice1, Alice2, Alice3, Alice4, Alice5]), 262: wait_for_pong_hooks(5) 263: end). 264: 265: server_ping_pang(ConfigIn) -> 266: HostType = domain_helper:host_type(mim), 267: HostTypePrefix = domain_helper:make_metrics_prefix(HostType), 268: Metrics = [ 269: {[HostTypePrefix, mod_ping, ping_response], 0}, 270: {[HostTypePrefix, mod_ping, ping_response_timeout], 1} 271: ], 272: Config = [{mongoose_metrics, Metrics} | ConfigIn], 273: escalus:fresh_story(Config, [{alice, 1}], 274: fun(Alice) -> 275: wait_for_ping_req(Alice), 276: %% do not resp to ping req 277: ct:sleep(ping_req_timeout() + timer:seconds(1)/2), 278: TimeoutAction = ?config(timeout_action, Config), 279: check_connection(TimeoutAction, Alice), 280: escalus_client:kill_connection(Config, Alice) 281: end). 282: 283: wait_ping_interval(Ration) -> 284: WaitTime = ping_interval() * Ration, 285: ct:sleep(WaitTime). 286: 287: check_connection(kill, Client) -> 288: mongoose_helper:wait_until(fun() -> escalus_connection:is_connected(Client) end, false); 289: check_connection(_, Client) -> 290: true = escalus_connection:is_connected(Client). 291: 292: wait_for_ping_req(Alice) -> 293: PingReq = escalus_client:wait_for_stanza(Alice, timer:seconds(10)), 294: escalus:assert(is_iq_get, PingReq), 295: <<"urn:xmpp:ping">> = exml_query:path(PingReq, [{element, <<"ping">>}, 296: {attr, <<"xmlns">>}]), 297: PingReq. 298: 299: wait_for_pong_hooks(0) -> 300: ok; 301: wait_for_pong_hooks(N) -> 302: receive 303: {pong, _} -> wait_for_pong_hooks(N-1) 304: after 305: 5000 -> 306: ct:fail({pong_hook_runs_missing, N}) 307: end.