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