1: -module(inbox_extensions_SUITE). 2: 3: -include_lib("escalus/include/escalus.hrl"). 4: -include_lib("escalus/include/escalus_xmlns.hrl"). 5: -include_lib("common_test/include/ct.hrl"). 6: -include_lib("exml/include/exml.hrl"). 7: -include_lib("eunit/include/eunit.hrl"). 8: -include("inbox.hrl"). 9: 10: -define(ROOM_MARKERS_RESET, <<"room_markers_reset">>). 11: -define(HOUR, 3600). 12: -define(VALID_JID, <<"mike@localhost">>). 13: -define(INVALID_JID, <<"$@/">>). 14: -define(INVALID_VALUE, <<"invalid">>). 15: 16: -export([all/0, 17: groups/0, 18: suite/0, 19: init_per_suite/1, 20: end_per_suite/1, 21: init_per_group/2, 22: end_per_group/2, 23: init_per_testcase/2, 24: end_per_testcase/2]). 25: 26: %% ERRORS 27: -export([ 28: % General 29: returns_error_when_no_jid_provided/1, 30: returns_error_when_invalid_jid_provided/1, 31: returns_error_when_valid_jid_but_no_property/1, 32: % Set-unread errors 33: returns_error_when_read_invalid_value/1, 34: returns_error_when_read_valid_request_but_not_in_inbox/1, 35: % Archiving errors 36: returns_error_when_archive_invalid_value/1, 37: returns_error_when_archive_valid_request_but_not_in_inbox/1, 38: % Muting errors 39: returns_error_when_mute_invalid_value/1, 40: returns_error_when_mute_invalid_seconds/1, 41: returns_error_when_mute_valid_request_but_not_in_inbox/1, 42: % Form errors 43: returns_error_when_archive_field_is_invalid/1, 44: returns_error_when_max_is_not_a_number/1 45: ]). 46: %% SUCCESSES 47: -export([ 48: % read 49: read_unread_entry_set_to_read/1, 50: read_unread_entry_set_to_read_iq_id_as_fallback/1, 51: read_unread_entry_set_to_read_queryid/1, 52: read_read_entry_set_to_unread/1, 53: read_unread_entry_with_two_messages_when_set_unread_then_unread_count_stays_in_two/1, 54: % archive 55: archive_active_entry_gets_archived/1, 56: archive_archived_entry_gets_active_on_request/1, 57: archive_archived_entry_gets_active_for_the_sender_on_new_message/1, 58: archive_archived_entry_gets_active_for_the_receiver_on_new_message/1, 59: archive_active_unread_entry_gets_archived_and_still_unread/1, 60: archive_full_archive_can_be_fetched/1, 61: archive_full_archive_can_be_fetched_queryid/1, 62: % mute 63: mute_unmuted_entry_gets_muted/1, 64: mute_muted_entry_gets_unmuted/1, 65: mute_after_timestamp_gets_unmuted/1, 66: mute_muted_conv_restarts_timestamp/1, 67: % other 68: returns_valid_properties_form/1, 69: properties_can_be_get/1, 70: properties_many_can_be_set/1, 71: properties_many_can_be_set_queryid/1, 72: max_queries_can_be_limited/1, 73: max_queries_can_fetch_ahead/1, 74: timestamp_is_not_reset_with_setting_properties/1 75: ]). 76: %% Groupchats 77: -export([ 78: groupchat_setunread_stanza_sets_inbox/1 % muclight 79: ]). 80: 81: 82: all() -> 83: inbox_helper:skip_or_run_inbox_tests(tests()). 84: 85: tests() -> 86: [ 87: {group, regular}, 88: {group, async_pools} 89: ]. 90: 91: suite() -> 92: escalus:suite(). 93: 94: groups() -> 95: Gs = [ 96: {generic, [], [ 97: % General errors 98: returns_error_when_no_jid_provided, 99: returns_error_when_invalid_jid_provided, 100: returns_error_when_valid_jid_but_no_property, 101: % Set-unread errors 102: returns_error_when_read_invalid_value, 103: returns_error_when_read_valid_request_but_not_in_inbox, 104: % Archiving errors 105: returns_error_when_archive_invalid_value, 106: returns_error_when_archive_valid_request_but_not_in_inbox, 107: % Muting errors 108: returns_error_when_mute_invalid_value, 109: returns_error_when_mute_invalid_seconds, 110: returns_error_when_mute_valid_request_but_not_in_inbox, 111: % Form errors 112: returns_error_when_archive_field_is_invalid, 113: returns_error_when_max_is_not_a_number 114: ]}, 115: {one_to_one, [], [ 116: % read 117: read_unread_entry_set_to_read, 118: read_unread_entry_set_to_read_iq_id_as_fallback, 119: read_unread_entry_set_to_read_queryid, 120: read_read_entry_set_to_unread, 121: read_unread_entry_with_two_messages_when_set_unread_then_unread_count_stays_in_two, 122: % archive 123: archive_active_entry_gets_archived, 124: archive_archived_entry_gets_active_on_request, 125: archive_archived_entry_gets_active_for_the_sender_on_new_message, 126: archive_archived_entry_gets_active_for_the_receiver_on_new_message, 127: archive_active_unread_entry_gets_archived_and_still_unread, 128: archive_full_archive_can_be_fetched, 129: archive_full_archive_can_be_fetched_queryid, 130: % mute 131: mute_unmuted_entry_gets_muted, 132: mute_muted_entry_gets_unmuted, 133: mute_after_timestamp_gets_unmuted, 134: mute_muted_conv_restarts_timestamp, 135: % other 136: returns_valid_properties_form, 137: properties_can_be_get, 138: properties_many_can_be_set, 139: properties_many_can_be_set_queryid, 140: max_queries_can_be_limited, 141: max_queries_can_fetch_ahead, 142: timestamp_is_not_reset_with_setting_properties 143: ]}, 144: {muclight, [], [ 145: groupchat_setunread_stanza_sets_inbox 146: ]}, 147: {regular, [], [ 148: {group, generic}, 149: {group, one_to_one}, 150: {group, muclight} 151: ]}, 152: {async_pools, [], [ 153: {group, generic}, 154: {group, one_to_one}, 155: {group, muclight} 156: ]} 157: ], 158: inbox_helper:maybe_run_in_parallel(Gs). 159: 160: init_per_suite(Config) -> 161: escalus:init_per_suite(Config). 162: 163: end_per_suite(Config) -> 164: escalus:end_per_suite(Config). 165: 166: init_per_group(GroupName, Config) when GroupName =:= regular; GroupName =:= async_pools -> 167: HostType = domain_helper:host_type(), 168: Config1 = dynamic_modules:save_modules(HostType, Config), 169: ok = dynamic_modules:ensure_modules( 170: HostType, 171: inbox_helper:inbox_modules(GroupName) ++ inbox_helper:muclight_modules()), 172: InboxOptions = inbox_helper:inbox_opts(GroupName), 173: Config2 = [{inbox_opts, InboxOptions} | Config1], 174: escalus:create_users(Config2, escalus:get_users([alice, bob, kate, mike])); 175: init_per_group(_GroupName, Config) -> 176: Config. 177: 178: end_per_group(GroupName, Config) when GroupName =:= regular; GroupName =:= async_pools -> 179: muc_light_helper:clear_db(domain_helper:host_type()), 180: escalus_fresh:clean(), 181: Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])), 182: dynamic_modules:restore_modules(Config1); 183: end_per_group(_GroupName, Config) -> 184: Config. 185: 186: init_per_testcase(groupchat_setunread_stanza_sets_inbox, Config) -> 187: inbox_helper:clear_inbox_all(), 188: muc_light_helper:create_room(?ROOM_MARKERS_RESET, muc_light_helper:muc_host(), alice, [bob, kate], 189: Config, muc_light_helper:ver(1)), 190: escalus:init_per_testcase(groupchat_setunread_stanza_sets_inbox, Config); 191: init_per_testcase(TestCase, Config) -> 192: escalus:init_per_testcase(TestCase, Config). 193: 194: end_per_testcase(groupchat_setunread_stanza_sets_inbox, Config) -> 195: inbox_helper:clear_inbox_all(), 196: inbox_helper:restore_inbox_option(Config), 197: escalus:end_per_testcase(groupchat_setunread_stanza_sets_inbox, Config); 198: end_per_testcase(TestCase, Config) -> 199: escalus:end_per_testcase(TestCase, Config). 200: 201: 202: %%-------------------------------------------------------------------- 203: %% tests 204: %%-------------------------------------------------------------------- 205: 206: % General 207: returns_error_when_no_jid_provided(Config) -> 208: Stanza = make_inbox_iq_request(undefined, anything, anything), 209: returns_error(Config, Stanza, <<"jid-required">>). 210: 211: returns_error_when_invalid_jid_provided(Config) -> 212: Stanza = make_inbox_iq_request(?INVALID_JID, anything, anything), 213: returns_error(Config, Stanza, <<"invalid-jid">>). 214: 215: returns_error_when_valid_jid_but_no_property(Config) -> 216: Stanza = make_inbox_iq_request(?VALID_JID, undefined, anything), 217: returns_error(Config, Stanza, <<"no-property">>). 218: 219: % Set-unread errors 220: returns_error_when_read_invalid_value(Config) -> 221: Stanza = make_inbox_iq_request(?VALID_JID, read, ?INVALID_VALUE), 222: returns_error(Config, Stanza, <<"bad-request">>). 223: 224: returns_error_when_read_valid_request_but_not_in_inbox(Config) -> 225: Stanza = make_inbox_iq_request(?VALID_JID, read, true), 226: returns_error(Config, Stanza, <<"item-not-found">>). 227: 228: % Archiving errors 229: returns_error_when_archive_invalid_value(Config) -> 230: Stanza = make_inbox_iq_request(?VALID_JID, archive, ?INVALID_VALUE), 231: returns_error(Config, Stanza, <<"bad-request">>). 232: 233: returns_error_when_archive_valid_request_but_not_in_inbox(Config) -> 234: Stanza = make_inbox_iq_request(?VALID_JID, archive, true), 235: returns_error(Config, Stanza, <<"item-not-found">>). 236: 237: % Muting errors 238: returns_error_when_mute_invalid_value(Config) -> 239: Stanza = make_inbox_iq_request(?VALID_JID, mute, ?INVALID_VALUE), 240: returns_error(Config, Stanza, <<"bad-request">>). 241: 242: returns_error_when_mute_invalid_seconds(Config) -> 243: Stanza = make_inbox_iq_request(?VALID_JID, mute, -?HOUR), 244: returns_error(Config, Stanza, <<"bad-request">>). 245: 246: returns_error_when_mute_valid_request_but_not_in_inbox(Config) -> 247: Stanza = make_inbox_iq_request(?VALID_JID, mute, ?HOUR), 248: returns_error(Config, Stanza, <<"item-not-found">>). 249: 250: % Form errors 251: returns_error_when_archive_field_is_invalid(Config) -> 252: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 253: inbox_helper:assert_invalid_inbox_form_value_error(Alice, <<"archive">>, <<"invalid">>) 254: end). 255: 256: returns_error_when_max_is_not_a_number(Config) -> 257: Stanza = inbox_helper:make_inbox_stanza(#{box => both, limit => <<"NaN">>}), 258: returns_error(Config, Stanza, <<"bad-request">>). 259: 260: returns_error(Config, Stanza, Value) -> 261: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 262: assert_invalid_request(Alice, Stanza, Value) 263: end). 264: 265: % read 266: read_unread_entry_set_to_read(Config) -> 267: read_unread_entry_set_to_read(Config, undefined). 268: 269: read_unread_entry_set_to_read_iq_id_as_fallback(Config) -> 270: read_unread_entry_set_to_read(Config, iq_id). 271: 272: read_unread_entry_set_to_read_queryid(Config) -> 273: read_unread_entry_set_to_read(Config, queryid). 274: 275: read_unread_entry_set_to_read(Config, QueryId) -> 276: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 277: % Alice sends a message to Bob 278: Body = <<"Hi Bob">>, 279: inbox_helper:send_msg(Alice, Bob, Body), 280: % Bob has one unread message 281: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 282: % Then Bob decides to mark it as read 283: set_inbox_properties(Bob, Alice, [{read, true}], inbox_helper:maybe_make_queryid(QueryId)), 284: % Bob's inbox has no unread messages 285: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]) 286: end). 287: 288: read_read_entry_set_to_unread(Config) -> 289: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 290: % Alice sends a message to Bob and Bob reads it 291: Body = <<"Hi Bob">>, 292: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 293: % Bob has no unread messages 294: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 295: % Then Bob decides to mark it again as unread 296: set_inbox_properties(Bob, Alice, [{read, false}]), 297: % Bob's inbox has something unread 298: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]) 299: end). 300: 301: read_unread_entry_with_two_messages_when_set_unread_then_unread_count_stays_in_two(Config) -> 302: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 303: % Alice sends a message to Bob and Bob reads it 304: Body = <<"Hi again Bob">>, 305: inbox_helper:send_msg(Alice, Bob, <<"Hi Bob">>), 306: inbox_helper:send_msg(Alice, Bob, Body), 307: % Bob has some unread messages 308: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}]), 309: % Then Bob decides to mark it again as unread 310: set_inbox_properties(Bob, Alice, [{read, false}]), 311: % And the count didn't really change, to prevent losing higher counts 312: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}]) 313: end). 314: 315: % archive 316: archive_active_entry_gets_archived(Config) -> 317: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 318: % Alice sends a message to Bob 319: Body = <<"Hi Bob">>, 320: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 321: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 322: % Then Bob decides to archive it 323: set_inbox_properties(Bob, Alice, [{archive, true}]), 324: % Then the conversation is in the archive and not in the active box 325: inbox_helper:check_inbox(Bob, [], #{box => active}), 326: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], #{box => archive}) 327: end). 328: 329: archive_archived_entry_gets_active_on_request(Config) -> 330: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 331: % Alice sends a message to Bob and Bob archives it immediately 332: Body = <<"Hi Bob">>, 333: inbox_helper:send_msg(Alice, Bob, Body), 334: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 335: set_inbox_properties(Bob, Alice, [{archive, true}]), 336: % Then bob decides to recover the conversation 337: set_inbox_properties(Bob, Alice, [{archive, false}]), 338: % Then the conversation is in the active and not in the archive box 339: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], #{box => active}), 340: inbox_helper:check_inbox(Bob, [], #{box => archive}) 341: end). 342: 343: archive_archived_entry_gets_active_for_the_receiver_on_new_message(Config) -> 344: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 345: % Alice sends a message to Bob and Bob archives it immediately 346: Body = <<"Hi Bob">>, 347: inbox_helper:send_msg(Alice, Bob, Body), 348: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 349: set_inbox_properties(Bob, Alice, [{archive, true}]), 350: % But then Alice keeps writing: 351: inbox_helper:send_msg(Alice, Bob, Body), 352: % Then the conversation is automatically in the active and not in the archive box 353: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}], #{box => active}), 354: inbox_helper:check_inbox(Bob, [], #{box => archive}) 355: end). 356: 357: archive_archived_entry_gets_active_for_the_sender_on_new_message(Config) -> 358: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 359: % Alice sends a message to Bob and then she archives the conversation 360: Body = <<"Hi Bob">>, 361: inbox_helper:send_msg(Alice, Bob, Body), 362: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 363: set_inbox_properties(Alice, Bob, [{archive, true}]), 364: % But then Alice keeps writing 365: inbox_helper:send_msg(Alice, Bob, Body), 366: % Then the conversation is automatically in the active and not in the archive box 367: inbox_helper:check_inbox(Alice, [], #{box => archive}), 368: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], #{box => active}) 369: end). 370: 371: archive_active_unread_entry_gets_archived_and_still_unread(Config) -> 372: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 373: % Alice sends a message to Bob, but Bob archives it without reading it 374: Body = <<"Hi Bob">>, 375: inbox_helper:send_msg(Alice, Bob, Body), 376: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 377: set_inbox_properties(Bob, Alice, [{archive, true}]), 378: inbox_helper:check_inbox(Bob, [], #{box => active}), 379: % Then Bob queries his archive and the conversation is there still unread 380: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], #{box => archive}) 381: end). 382: 383: archive_full_archive_can_be_fetched(Config) -> 384: archive_full_archive_can_be_fetched(Config, undefined). 385: archive_full_archive_can_be_fetched_queryid(Config) -> 386: archive_full_archive_can_be_fetched(Config, queryid). 387: 388: archive_full_archive_can_be_fetched(Config, QueryId) -> 389: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], fun(Alice, Bob, Kate, Mike) -> 390: % Several people write to Alice, and Alice reads and archives all of them 391: inbox_helper:check_inbox(Alice, [], #{box => archive}), 392: #{Alice := AliceConvs} = inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 393: inbox_helper:check_inbox(Alice, AliceConvs), 394: set_inbox_properties(Alice, Bob, [{archive, true}], inbox_helper:maybe_make_queryid(QueryId)), 395: set_inbox_properties(Alice, Kate, [{archive, true}], inbox_helper:maybe_make_queryid(QueryId)), 396: set_inbox_properties(Alice, Mike, [{archive, true}], inbox_helper:maybe_make_queryid(QueryId)), 397: % Then Alice queries her archive and the conversations are there and not in the active box 398: inbox_helper:check_inbox(Alice, [], #{box => active}), 399: inbox_helper:check_inbox(Alice, AliceConvs, #{box => archive}) 400: end). 401: 402: % mute 403: mute_unmuted_entry_gets_muted(Config) -> 404: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 405: % Alice sends a message to Bob and Bob reads it 406: Body = <<"Hi Bob">>, 407: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 408: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 409: % Then Bob decides to mute the conversation 410: set_inbox_properties(Bob, Alice, [{mute, 24*?HOUR}]), 411: % Alice keeps writing 412: inbox_helper:send_msg(Alice, Bob, Body), 413: % Bob's inbox has an unread message, but it's marked as muted 414: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body, 415: verify = fun(_, _, Outer) -> muted_status(23*?HOUR, Outer) end}]) 416: end). 417: 418: mute_muted_entry_gets_unmuted(Config) -> 419: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 420: % Alice sends a message to Bob and Bob reads it 421: Body = <<"Hi Bob">>, 422: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 423: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 424: % Then Bob decides to mute the conversation 425: set_inbox_properties(Bob, Alice, [{mute, 24*?HOUR}]), 426: % Alice keeps writing 427: inbox_helper:send_msg(Alice, Bob, Body), 428: % And Bob unmutes it again because he's now interested 429: set_inbox_properties(Bob, Alice, [{mute, 0}]), 430: % Bob's inbox has an unread message immediately unmuted 431: inbox_helper:check_inbox( 432: Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body, 433: verify = fun(_, _, Outer) -> muted_status(unmuted, Outer) end}]) 434: end). 435: 436: mute_after_timestamp_gets_unmuted(Config) -> 437: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 438: % Alice sends a message to Bob and Bob reads it 439: Body = <<"Hi Bob">>, 440: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 441: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 442: % Then Bob decides to mute the conversation (for a very short 1 second for testing purposes) 443: set_inbox_properties(Bob, Alice, [{mute, 1}]), 444: % Alice keeps writing 445: inbox_helper:send_msg(Alice, Bob, Body), 446: % Inbox eventually returns unmuted 447: Fun = fun() -> 448: try inbox_helper:check_inbox( 449: Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body, 450: verify = fun(_, _, Outer) -> muted_status(unmuted, Outer) end}]), 451: ok 452: catch _:_ -> not_unmuted_yet 453: end 454: end, 455: mongoose_helper:wait_until(Fun, ok, #{name => verify_its_unmuted, sleep_time => 250}) 456: end). 457: 458: mute_muted_conv_restarts_timestamp(Config) -> 459: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 460: % Alice sends a message to Bob and Bob reads it 461: Body = <<"Hi Bob">>, 462: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 463: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 464: % Then Bob decides to mute the conversation 465: set_inbox_properties(Bob, Alice, [{mute, ?HOUR}]), 466: % Alice keeps writing 467: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 468: % Then Bob decides to mute the conversation for way longer 469: set_inbox_properties(Bob, Alice, [{mute, 24*?HOUR}]), 470: % Muted timestamp is way longer than before 471: inbox_helper:check_inbox( 472: Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body, 473: verify = fun(_, _, Outer) -> muted_status(23*?HOUR, Outer) end}]) 474: end). 475: 476: % other 477: returns_valid_properties_form(Config) -> 478: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 479: InboxConversationNS = inbox_helper:inbox_ns_conversation(), 480: escalus:send(Alice, escalus_stanza:iq_get(InboxConversationNS, [])), 481: ResIQ = escalus:wait_for_stanza(Alice), 482: #{field_count := 4} = Form = inbox_helper:parse_form_iq(ResIQ), 483: #{<<"FORM_TYPE">> := #{type := <<"hidden">>, value := InboxConversationNS}} = Form, 484: #{<<"archive">> := #{type := <<"boolean">>, value := <<"false">>}} = Form, 485: #{<<"read">> := #{type := <<"boolean">>, value := <<"false">>}} = Form, 486: #{<<"mute">> := #{type := <<"text-single">>, value := <<"0">>}} = Form 487: end). 488: 489: properties_can_be_get(Config) -> 490: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 491: % Alice sends a message to Bob 492: Body = <<"Hi Bob">>, 493: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 494: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 495: % Then Bob can just query the properties of this entry at will 496: query_properties(Bob, Alice, [{archive, false}, {read, true}, {mute, 0}]) 497: end). 498: properties_many_can_be_set(Config) -> 499: properties_many_can_be_set(Config, undefined). 500: properties_many_can_be_set_queryid(Config) -> 501: properties_many_can_be_set(Config, queryid). 502: 503: properties_many_can_be_set(Config, QueryId) -> 504: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 505: % Alice sends a message to Bob, and Bob sets a bunch of properties about it 506: Body = <<"Hi Bob">>, 507: inbox_helper:send_msg(Alice, Bob, Body), 508: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 509: set_inbox_properties(Bob, Alice, [{archive, true}, {read, true}, {mute, 24*?HOUR}], 510: inbox_helper:maybe_make_queryid(QueryId)), 511: % Then Bob queries his boxes and everything is as expected 512: inbox_helper:check_inbox(Bob, [], #{box => active}), 513: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body, 514: verify = fun(_, _, Outer) -> muted_status(23*?HOUR, Outer) 515: end}], #{box => archive}) 516: end). 517: 518: max_queries_can_be_limited(Config) -> 519: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 520: fun(Alice, Bob, Kate, Mike) -> 521: % Several people write to Alice 522: #{Alice := AliceConvs} = 523: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 524: % Alice has some messages in her inbox 525: inbox_helper:check_inbox(Alice, AliceConvs), 526: % Then Alice queries her inbox setting a limit to only one conversation, 527: % and she gets the newest one 528: ConvWithMike = lists:keyfind(Mike, #conv.to, AliceConvs), 529: inbox_helper:check_inbox(Alice, [ConvWithMike], #{limit => 1, box => active}), 530: % And a limit to two also works fine 531: ConvWithKate = lists:keyfind(Kate, #conv.to, AliceConvs), 532: inbox_helper:check_inbox(Alice, [ConvWithMike, ConvWithKate], #{limit => 2, box => active}) 533: end). 534: 535: max_queries_can_fetch_ahead(Config) -> 536: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 537: fun(Alice, Bob, Kate, Mike) -> 538: #{Alice := AliceConvs} = 539: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 540: ConvWithBob = lists:keyfind(Bob, #conv.to, AliceConvs), 541: ConvWithKate = lists:keyfind(Kate, #conv.to, AliceConvs), 542: % ConvWithMike = lists:keyfind(Mike, #conv.to, AliceConvs), 543: TimeAfterKate = ConvWithKate#conv.time_after, 544: inbox_helper:check_inbox(Alice, [ConvWithKate, ConvWithBob], 545: #{limit => 2, 'end' => TimeAfterKate, box => active}) 546: end). 547: 548: timestamp_is_not_reset_with_setting_properties(Config) -> 549: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 550: % Alice sends a message to Bob 551: inbox_helper:send_msg(Alice, Bob), 552: %% We capture the timestamp 553: [Item1] = inbox_helper:get_inbox(Bob, #{count => 1}), 554: TStamp1 = inbox_helper:timestamp_from_item(Item1), 555: % Bob sets a bunch of properties 556: set_inbox_properties(Bob, Alice, [{read, true}, {mute, 24*?HOUR}]), 557: % Bob gets the inbox again, and timestamp should be the same 558: [Item2] = inbox_helper:get_inbox(Bob, #{count => 1}), 559: TStamp2 = inbox_helper:timestamp_from_item(Item2), 560: ?assertEqual(TStamp1, TStamp2) 561: end). 562: 563: % muclight 564: groupchat_setunread_stanza_sets_inbox(Config) -> 565: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 566: %%% DATA 567: MsgBody = <<"marker time!">>, 568: AliceJid = inbox_helper:to_bare_lower(Alice), 569: BobJid = inbox_helper:to_bare_lower(Bob), 570: KateJid = inbox_helper:to_bare_lower(Kate), 571: RoomJid = muc_light_helper:room_bin_jid(?ROOM_MARKERS_RESET), 572: AliceRoomJid = <<RoomJid/binary,"/", AliceJid/binary>>, 573: %%% WHEN A MESSAGE IS SENT (two times the same message) 574: MsgStanza1 = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomJid, MsgBody), <<"id-1">>), 575: MsgStanza2 = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomJid, MsgBody), <<"id-2">>), 576: escalus:send(Alice, MsgStanza1), 577: inbox_helper:wait_for_groupchat_msg([Alice, Bob, Kate]), 578: escalus:send(Alice, MsgStanza2), 579: inbox_helper:wait_for_groupchat_msg([Alice, Bob, Kate]), 580: % verify that Bob has the message on inbox, reset it, and verify is still there but read 581: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = AliceRoomJid, to = BobJid, content = MsgBody}]), 582: set_inbox_properties(Bob, RoomJid, [{read, true}]), 583: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = AliceRoomJid, to = BobJid, content = MsgBody}]), 584: % Bob sets the inbox as unread again and has so in his inbox 585: set_inbox_properties(Bob, RoomJid, [{read, false}]), 586: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = AliceRoomJid, to = BobJid, content = MsgBody}]), 587: %% Alice has 0 unread messages because she was the sender 588: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = MsgBody}]), 589: %% Kate still has unread messages, and setting the entry as unread keeps the count to two 590: set_inbox_properties(Kate, RoomJid, [{read, false}]), 591: inbox_helper:check_inbox(Kate, [#conv{unread = 2, from = AliceRoomJid, to = KateJid, content = MsgBody}]), 592: %% And nobody received any other stanza 593: inbox_helper:assert_has_no_stanzas([Alice, Bob, Kate]) 594: end). 595: 596: 597: %%-------------------------------------------------------------------- 598: %% Helpers 599: %%-------------------------------------------------------------------- 600: 601: -type maybe_client() :: undefined | escalus:client(). 602: -type box() :: both | active | archive. 603: 604: -spec query_properties(escalus:client(), escalus:client(), proplists:proplist()) -> [exml:element()]. 605: query_properties(From, To, Expected) -> 606: Stanza = make_inbox_get_properties(To), 607: escalus:send(From, Stanza), 608: Result = escalus:wait_for_stanza(From), 609: ?assert(escalus_pred:is_iq_result(Stanza, Result)), 610: [Props] = exml_query:subelements(Result, <<"query">>), 611: ?assertEqual(inbox_helper:inbox_ns_conversation(), exml_query:attr(Props, <<"xmlns">>)), 612: lists:foreach(fun({Key, Val}) -> assert_property(Props, Key, Val) end, Expected). 613: 614: -spec make_inbox_get_properties(escalus:client()) -> exml:element(). 615: make_inbox_get_properties(To) -> 616: Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), jid_attr(To), []), 617: escalus_stanza:iq(<<"get">>, [Query]). 618: 619: -spec set_inbox_properties(escalus:client(), escalus:client(), proplists:proplist()) -> ok. 620: set_inbox_properties(From, To, Properties) -> 621: set_inbox_properties(From, To, Properties, #{}). 622: 623: -spec set_inbox_properties(escalus:client(), escalus:client(), proplists:proplist(), map()) -> ok. 624: set_inbox_properties(From, To, Properties, QueryOpts) -> 625: Stanza = make_inbox_iq_request_with_query_id(To, Properties, QueryOpts), 626: escalus:send(From, Stanza), 627: check_message_with_properties(From, Stanza, Properties, QueryOpts), 628: check_iq_result_for_property(From, Stanza). 629: 630: -spec check_message_with_properties(escalus:client(), exml:element(), proplists:proplist(), map()) -> ok. 631: check_message_with_properties(From, Stanza, Properties, QueryOpts) -> 632: Message = escalus:wait_for_stanza(From), 633: ?assert(escalus_pred:is_message(Message)), 634: ?assert(has_same_id(Stanza, Message)), 635: [X] = exml_query:subelements(Message, <<"x">>), 636: ?assertEqual(inbox_helper:inbox_ns_conversation(), exml_query:attr(X, <<"xmlns">>)), 637: inbox_helper:maybe_check_queryid(X, QueryOpts), 638: % ?assertEqual(QueryId, exml_query:attr(X, <<"queryid">>)), 639: lists:foreach(fun({Key, Val}) -> assert_property(X, Key, Val) end, Properties). 640: 641: -spec check_iq_result_for_property(escalus:client(), exml:element()) -> ok. 642: check_iq_result_for_property(From, Stanza) -> 643: Result = escalus:wait_for_stanza(From), 644: ?assert(escalus_pred:is_iq_result(Stanza, Result)). 645: 646: -spec make_inbox_iq_request(maybe_client(), atom(), atom()) -> exml:element(). 647: make_inbox_iq_request(ToClient, Key, Value) -> 648: make_inbox_iq_request(ToClient, [{Key, Value}]). 649: 650: -spec make_inbox_iq_request(maybe_client(), proplists:proplist()) -> exml:element(). 651: make_inbox_iq_request(ToClient, Properties) when is_list(Properties) -> 652: make_inbox_iq_request_with_query_id(ToClient, Properties, #{}). 653: 654: -spec make_inbox_iq_request_with_query_id(maybe_client(), proplists:proplist(), map()) -> exml:element(). 655: make_inbox_iq_request_with_query_id(ToClient, Properties, QueryId) -> 656: JidAttr = jid_attr(ToClient), 657: IqAttr = id_attr(QueryId), 658: Children = props_to_children(Properties), 659: Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), JidAttr ++ IqAttr, Children), 660: inbox_iq(QueryId, Query). 661: 662: inbox_iq(#{iq_id := IqId}, Query) -> 663: IQ = escalus_stanza:iq(<<"set">>, [Query]), 664: escalus_stanza:set_id(IQ, IqId); 665: inbox_iq(_, Query) -> 666: escalus_stanza:iq(<<"set">>, [Query]). 667: 668: assert_invalid_request(From, Stanza, Value) -> 669: inbox_helper:assert_invalid_form(From, Stanza, Value, Value). 670: 671: -spec jid_attr(maybe_client()) -> proplists:proplist(). 672: jid_attr(undefined) -> []; 673: jid_attr(Client) -> [{<<"jid">>, escalus_utils:get_short_jid(Client)}]. 674: 675: id_attr(#{queryid := QueryId}) -> [{<<"queryid">>, QueryId}]; 676: id_attr(_) -> []. 677: 678: props_to_children(L) -> props_to_children(L, []). 679: props_to_children([], Acc) -> Acc; 680: props_to_children([{undefined, _} | Rest], Acc) -> 681: props_to_children(Rest, Acc); 682: props_to_children([{Key, undefined} | Rest], Acc) -> 683: props_to_children(Rest, [#xmlel{name = Key} | Acc]); 684: props_to_children([{Key, Value} | Rest], Acc) -> 685: props_to_children(Rest, 686: [#xmlel{name = to_bin(Key), children = [#xmlcdata{content = to_bin(Value)}]} | Acc]). 687: 688: assert_property(X, read, Val) -> 689: ?assertEqual(to_bin(Val), exml_query:path(X, [{element, <<"read">>}, cdata])); 690: assert_property(X, archive, Val) -> 691: ?assertEqual(to_bin(Val), exml_query:path(X, [{element, <<"archive">>}, cdata])); 692: assert_property(X, mute, 0) -> 693: ?assertEqual(0, binary_to_integer(exml_query:path(X, [{element, <<"mute">>}, cdata]))); 694: assert_property(X, mute, _) -> 695: Cal = binary_to_list(exml_query:path(X, [{element, <<"mute">>}, cdata])), 696: calendar:rfc3339_to_system_time(Cal, [{unit, microsecond}]). 697: 698: -spec to_bin(term()) -> binary(). 699: to_bin(Value) when is_binary(Value) -> Value; 700: to_bin(Value) when is_atom(Value) -> atom_to_binary(Value, utf8); 701: to_bin(Value) when is_integer(Value) -> integer_to_binary(Value). 702: 703: -spec has_same_id(exml:element(), exml:element()) -> boolean(). 704: has_same_id(OrigStanza, Stanza) -> 705: OrigId = exml_query:attr(OrigStanza, <<"id">>), 706: Id = exml_query:attr(Stanza, <<"id">>), 707: OrigId =:= Id. 708: 709: muted_status(unmuted, Outer) -> 710: Res = exml_query:path(Outer, [{element, <<"result">>}, {element, <<"mute">>}, cdata]), 711: ?assertEqual(<<"0">>, Res); 712: muted_status(MutedOrUnmuted, Outer) -> 713: GivenRfcTimestamp = exml_query:path(Outer, [{element, <<"result">>}, {element, <<"mute">>}, cdata]), 714: GivenMutedUntil = calendar:rfc3339_to_system_time(binary_to_list(GivenRfcTimestamp), [{offset, "Z"}, {unit, microsecond}]), 715: Now = erlang:system_time(microsecond), 716: case MutedOrUnmuted of 717: unmuted -> 718: ?assert(Now > GivenMutedUntil); 719: MutedDiff -> 720: Diff = erlang:convert_time_unit(MutedDiff, second, microsecond), 721: ?assert(Now + Diff < GivenMutedUntil) 722: end.