1: -module(carboncopy_SUITE). 2: 3: -compile([export_all, nowarn_export_all]). 4: -include_lib("common_test/include/ct.hrl"). 5: -include_lib("proper/include/proper.hrl"). 6: -include_lib("eunit/include/eunit.hrl"). 7: 8: -define(AE(Expected, Actual), ?assertEqual(Expected, Actual)). 9: -define(BODY, <<"And pious action">>). 10: 11: -import(mongoose_helper, [enable_carbons/1, disable_carbons/1]). 12: -import(domain_helper, [domain/0]). 13: 14: all() -> 15: [{group, all}]. 16: 17: groups() -> 18: G = [{all, [parallel], 19: [discovering_support, 20: enabling_carbons, 21: disabling_carbons, 22: avoiding_carbons, 23: non_enabled_clients_dont_get_sent_carbons, 24: non_enabled_clients_dont_get_received_carbons, 25: enabled_single_resource_doesnt_get_carbons, 26: unavailable_resources_dont_get_carbons, 27: dropped_client_doesnt_create_duplicate_carbons, 28: prop_forward_received_chat_messages, 29: prop_forward_sent_chat_messages, 30: prop_normal_routing_to_bare_jid 31: ]}], 32: ct_helper:repeat_all_until_all_ok(G). 33: 34: %%%=================================================================== 35: %%% Overall setup/teardown 36: %%%=================================================================== 37: init_per_suite(C) -> 38: escalus:init_per_suite(C). 39: 40: end_per_suite(C) -> 41: escalus_fresh:clean(), 42: escalus:end_per_suite(C). 43: 44: %%%=================================================================== 45: %%% Testcase specific setup/teardown 46: %%%=================================================================== 47: init_per_testcase(Name, C) -> 48: escalus:init_per_testcase(Name, C). 49: 50: end_per_testcase(Name, C) -> 51: escalus:end_per_testcase(Name, C). 52: 53: %%%=================================================================== 54: %%% Individual Test Cases (from groups() definition) 55: %%%=================================================================== 56: discovering_support(Config) -> 57: escalus:fresh_story( 58: Config, [{alice, 1}], 59: fun(Alice) -> 60: IqGet = escalus_stanza:disco_info(domain()), 61: escalus_client:send(Alice, IqGet), 62: Result = escalus_client:wait_for_stanza(Alice), 63: escalus:assert(is_iq_result, [IqGet], Result), 64: escalus:assert(has_feature, [<<"urn:xmpp:carbons:2">>], Result) 65: end). 66: 67: enabling_carbons(Config) -> 68: escalus:fresh_story(Config, [{alice, 1}], fun mongoose_helper:enable_carbons/1). 69: 70: disabling_carbons(Config) -> 71: escalus:fresh_story(Config, [{alice, 1}], 72: fun(Alice) -> enable_carbons(Alice), 73: disable_carbons(Alice) end). 74: 75: avoiding_carbons(Config) -> 76: escalus:fresh_story( 77: Config, [{alice, 2}, {bob, 1}], 78: fun(Alice1, Alice2, Bob) -> 79: enable_carbons([Alice1, Alice2]), 80: Msg = escalus_stanza:chat_without_carbon_to(Bob, ?BODY), 81: escalus_client:send(Alice1, Msg), 82: BobReceived = escalus_client:wait_for_stanza(Bob), 83: escalus:assert(is_chat_message, [?BODY], BobReceived), 84: ?assertEqual([], escalus_client:wait_for_stanzas(Alice2, 1, 500)), 85: ?assertEqual([], escalus_client:peek_stanzas(Alice2)) 86: end). 87: 88: non_enabled_clients_dont_get_sent_carbons(Config) -> 89: escalus:fresh_story( 90: Config, [{alice, 2}, {bob, 1}], 91: fun(Alice1, Alice2, Bob) -> 92: Msg = escalus_stanza:chat_to(Bob, ?BODY), 93: escalus_client:send(Alice1, Msg), 94: BobReceived = escalus_client:wait_for_stanza(Bob), 95: escalus:assert(is_chat_message, [?BODY], BobReceived), 96: ?assertEqual([], escalus_client:wait_for_stanzas(Alice2, 1, 500)), 97: ?assertEqual([], escalus_client:peek_stanzas(Alice2)) 98: end). 99: 100: non_enabled_clients_dont_get_received_carbons(Config) -> 101: escalus:fresh_story( 102: Config, [{alice, 2}, {bob, 1}], 103: fun(Alice1, Alice2, Bob) -> 104: Msg = escalus_stanza:chat_to(Alice1, ?BODY), 105: escalus_client:send(Bob, Msg), 106: AliceReceived = escalus_client:wait_for_stanza(Alice1), 107: escalus:assert(is_chat_message, [?BODY], AliceReceived), 108: ?assertEqual([], escalus_client:wait_for_stanzas(Alice2, 1, 500)), 109: ?assertEqual([], escalus_client:peek_stanzas(Alice2)) 110: end). 111: 112: enabled_single_resource_doesnt_get_carbons(Config) -> 113: BobsMessages = [ 114: <<"There's such a thing as dwelling">>, 115: <<"On the thought ourselves have nursed,">>, 116: <<"And with scorn and courage telling">>, 117: <<"The world to do its worst.">> 118: ], 119: escalus:fresh_story( 120: Config, [{alice, 1}, {bob, 1}], 121: fun(Alice, Bob) -> 122: enable_carbons(Alice), 123: [ escalus_client:send(Bob, escalus_stanza:chat_to(Alice, M)) 124: || M <- BobsMessages ], 125: [ escalus:assert(is_chat_message, [M], escalus_client:wait_for_stanza(Alice)) 126: || M <- BobsMessages ] 127: end). 128: 129: unavailable_resources_dont_get_carbons(Config) -> 130: escalus:fresh_story( 131: Config, [{alice, 2}, {bob, 1}], 132: fun(Alice1, Alice2, Bob) -> 133: enable_carbons([Alice1, Alice2]), 134: client_unsets_presence(Alice1), 135: escalus:assert(is_presence_with_type, [<<"unavailable">>], 136: escalus_client:wait_for_stanza(Alice2)), 137: 138: escalus_client:send(Bob, escalus_stanza:chat_to(Alice2, ?BODY)), 139: %% Alice2 receives the message, no carbons for Alice1 140: wait_for_message_with_body(Alice2, ?BODY), 141: client_sets_presence(Alice1), 142: %% still no carbons for Alice1, only presences 143: escalus_new_assert:mix_match([is_presence, is_presence], 144: escalus:wait_for_stanzas(Alice1, 2)), 145: escalus:assert(is_presence, [], 146: escalus_client:wait_for_stanza(Alice2)), 147: enable_carbons(Alice1), 148: %% Send a message with a different body and wait for it. 149: %% If we receive it, we can be sure, that our first message has 150: %% been fully processed by the routing pipeline. 151: Body2 = <<"carbonated">>, 152: escalus_client:send(Bob, escalus_stanza:chat_to(Alice2, Body2)), 153: wait_for_message_with_body(Alice2, Body2), 154: wait_for_carbon_with_body(Alice1, Body2, #{from => Bob, to => Alice2}) 155: end). 156: 157: dropped_client_doesnt_create_duplicate_carbons(Config) -> 158: escalus:fresh_story( 159: Config, [{alice, 2}, {bob, 1}], 160: fun(Alice1, Alice2, Bob) -> 161: enable_carbons([Alice1, Alice2]), 162: Msg = escalus_stanza:chat_to(Bob, ?BODY), 163: escalus_client:stop(Config, Alice2), 164: escalus:assert(is_presence_with_type, [<<"unavailable">>], 165: escalus_client:wait_for_stanza(Alice1)), 166: escalus_client:send(Alice1, Msg), 167: escalus:assert(is_chat_message, [?BODY], 168: escalus_client:wait_for_stanza(Bob)), 169: [] = escalus_client:peek_stanzas(Alice1) 170: end). 171: 172: prop_forward_received_chat_messages(Config) -> 173: run_prop 174: (forward_received, 175: ?FORALL({N, Msg}, {no_of_resources(), utterance()}, 176: true_story 177: (Config, [{alice, 1}, {bob, N}], 178: fun(Users) -> 179: all_bobs_other_resources_get_received_carbons(Users, 180: Msg) 181: end))). 182: 183: prop_forward_sent_chat_messages(Config) -> 184: run_prop 185: (forward_sent, 186: ?FORALL({N, Msg}, {no_of_resources(), utterance()}, 187: true_story 188: (Config, [{alice, 1}, {bob, N}], 189: fun(Users) -> 190: all_bobs_other_resources_get_sent_carbons(Users, 191: Msg) 192: end))). 193: 194: prop_normal_routing_to_bare_jid(Config) -> 195: run_prop 196: (normal_routing, 197: ?FORALL({N, Msg}, {no_of_resources(), utterance()}, 198: true_story 199: (Config, [{alice, 1}, {bob, N}], 200: fun(Users) -> 201: all_bobs_resources_get_message_to_bare_jid(Users, 202: Msg) 203: end))). 204: 205: 206: %% 207: %% Test scenarios w/assertions 208: %% 209: 210: all_bobs_resources_get_message_to_bare_jid([Alice, Bob1 | Bobs], Msg) -> 211: %% All connected resources receive messages sent 212: %% to the user's bare JID without carbon wrappers. 213: enable_carbons([Bob1|Bobs]), 214: escalus_client:send( 215: Alice, escalus_stanza:chat_to(escalus_client:short_jid(Bob1), Msg)), 216: GotMsg = fun(BobsResource) -> 217: escalus:assert( 218: is_chat_message, 219: [Msg], 220: escalus_client:wait_for_stanza(BobsResource)), 221: escalus_assert:has_no_stanzas(BobsResource) 222: end, 223: lists:foreach(GotMsg, [Bob1|Bobs]). 224: 225: all_bobs_other_resources_get_received_carbons([Alice, Bob1 | Bobs], Msg) -> 226: enable_carbons([Bob1|Bobs]), 227: escalus_client:send(Alice, escalus_stanza:chat_to(Bob1, Msg)), 228: escalus_client:wait_for_stanza(Bob1), 229: GotForward = fun(BobsResource) -> 230: escalus:assert( 231: is_forwarded_received_message, 232: [escalus_client:full_jid(Alice), 233: escalus_client:full_jid(Bob1), 234: Msg], 235: escalus_client:wait_for_stanza(BobsResource)), 236: escalus_assert:has_no_stanzas(BobsResource) end, 237: lists:foreach(GotForward, Bobs). 238: 239: all_bobs_other_resources_get_sent_carbons([Alice, Bob1 | Bobs], Msg) -> 240: enable_carbons([Bob1|Bobs]), 241: escalus_client:send(Bob1, escalus_stanza:chat_to(Alice, Msg)), 242: escalus:assert(is_chat_message, [Msg], escalus_client:wait_for_stanza(Alice)), 243: GotCarbon = fun(BobsResource) -> 244: escalus:assert( 245: is_forwarded_sent_message, 246: [escalus_client:full_jid(Bob1), 247: escalus_client:full_jid(Alice), 248: Msg], 249: escalus_client:wait_for_stanza(BobsResource)), 250: escalus_assert:has_no_stanzas(BobsResource) end, 251: lists:foreach(GotCarbon, Bobs). 252: 253: %% 254: %% Internal helpers 255: %% 256: 257: %% Wrapper around escalus:story. Returns PropEr result. 258: true_story(Config, UserSpecs, TestFun) -> 259: try escalus_fresh:story_with_client_list(Config, UserSpecs, TestFun), true 260: catch E -> 261: {error, E} 262: end. 263: 264: %% Number of resources per users 265: no_of_resources() -> 1 + rand:uniform(4). 266: 267: %% A sample chat message 268: utterance() -> 269: proper_types:oneof( 270: [<<"Now, fair Hippolyta, our nuptial hour">>, 271: <<"Draws on apace; four happy days bring in">>, 272: <<"Another moon: but, O, methinks, how slow">>, 273: <<"This old moon wanes! she lingers my desires">>, 274: <<"Like to a step-dame or a dowager">>, 275: <<"Long withering out a young man revenue.">>]). 276: 277: 278: client_unsets_presence(Client) -> 279: escalus_client:send(Client, escalus_stanza:presence(<<"unavailable">>)). 280: 281: client_sets_presence(Client) -> 282: escalus_client:send(Client, escalus_stanza:presence(<<"available">>)). 283: 284: run_prop(PropName, Property) -> 285: ?AE(true, proper:quickcheck(proper:conjunction([{PropName, Property}]), 286: [verbose, long_result, {numtests, 3}])). 287: 288: wait_for_message_with_body(Alice, Body) -> 289: AliceReceived = escalus_client:wait_for_stanza(Alice), 290: escalus:assert(is_chat_message, [Body], AliceReceived). 291: 292: wait_for_carbon_with_body(Alice, Body, #{from := From, to := To}) -> 293: escalus:assert( 294: is_forwarded_received_message, 295: [escalus_client:full_jid(From), escalus_client:full_jid(To), Body], 296: escalus_client:wait_for_stanza(Alice)).