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