1: -module(mod_aws_sns_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: 5: -include("jlib.hrl"). 6: -include("mongoose.hrl"). 7: -include("mod_event_pusher_events.hrl"). 8: -include_lib("exml/include/exml.hrl"). 9: -include_lib("common_test/include/ct.hrl"). 10: -include_lib("eunit/include/eunit.hrl"). 11: 12: -define(DEFAULT_SNS_CONFIG, [ 13: {access_key_id, "AKIAJAZYHOIPY6A2PESA"}, 14: {secret_access_key, "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"}, 15: {region, "eu-west-1"}, 16: {account_id, "251423380551"}, 17: {sns_host, "sns.eu-west-1.amazonaws.com"}, 18: {plugin_module, mod_event_pusher_sns_defaults}, 19: {presence_updates_topic, "user_presence_updated-dev-1"}, 20: {pm_messages_topic, "user_message_sent-dev-1"}, 21: {muc_messages_topic, "user_messagegroup_sent-dev-1"} 22: ]). 23: 24: -define(ACC_PARAMS, #{location => ?LOCATION, 25: host_type => undefined, 26: lserver => <<"localhost">>, 27: element => undefined}). 28: all() -> 29: [ 30: handles_unicode_messages, 31: forwards_chat_messages_to_chat_topic, 32: forwards_groupchat_messages_to_groupchat_topic, 33: does_not_forward_other_messages, 34: creates_proper_sns_topic_arn, 35: forwards_online_presence_to_presence_topic, 36: forwards_offline_presence_to_presence_topic, 37: does_not_forward_messages_without_body, 38: does_not_forward_messages_when_topic_is_unset, 39: does_not_forward_presences_when_topic_is_unset, 40: calls_callback_module_to_get_user_id, 41: calls_callback_module_to_retrieve_attributes_for_presence, 42: calls_callback_module_to_retrieve_attributes_for_message 43: ]. 44: 45: %% Tests 46: 47: handles_unicode_messages(Config) -> 48: expect_message_entry(message, <<"❤☀☆☂☻♞☯☭☢€"/utf8>>), 49: send_packet_callback(Config, <<"chat">>, <<"❤☀☆☂☻♞☯☭☢€"/utf8>>). 50: 51: forwards_chat_messages_to_chat_topic(Config) -> 52: expect_topic("user_message_sent-dev-1"), 53: send_packet_callback(Config, <<"chat">>, <<"message">>). 54: 55: forwards_groupchat_messages_to_groupchat_topic(Config) -> 56: expect_topic("user_messagegroup_sent-dev-1"), 57: send_packet_callback(Config, <<"groupchat">>, <<"message">>). 58: 59: does_not_forward_other_messages(Config) -> 60: meck:expect(erlcloud_sns, publish, fun(_, _, _, _, _, _) -> ok end), 61: send_packet_callback(Config, <<"othertype">>, <<"message">>), 62: ?assertNot(meck:called(erlcloud_sns, publish, '_')). 63: 64: creates_proper_sns_topic_arn(Config) -> 65: meck:expect(erlcloud_sns, publish, fun(_, _, _, _, _, _) -> ok end), 66: ExpectedTopic = craft_arn("user_message_sent-dev-1"), 67: send_packet_callback(Config, <<"chat">>, <<"message">>), 68: ?assert(meck:called(erlcloud_sns, publish, [topic, ExpectedTopic, '_', '_', '_', '_'])), 69: 70: set_sns_config(#{pm_messages_topic => "othertopicname", account_id => 71: "otheraccountid", region => "otherregion"}), 72: OtherExpectedTopic = craft_arn("otherregion", "otheraccountid", "othertopicname"), 73: send_packet_callback(Config, <<"chat">>, <<"message">>), 74: ?assert(meck:called(erlcloud_sns, publish, [topic, OtherExpectedTopic, '_', '_', '_', '_'])). 75: 76: forwards_online_presence_to_presence_topic(Config) -> 77: expect_message_entry(present, true), 78: user_present_callback(Config), 79: ExpectedTopic = craft_arn("user_presence_updated-dev-1"), 80: ?assert(meck:called(erlcloud_sns, publish, [topic, ExpectedTopic, '_', '_', '_', '_'])). 81: 82: forwards_offline_presence_to_presence_topic(Config) -> 83: expect_message_entry(present, false), 84: user_not_present_callback(Config), 85: ExpectedTopic = craft_arn("user_presence_updated-dev-1"), 86: ?assert(meck:called(erlcloud_sns, publish, [topic, ExpectedTopic, '_', '_', '_', '_'])). 87: 88: does_not_forward_messages_without_body(Config) -> 89: meck:expect(erlcloud_sns, publish, fun(_, _, _, _, _, _) -> ok end), 90: send_packet_callback(Config, <<"chat">>, undefined), 91: ?assertNot(meck:called(erlcloud_sns, publish, '_')). 92: 93: does_not_forward_messages_when_topic_is_unset(Config) -> 94: set_sns_config(#{pm_messages_topic => undefined}), 95: meck:expect(erlcloud_sns, publish, fun(_, _, _, _, _, _) -> ok end), 96: send_packet_callback(Config, <<"chat">>, <<"message">>), 97: ?assertNot(meck:called(erlcloud_sns, publish, '_')). 98: 99: does_not_forward_presences_when_topic_is_unset(Config) -> 100: set_sns_config(#{presence_updates_topic => undefined}), 101: meck:expect(erlcloud_sns, publish, fun(_, _, _, _, _, _) -> ok end), 102: user_not_present_callback(Config), 103: ?assertNot(meck:called(erlcloud_sns, publish, '_')). 104: 105: calls_callback_module_to_get_user_id(Config) -> 106: Module = custom_callback_module("customuserid", #{}), 107: set_sns_config(#{plugin_module => Module}), 108: expect_message_entry(user_id, "customuserid"), 109: user_not_present_callback(Config), 110: ?assert(meck:called(Module, user_guid, '_')). 111: 112: calls_callback_module_to_retrieve_attributes_for_presence(Config) -> 113: Module = custom_callback_module("", #{"a" => 123}), 114: set_sns_config(#{plugin_module => Module}), 115: expect_attributes("a", 123), 116: user_not_present_callback(Config), 117: ?assert(meck:called(Module, message_attributes, ['_', '_', '_'])). 118: 119: calls_callback_module_to_retrieve_attributes_for_message(Config) -> 120: Module = custom_callback_module("", #{"b" => "abc"}), 121: set_sns_config(#{plugin_module => Module}), 122: expect_attributes("b", "abc"), 123: send_packet_callback(Config, <<"groupchat">>, <<"message">>), 124: ?assert(meck:called(Module, message_attributes, ['_', '_', '_', '_', '_'])). 125: 126: %% Fixtures 127: 128: init_per_suite(Config) -> 129: {ok, _} = application:ensure_all_started(jid), 130: Config. 131: 132: end_per_suite(_) -> 133: ok. 134: 135: init_per_testcase(_, Config) -> 136: meck:new([gen_mod, erlcloud_sns, mongoose_wpool], [non_strict, passthrough]), 137: meck:expect(erlcloud_sns, new, fun(_, _, _) -> mod_aws_sns_SUITE_erlcloud_sns_new end), 138: set_sns_config(#{}), 139: meck:expect(mongoose_wpool, cast, fun(_, _, _, {M, F, A}) -> erlang:apply(M, F, A) end), 140: [{sender, jid:from_binary(<<"sender@localhost">>)}, 141: {recipient, jid:from_binary(<<"recipient@localhost">>)} | 142: Config]. 143: 144: end_per_testcase(_, Config) -> 145: meck:unload(), 146: Config. 147: 148: %% Wrapped callbacks 149: 150: send_packet_callback(Config, Type, Body) -> 151: Packet = message(Config, Type, Body), 152: Sender = #jid{lserver = Host} = ?config(sender, Config), 153: Recipient = ?config(recipient, Config), 154: mod_event_pusher_sns:push_event(mongoose_acc:new(?ACC_PARAMS), Host, 155: #chat_event{type = chat, direction = in, 156: from = Sender, to = Recipient, 157: packet = Packet}). 158: 159: user_present_callback(Config) -> 160: Jid = #jid{lserver = Host} = ?config(sender, Config), 161: mod_event_pusher_sns:push_event(mongoose_acc:new(?ACC_PARAMS), Host, 162: #user_status_event{jid = Jid, status = online}). 163: 164: user_not_present_callback(Config) -> 165: Jid = #jid{lserver = Host} = ?config(sender, Config), 166: mod_event_pusher_sns:push_event(mongoose_acc:new(?ACC_PARAMS), Host, 167: #user_status_event{jid = Jid, status = offline}). 168: 169: %% Helpers 170: 171: custom_callback_module(UserId, Attributes) -> 172: Module = mod_aws_sns_SUITE_mockcb, 173: set_sns_config(#{plugin_module => Module}), 174: meck:new(Module, [non_strict]), 175: meck:expect(Module, user_guid, fun(_) -> UserId end), 176: meck:expect(Module, message_attributes, fun(_, _, _) -> Attributes end), 177: meck:expect(Module, message_attributes, fun(_, _, _, _, _) -> Attributes end), 178: Module. 179: 180: craft_arn(Topic) -> 181: craft_arn(proplists:get_value(region, ?DEFAULT_SNS_CONFIG), 182: proplists:get_value(account_id, ?DEFAULT_SNS_CONFIG), Topic). 183: 184: craft_arn(Region, UserId, Topic) -> 185: "arn:aws:sns:" ++ Region ++ ":" ++ UserId ++ ":" ++ Topic. 186: 187: expect_topic(ExpectedTopic) -> 188: meck:expect(erlcloud_sns, publish, 189: fun(topic, Topic, _, _, _, _) -> true = lists:suffix(ExpectedTopic, Topic) end). 190: 191: expect_message_entry(Key, Value) -> 192: meck:expect( 193: erlcloud_sns, publish, 194: fun(_, _, TupleList, _, _, _) -> 195: MessageObject = maps:from_list(TupleList), 196: Value = maps:get(Key, MessageObject) 197: end). 198: 199: expect_attributes(Key, Value) -> 200: meck:expect( 201: erlcloud_sns, publish, 202: fun(_, _, _, _, Attrs, _) -> Value = proplists:get_value(Key, Attrs) end). 203: 204: set_sns_config(Overrides) -> 205: meck:expect(gen_mod, get_module_opt, 206: fun(_, _, Key, _) -> 207: maps:get(Key, Overrides, proplists:get_value(Key, ?DEFAULT_SNS_CONFIG)) 208: end). 209: 210: message(Config, Type, Body) -> 211: message(?config(sender, Config), ?config(recipient, Config), Type, Body). 212: 213: message(From, Recipient, Type, Body) -> 214: Children = 215: case Body of 216: undefined -> []; 217: _ -> [#xmlel{name = <<"body">>, children = [#xmlcdata{content = Body}]}] 218: end, 219: #xmlel{name = <<"message">>, 220: attrs = [{<<"from">>, jid:to_binary(From)}, {<<"type">>, Type}, 221: {<<"to">>, jid:to_binary(Recipient)}], 222: children = Children}.