1: %%%---------------------------------------------------------------------- 2: %%% File : mod_event_pusher_http_SUITE 3: %%% Author : Baibossynv Valery <baibossynov.valery@gmail.com> 4: %%% Purpose : Testing passing via http 5: %%% Created : 16 Dec 2015 by Baibossynv Valery <baibossynov.valery@gmail.com> 6: %%%---------------------------------------------------------------------- 7: 8: -module(mod_event_pusher_http_SUITE). 9: -author("baibossynov.valery@gmail.com"). 10: 11: -compile([export_all, nowarn_export_all]). 12: 13: -include_lib("escalus/include/escalus.hrl"). 14: -include_lib("common_test/include/ct.hrl"). 15: -include_lib("eunit/include/eunit.hrl"). 16: 17: -define(ETS_TABLE, mod_event_pusher_http). 18: 19: -import(distributed_helper, [mim/0, 20: require_rpc_nodes/1, 21: rpc/4]). 22: 23: -import(push_helper, [http_notifications_port/0, http_notifications_host/0]). 24: 25: -import(domain_helper, [host_type/0]). 26: 27: -import(config_parser_helper, [mod_config/2, mod_event_pusher_http_handler/0]). 28: 29: %%%=================================================================== 30: %%% Suite configuration 31: %%%=================================================================== 32: 33: suite() -> 34: require_rpc_nodes([mim]) ++ escalus:suite(). 35: 36: all() -> 37: [ 38: {group, no_prefix}, 39: {group, with_prefix} 40: ]. 41: 42: all_tests() -> 43: [ 44: simple_message, 45: simple_message_no_listener, 46: simple_message_failing_listener, 47: proper_http_message_encode_decode 48: ]. 49: 50: groups() -> 51: G = [{no_prefix, [sequence], all_tests()}, 52: {with_prefix, [sequence], all_tests()}], 53: ct_helper:repeat_all_until_all_ok(G). 54: 55: init_per_suite(Config0) -> 56: escalus:init_per_suite(Config0). 57: 58: end_per_suite(Config) -> 59: escalus_fresh:clean(), 60: escalus:end_per_suite(Config). 61: 62: init_per_group(no_prefix, Config) -> 63: set_modules(Config, #{}); 64: init_per_group(with_prefix, Config) -> 65: set_modules(Config, #{path => <<"prefix">>}). 66: 67: end_per_group(_GroupName, Config) -> 68: dynamic_modules:restore_modules(Config), 69: ok. 70: 71: init_per_testcase(CaseName, Config) -> 72: create_events_collection(), 73: start_http_listener(CaseName, get_prefix(Config)), 74: start_pool(), 75: escalus:init_per_testcase(CaseName, Config). 76: 77: end_per_testcase(CaseName, Config) -> 78: stop_pool(), 79: stop_http_listener(CaseName), 80: clear_events_collection(), 81: escalus:end_per_testcase(CaseName, Config). 82: 83: %%%=================================================================== 84: %%% offline tests 85: %%%=================================================================== 86: proper_http_message_encode_decode(Config) -> 87: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], 88: fun(Alice, Bob) -> 89: Sender = jid:nameprep(escalus_client:username(Alice)), 90: Receiver = jid:nameprep(escalus_client:username(Bob)), 91: Server = jid:nodeprep(escalus_users:get_host(Config, alice)), 92: Message = <<"Hi Test!&escape=Hello">>, 93: 94: Stanza = escalus_stanza:chat_to(Bob, Message), 95: escalus:send(Alice, Stanza), 96: escalus:wait_for_stanza(Bob), 97: 98: Body = get_http_request(), 99: 100: ExtractedAndDecoded = rpc(mim(), cow_qs, parse_qs, [Body]), 101: ExpectedList = [{<<"author">>,<<Sender/binary>>}, 102: {<<"server">>,<<Server/binary>>}, 103: {<<"receiver">>,<<Receiver/binary>>}, 104: {<<"message">>,<<Message/binary>>}], 105: SortedExtractedAndDecoded = lists:sort(ExtractedAndDecoded), 106: SortedExpectedList = lists:sort(ExpectedList), 107: ?assertEqual(SortedExpectedList, SortedExtractedAndDecoded) 108: end). 109: 110: 111: simple_message(Config) -> 112: %% we expect one notification message 113: do_simple_message(Config, <<"Hi, Simple!">>), 114: %% fail if we didn't receive http notification 115: Body = get_http_request(), 116: {_, _} = binary:match(Body, <<"alice">>), 117: {_, _} = binary:match(Body, <<"Simple">>). 118: 119: simple_message_no_listener(Config) -> 120: do_simple_message(Config, <<"Hi, NoListener!">>). 121: 122: simple_message_failing_listener(Config) -> 123: do_simple_message(Config, <<"Hi, Failing!">>). 124: 125: do_simple_message(Config0, Msg) -> 126: Config = escalus_fresh:create_users(Config0, [{alice, 1}, {bob, 1}]), 127: %% Alice sends a message to Bob, who is offline 128: {ok, Alice} = escalus_client:start(Config, alice, <<"res1">>), 129: escalus:send(Alice, escalus_stanza:presence(<<"available">>)), 130: BobJid = escalus_users:get_jid(Config, bob), 131: Stanza = escalus_stanza:chat_to(BobJid, Msg), 132: escalus:send(Alice, Stanza), 133: escalus_client:stop(Config, Alice), 134: %% Bob logs in 135: {ok, Bob} = escalus_client:start(Config, bob, <<"res1">>), 136: escalus:send(Bob, escalus_stanza:presence(<<"available">>)), 137: %% He receives his initial presence and the message 138: Stanzas = escalus:wait_for_stanzas(Bob, 2), 139: escalus_new_assert:mix_match([is_presence, is_chat(Msg)], Stanzas), 140: escalus_client:stop(Config, Bob). 141: 142: %%%=================================================================== 143: %%% Helpers 144: %%%=================================================================== 145: 146: get_http_request() -> 147: Key = got_http_request, 148: mongoose_helper:wait_until( 149: fun() -> 1 =:= length(ets:lookup(?ETS_TABLE, Key)) end, 150: true, #{name => missing_request}), 151: [Bins] = lists:map(fun({_, El}) -> El end, ets:lookup(?ETS_TABLE, Key)), 152: ets:delete(?ETS_TABLE, Key), 153: Bins. 154: 155: login_send_presence(Config, User) -> 156: Spec = escalus_users:get_userspec(Config, User), 157: {ok, Client} = escalus_client:start(Config, Spec, <<"dummy">>), 158: escalus:send(Client, escalus_stanza:presence(<<"available">>)), 159: Client. 160: 161: is_chat(Content) -> 162: fun(Stanza) -> escalus_pred:is_chat_message(Content, Stanza) end. 163: 164: get_prefix(no_prefix) -> 165: "/"; 166: get_prefix(with_prefix) -> 167: "/prefix"; 168: get_prefix(Config) -> 169: GroupName = proplists:get_value(name, proplists:get_value(tc_group_properties, Config)), 170: get_prefix(GroupName). 171: 172: start_pool() -> 173: HTTPOpts = #{server => http_notifications_host(), path_prefix => "/", request_timeout => 2000}, 174: PoolOpts = #{strategy => available_worker, workers => 5}, 175: ejabberd_node_utils:call_fun(mongoose_wpool, start_configured_pools, 176: [[#{type => http, scope => global, tag => http_pool, 177: opts => PoolOpts, conn_opts => HTTPOpts}]]). 178: 179: stop_pool() -> 180: ejabberd_node_utils:call_fun(mongoose_wpool, stop, [http, global, http_pool]). 181: 182: set_modules(Config0, ExtraHandlerOpts) -> 183: Config = dynamic_modules:save_modules(host_type(), Config0), 184: Backend = mongoose_helper:get_backend_mnesia_rdbms_riak(host_type()), 185: ModOffline = create_offline_config(Backend), 186: Handler = maps:merge(mod_event_pusher_http_handler(), ExtraHandlerOpts), 187: ModOpts = #{http => #{handlers => [Handler]}}, 188: dynamic_modules:ensure_modules(host_type(), [{mod_event_pusher, ModOpts} | ModOffline]), 189: Config. 190: 191: -spec create_offline_config(atom()) -> [{mod_offline, gen_mod:module_opts()}]. 192: create_offline_config(riak) -> 193: [{mod_offline, mod_config(mod_offline, #{backend => riak, 194: riak => #{bucket_type => <<"offline">>}})}]; 195: create_offline_config(Backend) -> 196: [{mod_offline, mod_config(mod_offline, #{backend => Backend})}]. 197: 198: start_http_listener(simple_message, Prefix) -> 199: http_helper:start(http_notifications_port(), Prefix, fun process_notification/1); 200: start_http_listener(simple_message_no_listener, _) -> 201: ok; 202: start_http_listener(simple_message_failing_listener, Prefix) -> 203: http_helper:start(http_notifications_port(), Prefix, fun(Req) -> Req end); 204: start_http_listener(proper_http_message_encode_decode, Prefix) -> 205: http_helper:start(http_notifications_port(), Prefix, fun process_notification/1). 206: 207: stop_http_listener(simple_message_no_listener) -> 208: ok; 209: stop_http_listener(_) -> 210: http_helper:stop(). 211: 212: process_notification(Req) -> 213: {ok, Body, Req1} = cowboy_req:read_body(Req), 214: Req2 = cowboy_req:reply(200, #{<<"content-type">> => <<"text/plain">>}, <<"OK">>, Req1), 215: Event = {got_http_request, Body}, 216: ets:insert(?ETS_TABLE, Event), 217: Req2. 218: 219: create_events_collection() -> 220: ets:new(?ETS_TABLE, [duplicate_bag, named_table, public]). 221: 222: clear_events_collection() -> 223: ets:delete_all_objects(?ETS_TABLE).