1: -module(push_integration_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("common_test/include/ct.hrl"). 5: -include_lib("eunit/include/eunit.hrl"). 6: -include_lib("exml/include/exml.hrl"). 7: -include_lib("inbox.hrl"). 8: 9: -define(RPC_SPEC, distributed_helper:mim()). 10: -define(SESSION_KEY, publish_service). 11: 12: -import(muc_light_helper, 13: [ 14: when_muc_light_message_is_sent/4, 15: then_muc_light_message_is_received_by/2, 16: when_muc_light_affiliations_are_set/3, 17: then_muc_light_affiliations_are_received_by/2 18: ]). 19: -import(push_helper, 20: [ 21: enable_stanza/3, 22: become_unavailable/1, 23: become_available/2 24: ]). 25: -import(distributed_helper, [rpc/4, subhost_pattern/1]). 26: -import(domain_helper, [domain/0]). 27: -import(config_parser_helper, [mod_config/2]). 28: 29: %%-------------------------------------------------------------------- 30: %% Suite configuration 31: %%-------------------------------------------------------------------- 32: 33: all() -> 34: [ 35: {group, pubsub_ful}, 36: {group, pubsub_less} 37: ]. 38: 39: basic_groups() -> 40: [ 41: {group, pm_msg_notifications}, 42: {group, muclight_msg_notifications}, 43: {group, pm_notifications_with_inbox}, 44: {group, groupchat_notifications_with_inbox}, 45: {group, failure_cases_v3}, 46: {group, failure_cases_v2}, 47: {group, integration_with_sm_and_offline_storage}, 48: {group, enhanced_integration_with_sm} 49: ]. 50: 51: groups() -> 52: G = [ 53: {pubsub_ful, [], basic_groups()}, 54: {pubsub_less, [], basic_groups()}, 55: {integration_with_sm_and_offline_storage,[parallel], 56: [ 57: no_duplicates_default_plugin, 58: sm_unack_messages_notified_default_plugin 59: ]}, 60: {enhanced_integration_with_sm,[], 61: [ 62: immediate_notification, 63: double_notification_with_two_sessions_in_resume 64: ]}, 65: {pm_msg_notifications, [parallel], 66: [ 67: pm_msg_notify_on_apns_w_high_priority, 68: pm_msg_notify_on_fcm_w_high_priority, 69: pm_msg_notify_on_apns_w_high_priority_silent, 70: pm_msg_notify_on_fcm_w_high_priority_silent, 71: pm_msg_notify_on_apns_no_click_action, 72: pm_msg_notify_on_fcm_no_click_action, 73: pm_msg_notify_on_apns_w_click_action, 74: pm_msg_notify_on_fcm_w_click_action, 75: pm_msg_notify_on_apns_silent, 76: pm_msg_notify_on_fcm_silent, 77: pm_msg_notify_on_apns_w_topic 78: ]}, 79: {muclight_msg_notifications, [parallel], 80: [ 81: muclight_msg_notify_on_apns_w_high_priority, 82: muclight_msg_notify_on_fcm_w_high_priority, 83: muclight_msg_notify_on_apns_w_high_priority_silent, 84: muclight_msg_notify_on_fcm_w_high_priority_silent, 85: muclight_msg_notify_on_apns_no_click_action, 86: muclight_msg_notify_on_fcm_no_click_action, 87: muclight_msg_notify_on_apns_w_click_action, 88: muclight_msg_notify_on_fcm_w_click_action, 89: muclight_msg_notify_on_apns_silent, 90: muclight_msg_notify_on_fcm_silent, 91: muclight_msg_notify_on_w_topic 92: ]}, 93: {groupchat_notifications_with_inbox, [parallel], 94: [ 95: muclight_inbox_msg_unread_count_apns, 96: muclight_inbox_msg_unread_count_fcm, 97: muclight_aff_change_fcm, 98: muclight_aff_change_apns 99: ]}, 100: {pm_notifications_with_inbox, [parallel], 101: [ 102: inbox_msg_unread_count_apns, 103: inbox_msg_unread_count_fcm, 104: inbox_msg_reset_unread_count_apns, 105: inbox_msg_reset_unread_count_fcm 106: ]}, 107: {failure_cases_v3, [parallel], failure_cases()}, 108: {failure_cases_v2, [parallel], failure_cases()} 109: ], 110: G. 111: 112: failure_cases() -> 113: [ 114: no_push_notification_for_expired_device, 115: no_push_notification_for_internal_mongoose_push_error 116: ]. 117: 118: suite() -> 119: escalus:suite(). 120: 121: %%-------------------------------------------------------------------- 122: %% Init & teardown 123: %%-------------------------------------------------------------------- 124: 125: init_per_suite(Config) -> 126: catch mongoose_push_mock:stop(), 127: mongoose_push_mock:start(Config), 128: Port = mongoose_push_mock:port(), 129: 130: PoolOpts = #{strategy => available_worker, workers => 20}, 131: HTTPOpts = #{server => "https://localhost:" ++ integer_to_list(Port), path_prefix => "/", 132: request_timeout => 2000}, 133: rpc(?RPC_SPEC, mongoose_wpool, start_configured_pools, 134: [[#{type => http, scope => global, tag => mongoose_push_http, opts => PoolOpts, 135: conn_opts => HTTPOpts}]]), 136: ConfigWithModules = dynamic_modules:save_modules(domain(), Config), 137: escalus:init_per_suite(ConfigWithModules). 138: 139: 140: end_per_suite(Config) -> 141: escalus_fresh:clean(), 142: rpc(?RPC_SPEC, mongoose_wpool, stop, [http, global, mongoose_push_http]), 143: mongoose_push_mock:stop(), 144: escalus:end_per_suite(Config). 145: 146: init_per_group(pubsub_less, Config) -> 147: [{pubsub_host, virtual} | Config]; 148: init_per_group(pubsub_ful, Config) -> 149: [{pubsub_host, real} | Config]; 150: init_per_group(G, Config) when G =:= pm_notifications_with_inbox; 151: G =:= groupchat_notifications_with_inbox; 152: G =:= integration_with_sm_and_offline_storage -> 153: case mongoose_helper:is_rdbms_enabled(domain()) of 154: true -> 155: init_modules(G, Config); 156: _ -> 157: {skip, require_rdbms} 158: end; 159: init_per_group(G, Config) -> 160: %% Some cleaning up 161: C = init_modules(G, Config), 162: ReqMods = proplists:get_value(required_modules, C, []), 163: case lists:keymember(mod_muc_light, 1, ReqMods) of 164: true -> 165: muc_light_helper:clear_db(domain_helper:host_type()); 166: false -> 167: ct:log("Skip muc_light_helper:clear_db()", []) 168: end, 169: C. 170: 171: end_per_group(_, Config) -> 172: dynamic_modules:restore_modules(Config), 173: Config. 174: 175: init_per_testcase(CaseName, Config) -> 176: escalus:init_per_testcase(CaseName, Config). 177: 178: end_per_testcase(CaseName, Config) -> 179: escalus:end_per_testcase(CaseName, Config). 180: 181: %%------------------------------------------------------------------------------------ 182: %% GROUP integration_with_sm_and_offline_storage & enhanced_integration_with_sm 183: %%------------------------------------------------------------------------------------ 184: no_duplicates_default_plugin(Config) -> 185: ConnSteps = [start_stream, stream_features, maybe_use_ssl, 186: authenticate, bind, session], 187: 188: %% connect bob and alice 189: BobSpec = escalus_fresh:create_fresh_user(Config, bob), 190: {ok, Bob, _} = escalus_connection:start(BobSpec), 191: escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), 192: BobJID = bare_jid(Bob), 193: 194: AliceSpec = escalus_fresh:create_fresh_user(Config, alice), 195: {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps), 196: escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), 197: escalus_connection:get_stanza(Alice, presence), 198: 199: #{device_token := APNSDevice} = enable_push_for_user(Alice, <<"apns">>, [], Config), 200: 201: escalus_connection:stop(Alice), 202: push_helper:wait_for_user_offline(Alice), 203: 204: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice), <<"msg-1">>)), 205: mongoose_helper:wait_until(fun() -> get_number_of_offline_msgs(AliceSpec) end, 1), 206: verify_notification(APNSDevice, <<"apns">>, [], BobJID, <<"msg-1">>), 207: {ok, NewAlice, _} = escalus_connection:start([{manual_ack, true} | AliceSpec], 208: ConnSteps ++ [stream_management]), 209: escalus_connection:send(NewAlice, escalus_stanza:presence(<<"available">>)), 210: Stanzas = escalus:wait_for_stanzas(NewAlice, 2), 211: escalus:assert_many([is_presence, is_chat_message], Stanzas), 212: 213: escalus_connection:stop(NewAlice), 214: mongoose_helper:wait_until(fun() -> get_number_of_offline_msgs(AliceSpec) end, 1), 215: 216: ?assertExit({test_case_failed, _}, wait_for_push_request(APNSDevice, 500)), 217: 218: escalus_connection:stop(Bob). 219: 220: get_number_of_offline_msgs(Spec) -> 221: Username = escalus_utils:jid_to_lower(proplists:get_value(username, Spec)), 222: Server = proplists:get_value(server, Spec), 223: mongoose_helper:total_offline_messages({Username, Server}). 224: 225: sm_unack_messages_notified_default_plugin(Config) -> 226: ConnSteps = [start_stream, stream_features, maybe_use_ssl, 227: authenticate, bind, session, stream_management], 228: 229: %% connect bob and alice 230: BobSpec = escalus_fresh:create_fresh_user(Config, bob), 231: {ok, Bob, _} = escalus_connection:start(BobSpec), 232: escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), 233: escalus_connection:get_stanza(Bob, presence), 234: BobJID = bare_jid(Bob), 235: 236: AliceSpec = [{manual_ack, false}, {stream_management, true} | 237: escalus_fresh:create_fresh_user(Config, alice)], 238: {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps), 239: escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), 240: escalus_connection:get_stanza(Alice, presence), 241: 242: Room = fresh_room_name(), 243: RoomJID = muc_light_helper:given_muc_light_room(Room, Alice, [{Bob, member}]), 244: 245: #{device_token := FCMDevice} = enable_push_for_user(Alice, <<"fcm">>, [], Config), 246: 247: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice), <<"msg-0">>)), 248: escalus:assert(is_chat_message, [<<"msg-0">>], escalus_connection:get_stanza(Alice, msg)), 249: 250: H = escalus_connection:get_sm_h(Alice), 251: escalus:send(Alice, escalus_stanza:sm_ack(H)), 252: 253: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice), <<"msg-1">>)), 254: escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), 255: SenderJID = muclight_conversation(Bob, RoomJID, <<"msg-2">>), 256: escalus:assert(is_groupchat_message, [<<"msg-2">>], escalus_connection:get_stanza(Alice, msg)), 257: 258: escalus_connection:stop(Alice), 259: push_helper:wait_for_user_offline(Alice), 260: 261: verify_notification(FCMDevice, <<"fcm">>, [], [{SenderJID, <<"msg-2">>}, 262: {BobJID, <<"msg-1">>}]), 263: 264: ?assertExit({test_case_failed, _}, wait_for_push_request(FCMDevice, 500)), 265: 266: escalus_connection:stop(Bob). 267: 268: immediate_notification(Config) -> 269: ConnSteps = [start_stream, stream_features, maybe_use_ssl, 270: authenticate, bind, session, stream_resumption], 271: 272: %% connect bob and alice 273: BobSpec = escalus_fresh:create_fresh_user(Config, bob), 274: {ok, Bob, _} = escalus_connection:start(BobSpec), 275: escalus_connection:send(Bob, escalus_stanza:presence(<<"available">>)), 276: escalus_connection:get_stanza(Bob, presence), 277: BobJID = bare_jid(Bob), 278: 279: AliceSpec = [{manual_ack, false}, {stream_management, true} | 280: escalus_fresh:create_fresh_user(Config, alice)], 281: {ok, Alice, _} = escalus_connection:start(AliceSpec, ConnSteps), 282: escalus_connection:send(Alice, escalus_stanza:presence(<<"available">>)), 283: escalus_connection:get_stanza(Alice, presence), 284: 285: #{device_token := APNSDevice} = enable_push_for_user(Alice, <<"apns">>, [], Config), 286: #{device_token := FCMDevice} = enable_push_for_user(Alice, <<"fcm">>, [], Config), 287: 288: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice), <<"msg-0">>)), 289: escalus:assert(is_chat_message, [<<"msg-0">>], escalus_connection:get_stanza(Alice, msg)), 290: 291: H = escalus_connection:get_sm_h(Alice), 292: escalus:send(Alice, escalus_stanza:sm_ack(H)), 293: 294: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice), <<"msg-1">>)), 295: escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice, msg)), 296: 297: C2SPid = mongoose_helper:get_session_pid(Alice, distributed_helper:mim()), 298: escalus_connection:kill(Alice), 299: 300: verify_notification(FCMDevice, <<"fcm">>, [], BobJID, <<"msg-1">>), 301: 302: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice), <<"msg-2">>)), 303: verify_notification(FCMDevice, <<"fcm">>, [], BobJID, <<"msg-2">>), 304: 305: ?assertExit({test_case_failed, _}, wait_for_push_request(APNSDevice, 500)), 306: 307: rpc(?RPC_SPEC, sys, terminate, [C2SPid, normal]), 308: 309: verify_notification(APNSDevice, <<"apns">>, [], [{BobJID, <<"msg-1">>}, 310: {BobJID, <<"msg-2">>}]), 311: 312: ?assertExit({test_case_failed, _}, wait_for_push_request(FCMDevice, 500)), 313: 314: escalus_connection:stop(Bob). 315: 316: double_notification_with_two_sessions_in_resume(Config) -> 317: 318: %% This test case serves as a demonstration of doubled push notifications 319: %% which occur with multiple push devices and sessions in resume state 320: 321: %% diagram presenting the test's logic 322: %% generated using https://www.sequencediagram.org/ 323: %% title double notifications in 2 sessions in resume 324: %% BobSocket -> BobC2S: msg-1 325: %% participant Alice1C2S #red 326: %% participant Alice2C2S #blue 327: %% BobC2S -#red> Alice1C2S: msg-1 328: %% BobC2S -#blue> Alice2C2S: msg-1 329: %% note over Alice1C2S: connection dies 330: %% activate Alice1C2S 331: %% note over Alice2C2S: connection dies 332: %% activate Alice2C2S 333: %% Alice1C2S -#red> PushService: msg-1 (alice1 device) 334: %% deactivate Alice1C2S 335: %% Alice2C2S -#blue> PushService: msg-1 (alice2 device) 336: %% deactivate Alice2C2S 337: %% note over Alice1C2S: resumption t/o 338: %% activate Alice1C2S 339: %% Alice1C2S -#red> Alice2C2S: msg-1 340: %% activate Alice2C2S #red 341: %% deactivate Alice1C2S 342: %% destroyafter Alice1C2S 343: %% Alice2C2S -#red> PushService: msg-1 (alice2 device) 344: %% deactivate Alice2C2S 345: %% note over Alice2C2S: resumption t/o 346: %% activate Alice2C2S 347: %% Alice2C2S -#blue> PushService: msg-1 (alice1 device) 348: %% deactivate Alice2C2S 349: %% destroyafter Alice2C2S 350: 351: ConnSteps = [start_stream, stream_features, maybe_use_ssl, 352: authenticate, bind, session, stream_resumption], 353: 354: %% connect bob 355: BobSpec = escalus_fresh:create_fresh_user(Config, bob), 356: {ok, Bob, _} = escalus_connection:start(BobSpec), 357: escalus_session:send_presence_available(Bob), 358: escalus_connection:get_stanza(Bob, presence), 359: BobJID = bare_jid(Bob), 360: 361: %% connect two resources for alice 362: AliceSpec1 = [{manual_ack, false}, {stream_resumption, true} | 363: escalus_fresh:create_fresh_user(Config, alice)], 364: AliceSpec2 = [{resource,<<"RES2">>} | AliceSpec1], 365: 366: {ok, Alice1, _} = escalus_connection:start(AliceSpec1, ConnSteps), 367: {ok, Alice2, _} = escalus_connection:start(AliceSpec2, ConnSteps), 368: 369: escalus_session:send_presence_available(Alice1), 370: escalus_connection:get_stanza(Alice1, presence), 371: 372: escalus_session:send_presence_available(Alice2), 373: escalus_connection:get_stanza(Alice2, presence), 374: 375: escalus_connection:get_stanza(Alice1, presence), 376: escalus_connection:get_stanza(Alice2, presence), 377: 378: #{device_token := APNSDevice1} = enable_push_for_user(Alice1, <<"apns">>, [], Config), 379: #{device_token := APNSDevice2} = enable_push_for_user(Alice2, <<"apns">>, [], Config), 380: 381: escalus_connection:send(Bob, escalus_stanza:chat_to(bare_jid(Alice1), <<"msg-1">>)), 382: escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice1, msg)), 383: escalus:assert(is_chat_message, [<<"msg-1">>], escalus_connection:get_stanza(Alice2, msg)), 384: 385: %% go into resume state, which should fire a hook which pushes notifications 386: C2SPid1 = mongoose_helper:get_session_pid(Alice1, distributed_helper:mim()), 387: escalus_connection:kill(Alice1), 388: C2SPid2 = mongoose_helper:get_session_pid(Alice2, distributed_helper:mim()), 389: escalus_connection:kill(Alice2), 390: 391: verify_notification(APNSDevice1, <<"apns">>, [], [{BobJID, <<"msg-1">>}]), 392: verify_notification(APNSDevice2, <<"apns">>, [], [{BobJID, <<"msg-1">>}]), 393: 394: ?assertExit({test_case_failed, _}, wait_for_push_request(APNSDevice1, 500)), 395: ?assertExit({test_case_failed, _}, wait_for_push_request(APNSDevice2, 1)), 396: 397: %% close xmpp stream for Alice1, which causes push notification for APNSDevice2 398: rpc(?RPC_SPEC, sys, terminate, [C2SPid1, normal]), 399: 400: verify_notification(APNSDevice2, <<"apns">>, [], [{BobJID, <<"msg-1">>}]), 401: ?assertExit({test_case_failed, _}, wait_for_push_request(APNSDevice1, 500)), 402: 403: %% close xmpp stream for Alice2, which causes push notification for APNSDevice1 404: rpc(?RPC_SPEC, sys, terminate, [C2SPid2, normal]), 405: 406: verify_notification(APNSDevice1, <<"apns">>, [], [{BobJID, <<"msg-1">>}]), 407: ?assertExit({test_case_failed, _}, wait_for_push_request(APNSDevice2, 500)), 408: 409: escalus_connection:stop(Bob). 410: 411: verify_notification(DeviceToken, Service, EnableOpts, Jid, Msg) -> 412: verify_notification(DeviceToken, Service, EnableOpts, [{Jid, Msg}]). 413: 414: verify_notification(DeviceToken, Service, EnableOpts, ParamsList) -> 415: PredGen = fun({Jid, Msg}) -> 416: fun(Notification) -> 417: try 418: Expected = [{body, Msg}, {unread_count, 1}, {badge, 1}], 419: assert_push_notification(Notification, Service, EnableOpts, 420: Jid, Expected), 421: true 422: catch 423: _:_ -> false 424: end 425: end 426: end, 427: Notifications = [begin 428: {Notification, _} = wait_for_push_request(DeviceToken), 429: Notification 430: end || _ <- ParamsList], 431: ?assertEqual(true, escalus_utils:mix_match(PredGen, ParamsList, Notifications)). 432: 433: %%-------------------------------------------------------------------- 434: %% GROUP pm_msg_notifications 435: %%-------------------------------------------------------------------- 436: 437: pm_msg_notify_on_apns(Config, EnableOpts) -> 438: escalus:fresh_story( 439: Config, [{bob, 1}, {alice, 1}], 440: fun(Bob, Alice) -> 441: {SenderJID, DeviceToken} = pm_conversation(Alice, Bob, <<"apns">>, EnableOpts, Config), 442: {Notification, _} = wait_for_push_request(DeviceToken), 443: 444: assert_push_notification(Notification, <<"apns">>, EnableOpts, SenderJID, []) 445: 446: end). 447: 448: pm_msg_notify_on_fcm(Config, EnableOpts) -> 449: escalus:fresh_story( 450: Config, [{bob, 1}, {alice, 1}], 451: fun(Bob, Alice) -> 452: {SenderJID, DeviceToken} = pm_conversation(Alice, Bob, <<"fcm">>, EnableOpts, Config), 453: {Notification, _} = wait_for_push_request(DeviceToken), 454: 455: assert_push_notification(Notification, <<"fcm">>, EnableOpts, SenderJID) 456: 457: end). 458: 459: assert_push_notification(Notification, Service, EnableOpts, SenderJID) -> 460: assert_push_notification(Notification, Service, EnableOpts, SenderJID, []). 461: 462: assert_push_notification(Notification, Service, EnableOpts, SenderJID, Expected) -> 463: 464: ?assertMatch(#{<<"service">> := Service}, Notification), 465: 466: Alert = maps:get(<<"alert">>, Notification, undefined), 467: Data = maps:get(<<"data">>, Notification, undefined), 468: 469: ExpectedBody = proplists:get_value(body, Expected, <<"OH, HAI!">>), 470: UnreadCount = proplists:get_value(unread_count, Expected, 1), 471: Badge = proplists:get_value(badge, Expected, 1), 472: 473: case proplists:get_value(<<"silent">>, EnableOpts) of 474: undefined -> 475: ?assertMatch(#{<<"body">> := ExpectedBody}, Alert), 476: ?assertMatch(#{<<"title">> := SenderJID}, Alert), 477: ?assertMatch(#{<<"badge">> := Badge}, Alert), 478: ?assertMatch(#{<<"tag">> := SenderJID}, Alert), 479: 480: case proplists:get_value(<<"click_action">>, EnableOpts) of 481: undefined -> 482: ?assertEqual(error, maps:find(<<"click_action">>, Alert)); 483: Activity -> 484: ?assertMatch(#{<<"click_action">> := Activity}, Alert) 485: end; 486: <<"true">> -> 487: ?assertMatch(#{<<"last-message-body">> := ExpectedBody}, Data), 488: ?assertMatch(#{<<"last-message-sender">> := SenderJID}, Data), 489: ?assertMatch(#{<<"message-count">> := UnreadCount}, Data) 490: end, 491: 492: case proplists:get_value(<<"priority">>, EnableOpts) of 493: undefined -> ok; 494: Priority -> 495: ?assertMatch(Priority, maps:get(<<"priority">>, Notification, undefined)) 496: end, 497: 498: case proplists:get_value(<<"topic">>, EnableOpts) of 499: undefined -> ok; 500: Topic -> 501: ?assertMatch(Topic, maps:get(<<"topic">>, Notification, undefined)) 502: end. 503: 504: 505: pm_msg_notify_on_apns_no_click_action(Config) -> 506: pm_msg_notify_on_apns(Config, []). 507: 508: pm_msg_notify_on_fcm_no_click_action(Config) -> 509: pm_msg_notify_on_fcm(Config, []). 510: 511: pm_msg_notify_on_apns_w_high_priority(Config) -> 512: pm_msg_notify_on_apns(Config, [{<<"priority">>, <<"high">>}]). 513: 514: pm_msg_notify_on_fcm_w_high_priority(Config) -> 515: pm_msg_notify_on_fcm(Config, [{<<"priority">>, <<"high">>}]). 516: 517: pm_msg_notify_on_apns_w_high_priority_silent(Config) -> 518: pm_msg_notify_on_apns(Config, [{<<"silent">>, <<"true">>}, {<<"priority">>, <<"high">>}]). 519: 520: pm_msg_notify_on_fcm_w_high_priority_silent(Config) -> 521: pm_msg_notify_on_fcm(Config, [{<<"silent">>, <<"true">>}, {<<"priority">>, <<"high">>}]). 522: 523: pm_msg_notify_on_apns_w_click_action(Config) -> 524: pm_msg_notify_on_apns(Config, [{<<"click_action">>, <<"myactivity">>}]). 525: 526: pm_msg_notify_on_fcm_w_click_action(Config) -> 527: pm_msg_notify_on_fcm(Config, [{<<"click_action">>, <<"myactivity">>}]). 528: 529: pm_msg_notify_on_fcm_silent(Config) -> 530: pm_msg_notify_on_fcm(Config, [{<<"silent">>, <<"true">>}]). 531: 532: pm_msg_notify_on_apns_silent(Config) -> 533: pm_msg_notify_on_apns(Config, [{<<"silent">>, <<"true">>}]). 534: 535: pm_msg_notify_on_apns_w_topic(Config) -> 536: pm_msg_notify_on_apns(Config, [{<<"topic">>, <<"some_topic">>}]). 537: 538: 539: %%-------------------------------------------------------------------- 540: %% GROUP inbox_msg_notifications 541: %%-------------------------------------------------------------------- 542: 543: inbox_msg_unread_count_apns(Config) -> 544: inbox_msg_unread_count(Config, <<"apns">>, [{<<"silent">>, <<"true">>}]). 545: 546: inbox_msg_unread_count_fcm(Config) -> 547: inbox_msg_unread_count(Config, <<"fcm">>, [{<<"silent">>, <<"true">>}]). 548: 549: muclight_inbox_msg_unread_count_apns(Config) -> 550: muclight_inbox_msg_unread_count(Config, <<"apns">>, [{<<"silent">>, <<"true">>}]). 551: 552: muclight_inbox_msg_unread_count_fcm(Config) -> 553: muclight_inbox_msg_unread_count(Config, <<"fcm">>, [{<<"silent">>, <<"true">>}]). 554: 555: inbox_msg_reset_unread_count_apns(Config) -> 556: inbox_msg_reset_unread_count(Config, <<"apns">>, [{<<"silent">>, <<"true">>}]). 557: 558: inbox_msg_reset_unread_count_fcm(Config) -> 559: inbox_msg_reset_unread_count(Config, <<"fcm">>, [{<<"silent">>, <<"true">>}]). 560: 561: inbox_msg_unread_count(Config, Service, EnableOpts) -> 562: escalus:fresh_story( 563: Config, [{bob, 1}, {alice, 1}, {kate, 1}], 564: fun(Bob, Alice, Kate) -> 565: % In this test Bob is the only recipient of all messages 566: #{device_token := DeviceToken} = 567: enable_push_and_become_unavailable(Bob, Service, EnableOpts, Config), 568: 569: % We're going to interleave messages from Alice and Kate to ensure 570: % that their unread counts don't leak to each other's notifications 571: 572: % We send a first message from Alice, unread counts in convs.: Alice 1, Kate 0 573: send_private_message(Alice, Bob), 574: check_notification(DeviceToken, 1), 575: 576: % We send a first message from Kate, unread counts in convs.: Alice 1, Kate 1 577: send_private_message(Kate, Bob), 578: check_notification(DeviceToken, 1), 579: 580: % Now a second message from Alice, unread counts in convs.: Alice 2, Kate 1 581: send_private_message(Alice, Bob), 582: check_notification(DeviceToken, 2), 583: 584: % And one more from Alice, unread counts in convs.: Alice 3, Kate 1 585: send_private_message(Alice, Bob), 586: check_notification(DeviceToken, 3), 587: 588: % Time for Kate again, unread counts in convs.: Alice 3, Kate 2 589: send_private_message(Kate, Bob), 590: check_notification(DeviceToken, 2) 591: end). 592: 593: inbox_msg_reset_unread_count(Config, Service, EnableOpts) -> 594: escalus:fresh_story( 595: Config, [{bob, 1}, {alice, 1}], 596: fun(Bob, Alice) -> 597: #{device_token := DeviceToken} = 598: enable_push_and_become_unavailable(Bob, Service, EnableOpts, Config), 599: send_private_message(Alice, Bob, <<"FIRST MESSAGE">>), 600: check_notification(DeviceToken, 1), 601: MsgId = send_private_message(Alice, Bob, <<"SECOND MESSAGE">>), 602: check_notification(DeviceToken, 2), 603: 604: become_available(Bob, 2), 605: inbox_helper:get_inbox(Bob, #{ count => 1 }), 606: ChatMarker = escalus_stanza:chat_marker(Alice, <<"displayed">>, MsgId), 607: escalus:send(Bob, ChatMarker), 608: escalus:wait_for_stanza(Alice), 609: 610: become_unavailable(Bob), 611: send_private_message(Alice, Bob, <<"THIRD MESSAGE">>), 612: check_notification(DeviceToken, 1) 613: end). 614: 615: 616: muclight_inbox_msg_unread_count(Config, Service, EnableOpts) -> 617: escalus:fresh_story( 618: Config, [{alice, 1}, {kate, 1}], 619: fun(Alice, Kate) -> 620: Room = fresh_room_name(), 621: RoomJID = muc_light_helper:given_muc_light_room(Room, Alice, []), 622: KateJid = inbox_helper:to_bare_lower(Kate), 623: 624: when_muc_light_affiliations_are_set(Alice, Room, [{Kate, member}]), 625: muc_light_helper:verify_aff_bcast([{Kate, member}, {Alice, owner}], [{Kate, member}]), 626: escalus:wait_for_stanza(Alice), 627: 628: #{device_token := KateToken} = 629: enable_push_and_become_unavailable(Kate, Service, EnableOpts, Config), 630: 631: SenderJID = muclight_conversation(Alice, RoomJID, <<"First!">>), 632: escalus:wait_for_stanza(Alice), 633: {Notification, _} = wait_for_push_request(KateToken), 634: assert_push_notification(Notification, Service, EnableOpts, SenderJID, 635: [{body, <<"First!">>}, {unread_count, 1}, {badge, 1}]), 636: 637: muclight_conversation(Alice, RoomJID, <<"Second!">>), 638: escalus:wait_for_stanza(Alice), 639: {Notification2, _} = wait_for_push_request(KateToken), 640: assert_push_notification(Notification2, Service, EnableOpts, SenderJID, 641: [{body, <<"Second!">>}, {unread_count, 2}, {badge, 1}]), 642: 643: {ok, true} = become_available(Kate, 0), 644: 645: muclight_conversation(Alice, RoomJID, <<"Third!">>), 646: escalus:wait_for_stanza(Kate), 647: escalus:wait_for_stanza(Alice), 648: inbox_helper:check_inbox(Kate, [#conv{unread = 3, 649: from = SenderJID, 650: to = KateJid, 651: content = <<"Third!">>}]) 652: end). 653: 654: send_private_message(Sender, Recipient) -> 655: send_private_message(Sender, Recipient, <<"Private message">>). 656: 657: send_private_message(Sender, Recipient, Body) -> 658: Id = escalus_stanza:id(), 659: Msg = escalus_stanza:set_id( escalus_stanza:chat_to(bare_jid(Recipient), Body), Id), 660: escalus:send(Sender, Msg), 661: Id. 662: 663: check_notification(DeviceToken, ExpectedCount) -> 664: {Notification, _} = wait_for_push_request(DeviceToken), 665: Data = maps:get(<<"data">>, Notification, undefined), 666: ?assertMatch(#{<<"message-count">> := ExpectedCount}, Data). 667: 668: send_message_to_room(Sender, RoomJID) -> 669: Stanza = escalus_stanza:groupchat_to(RoomJID, <<"GroupChat message">>), 670: escalus:send(Sender, Stanza). 671: 672: 673: %%-------------------------------------------------------------------- 674: %% GROUP muclight_msg_notifications 675: %%-------------------------------------------------------------------- 676: 677: muclight_msg_notify_on_apns(Config, EnableOpts) -> 678: escalus:fresh_story( 679: Config, [{alice, 1}, {bob, 1}], 680: fun(Alice, Bob) -> 681: Room = fresh_room_name(), 682: RoomJID = muc_light_helper:given_muc_light_room(Room, Alice, [{Bob, member}]), 683: #{device_token := DeviceToken} = 684: enable_push_and_become_unavailable(Bob, <<"apns">>, EnableOpts, Config), 685: 686: SenderJID = muclight_conversation(Alice, RoomJID, <<"Heyah!">>), 687: {Notification, _} = wait_for_push_request(DeviceToken), 688: assert_push_notification(Notification, <<"apns">>, EnableOpts, SenderJID, 689: [{body, <<"Heyah!">>}, {unread_count, 1}, {badge, 1}]) 690: end). 691: 692: muclight_msg_notify_on_fcm(Config, EnableOpts) -> 693: escalus:fresh_story( 694: Config, [{alice, 1}, {bob, 1}], 695: fun(Alice, Bob) -> 696: Room = fresh_room_name(), 697: RoomJID = muc_light_helper:given_muc_light_room(Room, Alice, [{Bob, member}]), 698: #{device_token := DeviceToken} = 699: enable_push_and_become_unavailable(Bob, <<"fcm">>, EnableOpts, Config), 700: 701: SenderJID = muclight_conversation(Alice, RoomJID, <<"Heyah!">>), 702: {Notification, _} = wait_for_push_request(DeviceToken), 703: assert_push_notification(Notification, <<"fcm">>, 704: EnableOpts, SenderJID, [{body, <<"Heyah!">>}, {unread_count, 1}, {badge, 1}]) 705: end). 706: 707: muclight_aff_change(Config, Service, EnableOpts) -> 708: escalus:fresh_story( 709: Config, [{alice, 1}, {kate, 1}, {bob, 1}], 710: fun(Alice, Kate, Bob) -> 711: Room = fresh_room_name(), 712: RoomJID = muc_light_helper:given_muc_light_room(Room, Alice, []), 713: 714: {_, Affiliations} = when_muc_light_affiliations_are_set(Alice, Room, [{Kate, member}]), 715: then_muc_light_affiliations_are_received_by([Alice, Kate], {Room, Affiliations}), 716: escalus:wait_for_stanza(Alice), 717: 718: #{device_token := KateToken} = 719: enable_push_and_become_unavailable(Kate, Service, EnableOpts, Config), 720: 721: Bare = bare_jid(Alice), 722: SenderJID = <<RoomJID/binary, "/", Bare/binary>>, 723: 724: {Room, Body, M1} = when_muc_light_message_is_sent(Alice, Room, <<"First!">>, <<"M1">>), 725: then_muc_light_message_is_received_by([Alice], {Room, Body, M1}), 726: 727: {Notification, _} = wait_for_push_request(KateToken), 728: assert_push_notification(Notification, Service, EnableOpts, SenderJID, 729: [{body, <<"First!">>}, {unread_count, 1}, {badge, 1}]), 730: 731: {_, Aff} = when_muc_light_affiliations_are_set(Alice, Room, [{Bob, member}]), 732: then_muc_light_affiliations_are_received_by([Alice, Bob], {Room, Aff}), 733: escalus:wait_for_stanza(Alice), 734: 735: {_, B2, M2} = when_muc_light_message_is_sent(Alice, Room, <<"Second!">>, <<"M2">>), 736: then_muc_light_message_is_received_by([Alice, Bob], {Room, B2, M2}), 737: 738: {Notification2, _} = wait_for_push_request(KateToken), 739: assert_push_notification(Notification2, Service, EnableOpts, SenderJID, 740: [{body, <<"Second!">>}, {unread_count, 2}, {badge, 1}]) 741: 742: end). 743: 744: 745: muclight_msg_notify_on_apns_no_click_action(Config) -> 746: muclight_msg_notify_on_apns(Config, []). 747: 748: muclight_msg_notify_on_fcm_no_click_action(Config) -> 749: muclight_msg_notify_on_fcm(Config, []). 750: 751: muclight_msg_notify_on_apns_w_high_priority(Config) -> 752: muclight_msg_notify_on_apns(Config, [{<<"priority">>, <<"high">>}]). 753: 754: muclight_msg_notify_on_fcm_w_high_priority(Config) -> 755: muclight_msg_notify_on_fcm(Config, [{<<"priority">>, <<"high">>}]). 756: 757: muclight_msg_notify_on_apns_w_high_priority_silent(Config) -> 758: muclight_msg_notify_on_apns(Config, [{<<"silent">>, <<"true">>}, {<<"priority">>, <<"high">>}]). 759: 760: muclight_msg_notify_on_fcm_w_high_priority_silent(Config) -> 761: muclight_msg_notify_on_fcm(Config, [{<<"silent">>, <<"true">>}, {<<"priority">>, <<"high">>}]). 762: 763: muclight_msg_notify_on_apns_w_click_action(Config) -> 764: muclight_msg_notify_on_apns(Config, [{<<"click_action">>, <<"myactivity">>}]). 765: 766: muclight_msg_notify_on_fcm_w_click_action(Config) -> 767: muclight_msg_notify_on_fcm(Config, [{<<"click_action">>, <<"myactivity">>}]). 768: 769: muclight_msg_notify_on_fcm_silent(Config) -> 770: muclight_msg_notify_on_fcm(Config, [{<<"silent">>, <<"true">>}]). 771: 772: muclight_msg_notify_on_apns_silent(Config) -> 773: muclight_msg_notify_on_apns(Config, [{<<"silent">>, <<"true">>}]). 774: 775: muclight_msg_notify_on_w_topic(Config) -> 776: muclight_msg_notify_on_apns(Config, [{<<"topic">>, <<"some_topic">>}]). 777: 778: muclight_aff_change_fcm(Config) -> 779: muclight_aff_change(Config, <<"fcm">>, [{<<"silent">>, <<"true">>}]). 780: 781: muclight_aff_change_apns(Config) -> 782: muclight_aff_change(Config, <<"apns">>, [{<<"silent">>, <<"true">>}]). 783: 784: no_push_notification_for_expired_device(Config) -> 785: escalus:fresh_story( 786: Config, [{bob, 1}, {alice, 1}], 787: fun(Bob, Alice) -> 788: Response = mongoose_push_unregistered_device_resp(Config), 789: #{device_token := DeviceToken, pubsub_node := PushNode} = 790: enable_push_and_become_unavailable(Bob, <<"fcm">>, [], Response, Config), 791: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 792: {_, Response} = wait_for_push_request(DeviceToken), 793: maybe_check_if_push_node_was_disabled(?config(api_v, Config), Bob, PushNode) 794: 795: end). 796: 797: mongoose_push_unregistered_device_resp(Config) -> 798: case ?config(api_v, Config) of 799: <<"v3">> -> 800: {410, jiffy:encode(#{<<"reason">> => <<"unregistered">>})}; 801: <<"v2">> -> 802: {500, jiffy:encode(#{<<"details">> => <<"probably_unregistered">>})} 803: end. 804: 805: maybe_check_if_push_node_was_disabled(<<"v2">>, _, _) -> 806: ok; 807: maybe_check_if_push_node_was_disabled(<<"v3">>, User, PushNode) -> 808: JID = rpc(?RPC_SPEC, jid, binary_to_bare, [escalus_utils:get_jid(User)]), 809: Host = escalus_utils:get_server(User), 810: Fun = fun() -> 811: {ok, Services} = rpc(?RPC_SPEC, mod_event_pusher_push_backend, get_publish_services, [Host, JID]), 812: lists:keymember(PushNode, 2, Services) 813: end, 814: mongoose_helper:wait_until(Fun, false), 815: 816: Fun2 = fun() -> 817: Info = mongoose_helper:get_session_info(?RPC_SPEC, User), 818: maps:get(?SESSION_KEY, Info, false) 819: end, 820: mongoose_helper:wait_until(Fun2, false). 821: 822: no_push_notification_for_internal_mongoose_push_error(Config) -> 823: escalus:fresh_story( 824: Config, [{bob, 1}, {alice, 1}], 825: fun(Bob, Alice) -> 826: Response = {503, jiffy:encode(#{<<"reason">> => <<"unspecified">>})}, 827: #{device_token := DeviceToken} = 828: enable_push_and_become_unavailable(Bob, <<"fcm">>, [], Response, Config), 829: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 830: {_, Response} = wait_for_push_request(DeviceToken) 831: 832: end). 833: 834: 835: %%-------------------------------------------------------------------- 836: %% Test helpers 837: %%-------------------------------------------------------------------- 838: 839: muclight_conversation(Sender, RoomJID, Msg) -> 840: Bare = bare_jid(Sender), 841: SenderJID = <<RoomJID/binary, "/", Bare/binary>>, 842: Stanza = escalus_stanza:groupchat_to(RoomJID, Msg), 843: escalus:send(Sender, Stanza), 844: SenderJID. 845: 846: pm_conversation(Alice, Bob, Service, EnableOpts, Config) -> 847: AliceJID = bare_jid(Alice), 848: #{device_token := DeviceToken} = 849: enable_push_and_become_unavailable(Bob, Service, EnableOpts, Config), 850: escalus:send(Alice, escalus_stanza:chat_to(Bob, <<"OH, HAI!">>)), 851: {AliceJID, DeviceToken}. 852: 853: enable_push_and_become_unavailable(User, Service, EnableOpts, Config) -> 854: enable_push_and_become_unavailable(User, Service, EnableOpts, {200, <<"OK">>}, Config). 855: 856: enable_push_and_become_unavailable(User, Service, EnableOpts, MockResponse, Config) -> 857: Ret = enable_push_for_user(User, Service, EnableOpts, MockResponse, Config), 858: become_unavailable(User), 859: Ret. 860: 861: enable_push_for_user(User, Service, EnableOpts, Config) -> 862: enable_push_for_user(User, Service, EnableOpts, {200, <<"OK">>}, Config). 863: 864: enable_push_for_user(User, Service, EnableOpts, MockResponse, Config) -> 865: Node = {PubsubJID, NodeName} = pubsub_node_from_host(Config), 866: 867: DeviceToken = gen_token(), 868: 869: case ?config(pubsub_host, Config) of 870: real -> 871: Configuration = [{<<"pubsub#access_model">>, <<"whitelist">>}, 872: {<<"pubsub#publish_model">>, <<"publishers">>}], 873: pubsub_tools:create_node(User, Node, [{type, <<"push">>}, 874: {config, Configuration}]), 875: add_user_server_to_whitelist(User, Node); 876: _ -> 877: skip 878: end, 879: 880: escalus:send(User, enable_stanza(PubsubJID, NodeName, 881: [{<<"service">>, Service}, 882: {<<"device_id">>, DeviceToken}] ++ EnableOpts)), 883: escalus:assert(is_iq_result, escalus:wait_for_stanza(User)), 884: 885: assert_push_notification_in_session(User, NodeName, Service, DeviceToken), 886: 887: mongoose_push_mock:subscribe(DeviceToken, MockResponse), 888: #{device_token => DeviceToken, 889: pubsub_node => NodeName}. 890: 891: add_user_server_to_whitelist(User, {NodeAddr, NodeName}) -> 892: AffList = [ #xmlel{ name = <<"affiliation">>, 893: attrs = [{<<"jid">>, escalus_utils:get_server(User)}, 894: {<<"affiliation">>, <<"publish-only">>}] } 895: ], 896: Affiliations = #xmlel{ name = <<"affiliations">>, attrs = [{<<"node">>, NodeName}], 897: children = AffList }, 898: Id = base64:encode(crypto:strong_rand_bytes(5)), 899: Stanza = escalus_pubsub_stanza:pubsub_owner_iq(<<"set">>, User, Id, NodeAddr, [Affiliations]), 900: escalus:send(User, Stanza), 901: escalus:assert(is_iq_result, [Stanza], escalus:wait_for_stanza(User)). 902: 903: assert_push_notification_in_session(User, NodeName, Service, DeviceToken) -> 904: Info = mongoose_helper:get_session_info(?RPC_SPEC, User), 905: {_JID, NodeName, Details} = maps:get(?SESSION_KEY, Info), 906: ?assertMatch({<<"service">>, Service}, lists:keyfind(<<"service">>, 1, Details)), 907: ?assertMatch({<<"device_id">>, DeviceToken}, lists:keyfind(<<"device_id">>, 1, Details)). 908: 909: wait_for_push_request(DeviceToken) -> 910: mongoose_push_mock:wait_for_push_request(DeviceToken, 10000). 911: 912: wait_for_push_request(DeviceToken, Timeout) -> 913: mongoose_push_mock:wait_for_push_request(DeviceToken, Timeout). 914: 915: %% ---------------------------------- 916: %% Other helpers 917: %% ---------------------------------- 918: 919: fresh_room_name(Username) -> 920: escalus_utils:jid_to_lower(<<"room-", Username/binary>>). 921: 922: fresh_room_name() -> 923: fresh_room_name(base16:encode(crypto:strong_rand_bytes(5))). 924: 925: 926: bare_jid(JIDOrClient) -> 927: ShortJID = escalus_client:short_jid(JIDOrClient), 928: escalus_utils:jid_to_lower(ShortJID). 929: 930: gen_token() -> 931: integer_to_binary(binary:decode_unsigned(crypto:strong_rand_bytes(16)), 24). 932: 933: lower(Bin) when is_binary(Bin) -> 934: list_to_binary(string:to_lower(binary_to_list(Bin))). 935: 936: pubsub_node_from_host(Config) -> 937: case ?config(pubsub_host, Config) of 938: virtual -> 939: pubsub_tools:pubsub_node_with_subdomain("virtual."); 940: real -> 941: pubsub_tools:pubsub_node() 942: end. 943: 944: getenv(VarName, Default) -> 945: case os:getenv(VarName) of 946: false -> 947: Default; 948: Value -> 949: Value 950: end. 951: 952: init_modules(G, Config) -> 953: MongoosePushAPI = mongoose_push_api_for_group(G), 954: PubSubHost = ?config(pubsub_host, Config), 955: Modules = required_modules_for_group(G, MongoosePushAPI, PubSubHost), 956: C = dynamic_modules:save_modules(domain(), Config), 957: Fun = fun() -> catch dynamic_modules:ensure_modules(domain(), Modules) end, 958: mongoose_helper:wait_until(Fun, ok), 959: [{api_v, MongoosePushAPI}, {required_modules, Modules} | C]. 960: 961: mongoose_push_api_for_group(failure_cases_v2) -> 962: <<"v2">>; 963: mongoose_push_api_for_group(_) -> 964: <<"v3">>. 965: 966: required_modules_for_group(pm_notifications_with_inbox, API, PubSubHost) -> 967: [{mod_inbox, inbox_opts()}, 968: {mod_offline, []} | 969: required_modules(API, PubSubHost)]; 970: required_modules_for_group(groupchat_notifications_with_inbox, API, PubSubHost)-> 971: [{mod_inbox, inbox_opts()}, {mod_muc_light, muc_light_opts()} 972: | required_modules(API, PubSubHost)]; 973: required_modules_for_group(muclight_msg_notifications, API, PubSubHost) -> 974: [{mod_muc_light, muc_light_opts()} | required_modules(API, PubSubHost)]; 975: required_modules_for_group(integration_with_sm_and_offline_storage, API, PubSubHost) -> 976: [{mod_muc_light, muc_light_opts()}, 977: {mod_stream_management, config_parser_helper:mod_config(mod_stream_management, 978: #{ack_freq => never, resume_timeout => 1})}, 979: {mod_offline, []} | 980: required_modules(API, PubSubHost)]; 981: required_modules_for_group(enhanced_integration_with_sm, API, PubSubHost) -> 982: [{mod_stream_management, config_parser_helper:mod_config(mod_stream_management, #{ack_freq => never})} | 983: required_modules(API, PubSubHost, mod_event_pusher_push_plugin_enhanced)]; 984: required_modules_for_group(_, API, PubSubHost) -> 985: required_modules(API, PubSubHost). 986: 987: required_modules(API, PubSubHost)-> 988: required_modules(API, PubSubHost, undefined). 989: 990: required_modules(API, PubSubHost, PluginModule) -> 991: VirtualHostOpt = case PubSubHost of 992: virtual -> 993: VirtHostPattern = subhost_pattern("virtual.@HOST@"), 994: [{virtual_pubsub_hosts, [VirtHostPattern]}]; 995: _ -> [] 996: end, 997: PushOpts = case PluginModule of 998: undefined -> VirtualHostOpt; 999: _ -> [{plugin_module, PluginModule} | VirtualHostOpt] 1000: end, 1001: PubSub = case PubSubHost of 1002: virtual -> []; 1003: _ -> 1004: HostPattern = subhost_pattern("pubsub.@HOST@"), 1005: [{mod_pubsub, [{plugins, [<<"dag">>, <<"push">>]}, 1006: {backend, mongoose_helper:mnesia_or_rdbms_backend()}, 1007: {nodetree, <<"dag">>}, 1008: {host, HostPattern}]}] 1009: end, 1010: PushBackend = {push, [{backend, mongoose_helper:mnesia_or_rdbms_backend()} | PushOpts]}, 1011: [ 1012: {mod_push_service_mongoosepush, config_parser_helper:mod_config(mod_push_service_mongoosepush, 1013: #{pool_name => mongoose_push_http, 1014: api_version => API})}, 1015: {mod_event_pusher, [{backends, [PushBackend]}]} | 1016: PubSub 1017: ]. 1018: 1019: muc_light_opts() -> 1020: mod_config(mod_muc_light, #{backend => mongoose_helper:mnesia_or_rdbms_backend(), 1021: rooms_in_rosters => true}). 1022: 1023: inbox_opts() -> 1024: (inbox_helper:inbox_opts())#{aff_changes := false}.