1: -module(inbox_extensions_SUITE). 2: -compile([export_all, nowarn_export_all]). 3: 4: -include_lib("escalus/include/escalus.hrl"). 5: -include_lib("escalus/include/escalus_xmlns.hrl"). 6: -include_lib("common_test/include/ct.hrl"). 7: -include_lib("exml/include/exml.hrl"). 8: -include_lib("eunit/include/eunit.hrl"). 9: -include("inbox.hrl"). 10: 11: -define(ROOM_MARKERS_RESET, <<"room_markers_reset">>). 12: -define(HOUR, 3600). 13: -define(VALID_JID, <<"mike@localhost">>). 14: -define(INVALID_JID, <<"$@/">>). 15: -define(INVALID_VALUE, <<"invalid">>). 16: 17: -export([all/0, 18: groups/0, 19: suite/0, 20: init_per_suite/1, 21: end_per_suite/1, 22: init_per_group/2, 23: end_per_group/2, 24: init_per_testcase/2, 25: end_per_testcase/2]). 26: 27: all() -> 28: inbox_helper:skip_or_run_inbox_tests(tests()). 29: 30: tests() -> 31: [ 32: {group, regular}, 33: {group, async_pools} 34: ]. 35: 36: suite() -> 37: escalus:suite(). 38: 39: groups() -> 40: Gs = [ 41: {generic, [], [ 42: % General errors 43: returns_error_when_no_jid_provided, 44: returns_error_when_invalid_jid_provided, 45: returns_error_when_valid_jid_but_no_property, 46: % Set-unread errors 47: returns_error_when_read_invalid_value, 48: returns_error_when_read_valid_request_but_not_in_inbox, 49: % Boxes errors 50: returns_error_when_box_invalid_value, 51: returns_error_when_box_valid_request_but_not_in_inbox, 52: % Archiving errors 53: returns_error_when_archive_invalid_value, 54: returns_error_when_archive_valid_request_but_not_in_inbox, 55: % Muting errors 56: returns_error_when_mute_invalid_value, 57: returns_error_when_mute_invalid_seconds, 58: returns_error_when_mute_valid_request_but_not_in_inbox, 59: % Form errors 60: returns_error_when_archive_field_is_invalid, 61: returns_error_when_max_is_not_a_number 62: ]}, 63: {one_to_one, [], [ 64: % read 65: read_unread_entry_set_to_read, 66: read_unread_entry_set_to_read_iq_id_as_fallback, 67: read_unread_entry_set_to_read_queryid, 68: read_read_entry_set_to_unread, 69: read_unread_entry_with_two_messages_when_set_unread_then_unread_count_stays_in_two, 70: % box 71: box_move_to_other_works_successfully, 72: box_active_entry_gets_archived, 73: box_other_entry_does_not_get_unarchived, 74: box_archived_entry_gets_active_on_request, 75: box_archived_entry_gets_active_for_the_sender_on_new_message, 76: box_archived_entry_gets_active_for_the_receiver_on_new_message, 77: box_active_unread_entry_gets_archived_and_still_unread, 78: box_full_archive_can_be_fetched, 79: box_full_archive_can_be_fetched_queryid, 80: box_and_archive_box_has_preference, 81: box_other_does_get_fetched, 82: box_all_full_fetch, 83: % archive 84: archive_active_entry_gets_archived, 85: archive_archived_entry_gets_active_on_request, 86: archive_archived_entry_gets_active_for_the_sender_on_new_message, 87: archive_archived_entry_gets_active_for_the_receiver_on_new_message, 88: archive_active_unread_entry_gets_archived_and_still_unread, 89: archive_full_archive_can_be_fetched, 90: archive_full_archive_can_be_fetched_queryid, 91: % mute 92: mute_unmuted_entry_gets_muted, 93: mute_muted_entry_gets_unmuted, 94: mute_after_timestamp_gets_unmuted, 95: mute_muted_conv_restarts_timestamp, 96: % other 97: returns_valid_properties_form, 98: properties_can_be_get, 99: properties_full_entry_can_be_get, 100: properties_many_can_be_set, 101: properties_many_can_be_set_queryid, 102: timestamp_is_not_reset_with_setting_properties, 103: {group, pagination} 104: ]}, 105: {pagination, [], [ 106: pagination_error_conditions, 107: pagination_has_priority_over_form_before_overrides_start, 108: pagination_has_priority_over_form_after_overrides_end, 109: can_paginate_forwards, 110: can_paginate_backwards, 111: max_queries_can_be_limited, 112: max_queries_can_fetch_ahead 113: ]}, 114: {muclight, [], [ 115: groupchat_setunread_stanza_sets_inbox 116: ]}, 117: {regular, [], [ 118: {group, generic}, 119: {group, one_to_one}, 120: {group, muclight} 121: ]}, 122: {async_pools, [], [ 123: {group, generic}, 124: {group, one_to_one}, 125: {group, muclight} 126: ]} 127: ], 128: inbox_helper:maybe_run_in_parallel(Gs). 129: 130: init_per_suite(Config) -> 131: escalus:init_per_suite(Config). 132: 133: end_per_suite(Config) -> 134: escalus:end_per_suite(Config). 135: 136: init_per_group(GroupName, Config) when GroupName =:= regular; GroupName =:= async_pools -> 137: HostType = domain_helper:host_type(), 138: Config1 = dynamic_modules:save_modules(HostType, Config), 139: ok = dynamic_modules:ensure_modules( 140: HostType, 141: inbox_helper:inbox_modules(GroupName) ++ inbox_helper:muclight_modules()), 142: InboxOptions = inbox_helper:inbox_opts(GroupName), 143: Config2 = [{inbox_opts, InboxOptions} | Config1], 144: escalus:create_users(Config2, escalus:get_users([alice, bob, kate, mike])); 145: init_per_group(_GroupName, Config) -> 146: Config. 147: 148: end_per_group(GroupName, Config) when GroupName =:= regular; GroupName =:= async_pools -> 149: muc_light_helper:clear_db(domain_helper:host_type()), 150: escalus_fresh:clean(), 151: Config1 = escalus:delete_users(Config, escalus:get_users([alice, bob, kate, mike])), 152: dynamic_modules:restore_modules(Config1); 153: end_per_group(_GroupName, Config) -> 154: Config. 155: 156: init_per_testcase(groupchat_setunread_stanza_sets_inbox, Config) -> 157: inbox_helper:clear_inbox_all(), 158: muc_light_helper:create_room(?ROOM_MARKERS_RESET, muc_light_helper:muc_host(), alice, [bob, kate], 159: Config, muc_light_helper:ver(1)), 160: escalus:init_per_testcase(groupchat_setunread_stanza_sets_inbox, Config); 161: init_per_testcase(TestCase, Config) -> 162: escalus:init_per_testcase(TestCase, Config). 163: 164: end_per_testcase(groupchat_setunread_stanza_sets_inbox, Config) -> 165: inbox_helper:clear_inbox_all(), 166: inbox_helper:restore_inbox_option(Config), 167: escalus:end_per_testcase(groupchat_setunread_stanza_sets_inbox, Config); 168: end_per_testcase(TestCase, Config) -> 169: escalus:end_per_testcase(TestCase, Config). 170: 171: 172: %%-------------------------------------------------------------------- 173: %% tests 174: %%-------------------------------------------------------------------- 175: 176: % General 177: returns_error_when_no_jid_provided(Config) -> 178: Stanza = make_inbox_iq_request(undefined, anything, anything), 179: returns_error(Config, Stanza, <<"jid-required">>). 180: 181: returns_error_when_invalid_jid_provided(Config) -> 182: Stanza = make_inbox_iq_request(?INVALID_JID, anything, anything), 183: returns_error(Config, Stanza, <<"invalid-jid">>). 184: 185: returns_error_when_valid_jid_but_no_property(Config) -> 186: Stanza = make_inbox_iq_request(?VALID_JID, undefined, anything), 187: returns_error(Config, Stanza, <<"no-property">>). 188: 189: % Set-unread errors 190: returns_error_when_read_invalid_value(Config) -> 191: Stanza = make_inbox_iq_request(?VALID_JID, read, ?INVALID_VALUE), 192: returns_error(Config, Stanza, <<"bad-request">>). 193: 194: returns_error_when_read_valid_request_but_not_in_inbox(Config) -> 195: Stanza = make_inbox_iq_request(?VALID_JID, read, true), 196: returns_error(Config, Stanza, <<"item-not-found">>). 197: 198: % Boxes errors 199: returns_error_when_box_invalid_value(Config) -> 200: Stanza = make_inbox_iq_request(?VALID_JID, box, ?INVALID_VALUE), 201: returns_error(Config, Stanza, <<"invalid-box">>). 202: 203: returns_error_when_box_valid_request_but_not_in_inbox(Config) -> 204: Stanza = make_inbox_iq_request(?VALID_JID, box, <<"archive">>), 205: returns_error(Config, Stanza, <<"item-not-found">>). 206: 207: % Archiving errors 208: returns_error_when_archive_invalid_value(Config) -> 209: Stanza = make_inbox_iq_request(?VALID_JID, archive, ?INVALID_VALUE), 210: returns_error(Config, Stanza, <<"bad-request">>). 211: 212: returns_error_when_archive_valid_request_but_not_in_inbox(Config) -> 213: Stanza = make_inbox_iq_request(?VALID_JID, archive, true), 214: returns_error(Config, Stanza, <<"item-not-found">>). 215: 216: % Muting errors 217: returns_error_when_mute_invalid_value(Config) -> 218: Stanza = make_inbox_iq_request(?VALID_JID, mute, ?INVALID_VALUE), 219: returns_error(Config, Stanza, <<"bad-request">>). 220: 221: returns_error_when_mute_invalid_seconds(Config) -> 222: Stanza = make_inbox_iq_request(?VALID_JID, mute, -?HOUR), 223: returns_error(Config, Stanza, <<"bad-request">>). 224: 225: returns_error_when_mute_valid_request_but_not_in_inbox(Config) -> 226: Stanza = make_inbox_iq_request(?VALID_JID, mute, ?HOUR), 227: returns_error(Config, Stanza, <<"item-not-found">>). 228: 229: % Form errors 230: returns_error_when_archive_field_is_invalid(Config) -> 231: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 232: inbox_helper:assert_invalid_inbox_form_value_error(Alice, <<"archive">>, <<"invalid">>) 233: end). 234: 235: returns_error_when_max_is_not_a_number(Config) -> 236: Stanza = inbox_helper:make_inbox_stanza(#{box => both, limit => <<"NaN">>}), 237: returns_error(Config, Stanza, <<"bad-request">>). 238: 239: returns_error(Config, Stanza, Value) -> 240: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 241: assert_invalid_request(Alice, Stanza, Value) 242: end). 243: 244: % read 245: read_unread_entry_set_to_read(Config) -> 246: read_unread_entry_set_to_read(Config, undefined). 247: 248: read_unread_entry_set_to_read_iq_id_as_fallback(Config) -> 249: read_unread_entry_set_to_read(Config, iq_id). 250: 251: read_unread_entry_set_to_read_queryid(Config) -> 252: read_unread_entry_set_to_read(Config, queryid). 253: 254: read_unread_entry_set_to_read(Config, QueryId) -> 255: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 256: % Alice sends a message to Bob 257: Body = <<"Hi Bob">>, 258: inbox_helper:send_msg(Alice, Bob, Body), 259: % Bob has one unread message 260: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 261: % Then Bob decides to mark it as read 262: set_inbox_properties(Bob, Alice, [{read, true}], inbox_helper:maybe_make_queryid(QueryId)), 263: % Bob's inbox has no unread messages 264: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]) 265: end). 266: 267: read_read_entry_set_to_unread(Config) -> 268: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 269: % Alice sends a message to Bob and Bob reads it 270: Body = <<"Hi Bob">>, 271: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 272: % Bob has no unread messages 273: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 274: % Then Bob decides to mark it again as unread 275: set_inbox_properties(Bob, Alice, [{read, false}]), 276: % Bob's inbox has something unread 277: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]) 278: end). 279: 280: read_unread_entry_with_two_messages_when_set_unread_then_unread_count_stays_in_two(Config) -> 281: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 282: % Alice sends a message to Bob and Bob reads it 283: Body = <<"Hi again Bob">>, 284: inbox_helper:send_msg(Alice, Bob, <<"Hi Bob">>), 285: inbox_helper:send_msg(Alice, Bob, Body), 286: % Bob has some unread messages 287: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}]), 288: % Then Bob decides to mark it again as unread 289: set_inbox_properties(Bob, Alice, [{read, false}]), 290: % And the count didn't really change, to prevent losing higher counts 291: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}]) 292: end). 293: 294: % box 295: box_move_to_other_works_successfully(Config) -> 296: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 297: % Alice sends a message to Bob 298: Body = <<"Hi Bob">>, 299: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 300: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 301: % Then Bob decides to move it to other 302: set_inbox_properties(Bob, Alice, [{box, other}]), 303: % Then the conversation is in the right box 304: inbox_helper:check_inbox(Bob, [], #{box => archive}), 305: inbox_helper:check_inbox(Bob, [], #{box => inbox}), 306: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], 307: #{box => other}) 308: end). 309: 310: box_active_entry_gets_archived(Config) -> 311: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 312: % Alice sends a message to Bob 313: Body = <<"Hi Bob">>, 314: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 315: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 316: % Then Bob decides to archive it 317: set_inbox_properties(Bob, Alice, [{box, archive}]), 318: % Then the conversation is in the archive and not in the inbox box 319: inbox_helper:check_inbox(Bob, [], #{box => inbox}), 320: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], 321: #{box => archive}) 322: end). 323: 324: box_other_entry_does_not_get_unarchived(Config) -> 325: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 326: % Alice sends a message to Bob and then she moves the conversation to the 'other' box 327: Body1 = <<"Hi Bob">>, 328: inbox_helper:send_msg(Alice, Bob, Body1), 329: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body1}]), 330: set_inbox_properties(Alice, Bob, [{box, other}]), 331: % But then Alice keeps writing 332: Body2 = <<"Hi Bob again">>, 333: inbox_helper:send_msg(Alice, Bob, Body2), 334: inbox_helper:get_inbox(Alice, #{box => all, hidden_read => false}, #{count => 1}), 335: % Then the conversation is still in the same box 336: inbox_helper:check_inbox(Alice, [], #{box => inbox}), 337: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body2}], 338: #{box => other}) 339: end). 340: 341: box_archived_entry_gets_active_on_request(Config) -> 342: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 343: % Alice sends a message to Bob and Bob archives it immediately 344: Body = <<"Hi Bob">>, 345: inbox_helper:send_msg(Alice, Bob, Body), 346: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 347: set_inbox_properties(Bob, Alice, [{box, archive}]), 348: % Then bob decides to recover the conversation 349: set_inbox_properties(Bob, Alice, [{box, inbox}]), 350: % Then the conversation is in the inbox and not in the archive box 351: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 352: #{box => inbox}), 353: inbox_helper:check_inbox(Bob, [], #{box => archive}) 354: end). 355: 356: box_archived_entry_gets_active_for_the_receiver_on_new_message(Config) -> 357: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 358: % Alice sends a message to Bob and Bob archives it immediately 359: Body = <<"Hi Bob">>, 360: inbox_helper:send_msg(Alice, Bob, Body), 361: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 362: set_inbox_properties(Bob, Alice, [{box, archive}]), 363: % But then Alice keeps writing: 364: inbox_helper:send_msg(Alice, Bob, Body), 365: % Then the conversation is automatically in the inbox and not in the archive box 366: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}], 367: #{box => inbox}), 368: inbox_helper:check_inbox(Bob, [], #{box => archive}) 369: end). 370: 371: box_archived_entry_gets_active_for_the_sender_on_new_message(Config) -> 372: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 373: % Alice sends a message to Bob and then she archives the conversation 374: Body = <<"Hi Bob">>, 375: inbox_helper:send_msg(Alice, Bob, Body), 376: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 377: set_inbox_properties(Alice, Bob, [{box, archive}]), 378: % But then Alice keeps writing 379: inbox_helper:send_msg(Alice, Bob, Body), 380: % Then the conversation is automatically in the inbox and not in the archive box 381: inbox_helper:check_inbox(Alice, [], #{box => archive}), 382: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], 383: #{box => inbox}) 384: end). 385: 386: box_active_unread_entry_gets_archived_and_still_unread(Config) -> 387: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 388: % Alice sends a message to Bob, but Bob archives it without reading it 389: Body = <<"Hi Bob">>, 390: inbox_helper:send_msg(Alice, Bob, Body), 391: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 392: set_inbox_properties(Bob, Alice, [{box, archive}]), 393: inbox_helper:check_inbox(Bob, [], #{box => inbox}), 394: % Then Bob queries his archive and the conversation is there still unread 395: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 396: #{box => archive}) 397: end). 398: 399: box_full_archive_can_be_fetched(Config) -> 400: box_full_archive_can_be_fetched(Config, undefined). 401: box_full_archive_can_be_fetched_queryid(Config) -> 402: box_full_archive_can_be_fetched(Config, queryid). 403: 404: box_full_archive_can_be_fetched(Config, QueryId) -> 405: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], fun(Alice, Bob, Kate, Mike) -> 406: % Several people write to Alice, and Alice reads and archives all of them 407: inbox_helper:check_inbox(Alice, [], #{box => archive}), 408: #{Alice := AliceConvs} = inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 409: inbox_helper:check_inbox(Alice, AliceConvs), 410: set_inbox_properties(Alice, Bob, [{box, archive}], inbox_helper:maybe_make_queryid(QueryId)), 411: set_inbox_properties(Alice, Kate, [{box, archive}], inbox_helper:maybe_make_queryid(QueryId)), 412: set_inbox_properties(Alice, Mike, [{box, archive}], inbox_helper:maybe_make_queryid(QueryId)), 413: % Then Alice queries her archive and the conversations are there and not in the inbox box 414: inbox_helper:check_inbox(Alice, [], #{box => inbox}), 415: inbox_helper:check_inbox(Alice, AliceConvs, #{box => archive}) 416: end). 417: 418: box_and_archive_box_has_preference(Config) -> 419: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 420: % Alice sends a message to Bob 421: Body = <<"Hi Bob">>, 422: inbox_helper:send_msg(Alice, Bob, Body), 423: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 424: % Then fetching with archive and box ignores the archive value 425: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 426: #{box => inbox, archive => true}), 427: inbox_helper:check_inbox(Bob, [], #{box => archive, archive => false}) 428: end). 429: 430: box_other_does_get_fetched(Config) -> 431: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 432: % Alice sends a message to Bob and Bob throws in into 'other' 433: Body = <<"Hi Bob">>, 434: inbox_helper:send_msg(Alice, Bob, Body), 435: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 436: set_inbox_properties(Bob, Alice, [{box, other}]), 437: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 438: #{box => other}), 439: % Then bob decides to recover the conversation 440: set_inbox_properties(Bob, Alice, [{box, inbox}]), 441: % Then the conversation is in the inbox and not in the archive box 442: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 443: #{box => inbox}), 444: inbox_helper:check_inbox(Bob, [], #{box => archive}) 445: end). 446: 447: box_all_full_fetch(Config) -> 448: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 449: % Alice sends a message to Bob and Kate 450: #{ Alice := AliceConvs } = inbox_helper:given_conversations_between(Alice, [Bob, Kate]), 451: inbox_helper:check_inbox(Alice, AliceConvs), 452: set_inbox_properties(Alice, Bob, [{box, archive}]), 453: set_inbox_properties(Alice, Kate, [{box, other}]), 454: inbox_helper:check_inbox(Alice, AliceConvs, #{box => all}) 455: end). 456: 457: % archive 458: archive_active_entry_gets_archived(Config) -> 459: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 460: % Alice sends a message to Bob 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 archive it 465: set_inbox_properties(Bob, Alice, [{archive, true}]), 466: % Then the conversation is in the archive and not in the active box 467: inbox_helper:check_inbox(Bob, [], #{archive => false}), 468: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], 469: #{archive => true}) 470: end). 471: 472: archive_archived_entry_gets_active_on_request(Config) -> 473: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 474: % Alice sends a message to Bob and Bob archives it immediately 475: Body = <<"Hi Bob">>, 476: inbox_helper:send_msg(Alice, Bob, Body), 477: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 478: set_inbox_properties(Bob, Alice, [{archive, true}]), 479: % Then bob decides to recover the conversation 480: set_inbox_properties(Bob, Alice, [{archive, false}]), 481: % Then the conversation is in the active and not in the archive box 482: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 483: #{archive => false}), 484: inbox_helper:check_inbox(Bob, [], #{archive => true}) 485: end). 486: 487: archive_archived_entry_gets_active_for_the_receiver_on_new_message(Config) -> 488: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 489: % Alice sends a message to Bob and Bob archives it immediately 490: Body = <<"Hi Bob">>, 491: inbox_helper:send_msg(Alice, Bob, Body), 492: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 493: set_inbox_properties(Bob, Alice, [{archive, true}]), 494: % But then Alice keeps writing: 495: inbox_helper:send_msg(Alice, Bob, Body), 496: % Then the conversation is automatically in the active and not in the archive box 497: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = Alice, to = Bob, content = Body}], 498: #{archive => false}), 499: inbox_helper:check_inbox(Bob, [], #{archive => true}) 500: end). 501: 502: archive_archived_entry_gets_active_for_the_sender_on_new_message(Config) -> 503: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 504: % Alice sends a message to Bob and then she archives the conversation 505: Body = <<"Hi Bob">>, 506: inbox_helper:send_msg(Alice, Bob, Body), 507: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 508: set_inbox_properties(Alice, Bob, [{archive, true}]), 509: % But then Alice keeps writing 510: inbox_helper:send_msg(Alice, Bob, Body), 511: % Then the conversation is automatically in the active and not in the archive box 512: inbox_helper:check_inbox(Alice, [], #{archive => true}), 513: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = Alice, to = Bob, content = Body}], 514: #{archive => false}) 515: end). 516: 517: archive_active_unread_entry_gets_archived_and_still_unread(Config) -> 518: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 519: % Alice sends a message to Bob, but Bob archives it without reading it 520: Body = <<"Hi Bob">>, 521: inbox_helper:send_msg(Alice, Bob, Body), 522: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 523: set_inbox_properties(Bob, Alice, [{archive, true}]), 524: inbox_helper:check_inbox(Bob, [], #{archive => false}), 525: % Then Bob queries his archive and the conversation is there still unread 526: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}], 527: #{archive => true}) 528: end). 529: 530: archive_full_archive_can_be_fetched(Config) -> 531: archive_full_archive_can_be_fetched(Config, undefined). 532: archive_full_archive_can_be_fetched_queryid(Config) -> 533: archive_full_archive_can_be_fetched(Config, queryid). 534: 535: archive_full_archive_can_be_fetched(Config, QueryId) -> 536: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], fun(Alice, Bob, Kate, Mike) -> 537: % Several people write to Alice, and Alice reads and archives all of them 538: inbox_helper:check_inbox(Alice, [], #{archive => true}), 539: #{Alice := AliceConvs} = inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 540: inbox_helper:check_inbox(Alice, AliceConvs), 541: set_inbox_properties(Alice, Bob, [{archive, true}], inbox_helper:maybe_make_queryid(QueryId)), 542: set_inbox_properties(Alice, Kate, [{archive, true}], inbox_helper:maybe_make_queryid(QueryId)), 543: set_inbox_properties(Alice, Mike, [{archive, true}], inbox_helper:maybe_make_queryid(QueryId)), 544: % Then Alice queries her archive and the conversations are there and not in the active box 545: inbox_helper:check_inbox(Alice, [], #{archive => false}), 546: inbox_helper:check_inbox(Alice, AliceConvs, #{archive => true}) 547: end). 548: 549: % mute 550: mute_unmuted_entry_gets_muted(Config) -> 551: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 552: % Alice sends a message to Bob and Bob reads it 553: Body = <<"Hi Bob">>, 554: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 555: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 556: % Then Bob decides to mute the conversation 557: set_inbox_properties(Bob, Alice, [{mute, 24*?HOUR}]), 558: % Alice keeps writing 559: inbox_helper:send_msg(Alice, Bob, Body), 560: % Bob's inbox has an unread message, but it's marked as muted 561: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body, 562: verify = fun(_, _, Outer) -> muted_status(23*?HOUR, Outer) end}]) 563: end). 564: 565: mute_muted_entry_gets_unmuted(Config) -> 566: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 567: % Alice sends a message to Bob and Bob reads it 568: Body = <<"Hi Bob">>, 569: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 570: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 571: % Then Bob decides to mute the conversation 572: set_inbox_properties(Bob, Alice, [{mute, 24*?HOUR}]), 573: % Alice keeps writing 574: inbox_helper:send_msg(Alice, Bob, Body), 575: % And Bob unmutes it again because he's now interested 576: set_inbox_properties(Bob, Alice, [{mute, 0}]), 577: % Bob's inbox has an unread message immediately unmuted 578: inbox_helper:check_inbox( 579: Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body, 580: verify = fun(_, _, Outer) -> muted_status(unmuted, Outer) end}]) 581: end). 582: 583: mute_after_timestamp_gets_unmuted(Config) -> 584: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 585: % Alice sends a message to Bob and Bob reads it 586: Body = <<"Hi Bob">>, 587: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 588: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 589: % Then Bob decides to mute the conversation (for a very short 1 second for testing purposes) 590: set_inbox_properties(Bob, Alice, [{mute, 1}]), 591: % Alice keeps writing 592: inbox_helper:send_msg(Alice, Bob, Body), 593: % Inbox eventually returns unmuted 594: Fun = fun() -> 595: try inbox_helper:check_inbox( 596: Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body, 597: verify = fun(_, _, Outer) -> muted_status(unmuted, Outer) end}]), 598: ok 599: catch _:_ -> not_unmuted_yet 600: end 601: end, 602: mongoose_helper:wait_until(Fun, ok, #{name => verify_its_unmuted, sleep_time => 250}) 603: end). 604: 605: mute_muted_conv_restarts_timestamp(Config) -> 606: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 607: % Alice sends a message to Bob and Bob reads it 608: Body = <<"Hi Bob">>, 609: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 610: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 611: % Then Bob decides to mute the conversation 612: set_inbox_properties(Bob, Alice, [{mute, ?HOUR}]), 613: % Alice keeps writing 614: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 615: % Then Bob decides to mute the conversation for way longer 616: set_inbox_properties(Bob, Alice, [{mute, 24*?HOUR}]), 617: % Muted timestamp is way longer than before 618: inbox_helper:check_inbox( 619: Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body, 620: verify = fun(_, _, Outer) -> muted_status(23*?HOUR, Outer) end}]) 621: end). 622: 623: % other 624: returns_valid_properties_form(Config) -> 625: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 626: InboxConversationNS = inbox_helper:inbox_ns_conversation(), 627: escalus:send(Alice, escalus_stanza:iq_get(InboxConversationNS, [])), 628: ResIQ = escalus:wait_for_stanza(Alice), 629: #{field_count := 5} = Form = inbox_helper:parse_form_iq(ResIQ), 630: #{<<"FORM_TYPE">> := #{type := <<"hidden">>, value := InboxConversationNS}} = Form, 631: #{<<"archive">> := #{type := <<"boolean">>, value := <<"false">>}} = Form, 632: #{<<"box">> := #{type := <<"list-single">>, value := <<"all">>}} = Form, 633: #{<<"read">> := #{type := <<"boolean">>, value := <<"false">>}} = Form, 634: #{<<"mute">> := #{type := <<"text-single">>, value := <<"0">>}} = Form 635: end). 636: 637: properties_can_be_get(Config) -> 638: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 639: % Alice sends a message to Bob 640: Body = <<"Hi Bob">>, 641: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 642: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 643: % Then Bob can just query the properties of this entry at will 644: query_properties(Bob, Alice, [{archive, false}, {read, true}, {mute, 0}]) 645: end). 646: 647: properties_full_entry_can_be_get(Config) -> 648: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 649: % Alice sends a message to Bob 650: Body = <<"Hi Bob">>, 651: inbox_helper:send_and_mark_msg(Alice, Bob, Body), 652: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body}]), 653: % Then Bob can just query the properties of this entry at will 654: query_properties(Bob, Alice, [{archive, false}, {read, true}, {mute, 0}], full_entry) 655: end). 656: 657: properties_many_can_be_set(Config) -> 658: properties_many_can_be_set(Config, undefined). 659: properties_many_can_be_set_queryid(Config) -> 660: properties_many_can_be_set(Config, queryid). 661: 662: properties_many_can_be_set(Config, QueryId) -> 663: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 664: % Alice sends a message to Bob, and Bob sets a bunch of properties about it 665: Body = <<"Hi Bob">>, 666: inbox_helper:send_msg(Alice, Bob, Body), 667: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = Alice, to = Bob, content = Body}]), 668: set_inbox_properties(Bob, Alice, [{archive, true}, {read, true}, {mute, 24*?HOUR}], 669: inbox_helper:maybe_make_queryid(QueryId)), 670: % Then Bob queries his boxes and everything is as expected 671: inbox_helper:check_inbox(Bob, [], #{box => inbox}), 672: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = Alice, to = Bob, content = Body, 673: verify = fun(_, _, Outer) -> muted_status(23*?HOUR, Outer) 674: end}], #{box => archive}) 675: end). 676: 677: pagination_error_conditions(Config) -> 678: escalus:fresh_story(Config, [{alice, 1}], fun(Alice) -> 679: % We set start and end to return Convs with Mike, but using RSM we override that 680: TS = erlang:system_time(millisecond), 681: AliceJid = escalus_utils:get_short_jid(Alice), 682: IdNotDividing = <<(integer_to_binary(TS))/binary, (base64:encode(AliceJid))/binary>>, 683: verify_returns_error(Alice, #{box => inbox, 'after' => IdNotDividing}, <<"bad-request">>), 684: BadInt = <<(integer_to_binary(-10))/binary, "/", (base64:encode(AliceJid))/binary>>, 685: verify_returns_error(Alice, #{box => inbox, 'after' => BadInt}, <<"bad-request">>), 686: verify_returns_error(Alice, #{box => inbox, index => 10}, <<"feature-not-implemented">>) 687: end). 688: 689: pagination_has_priority_over_form_before_overrides_start(Config) -> 690: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 691: fun(Alice, Bob, Kate, Mike) -> 692: % Several people write to Alice 693: #{Alice := [_ConvWithMike, ConvWithKate, ConvWithBob] = AliceConvs} = 694: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 695: % Alice has some messages in her inbox 696: inbox_helper:check_inbox(Alice, AliceConvs), 697: % Extract all the helper values 698: TimeAfterBob = ConvWithBob#conv.time_after, 699: TimeAfterKate = ConvWithKate#conv.time_after, 700: % We set start and end to return Convs with Kate, 701: % but override with Rsm to get Kate and Bob 702: Params = #{box => inbox, start => TimeAfterBob, 'end' => TimeAfterKate, before => <<>>}, 703: inbox_helper:check_inbox(Alice, [ConvWithKate, ConvWithBob], Params) 704: end). 705: 706: pagination_has_priority_over_form_after_overrides_end(Config) -> 707: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 708: fun(Alice, Bob, Kate, Mike) -> 709: % Several people write to Alice 710: #{Alice := [ConvWithMike, ConvWithKate, ConvWithBob] = AliceConvs} = 711: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 712: % Alice has some messages in her inbox 713: inbox_helper:check_inbox(Alice, AliceConvs), 714: % Extract all the helper values 715: TimeAfterBob = ConvWithBob#conv.time_after, 716: TimeAfterKate = ConvWithKate#conv.time_after, 717: % We set start and end to return Convs with Kate, 718: % but override with Rsm to get Mike and Kate 719: Params = #{box => inbox, start => TimeAfterBob, 'end' => TimeAfterKate, 'after' => <<>>}, 720: inbox_helper:check_inbox(Alice, [ConvWithMike, ConvWithKate], Params) 721: end). 722: 723: can_paginate_forwards(Config) -> 724: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 725: fun(Alice, Bob, Kate, Mike) -> 726: % Several people write to Alice 727: #{Alice := [ConvWithMike, ConvWithKate, ConvWithBob] = AliceConvs} = 728: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 729: % Alice has some messages in her inbox 730: inbox_helper:check_inbox(Alice, AliceConvs), 731: Params1 = #{box => inbox, limit => 1}, 732: #{respond_iq := Iq} = inbox_helper:check_inbox(Alice, [ConvWithMike], Params1), 733: AfterPrevious = exml_query:path(Iq, [{element_with_ns, <<"fin">>, inbox_helper:inbox_ns()}, 734: {element, <<"set">>}, {element, <<"last">>}, cdata]), 735: Params2 = #{box => inbox, 'after' => AfterPrevious, limit => 2}, 736: inbox_helper:check_inbox(Alice, [ConvWithKate, ConvWithBob], Params2) 737: end). 738: 739: can_paginate_backwards(Config) -> 740: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 741: fun(Alice, Bob, Kate, Mike) -> 742: % Several people write to Alice 743: #{Alice := AliceConvs} = 744: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 745: % Alice has some messages in her inbox 746: inbox_helper:check_inbox(Alice, AliceConvs), 747: % Extract all the helper values 748: ConvWithBob = lists:keyfind(Bob, #conv.to, AliceConvs), 749: ConvWithKate = lists:keyfind(Kate, #conv.to, AliceConvs), 750: ConvWithMike = lists:keyfind(Mike, #conv.to, AliceConvs), 751: Params1 = #{box => inbox, limit => 1, before => <<>>}, 752: #{respond_iq := Iq} = inbox_helper:check_inbox(Alice, [ConvWithBob], Params1), 753: BeforeLast = exml_query:path(Iq, [{element_with_ns, <<"fin">>, inbox_helper:inbox_ns()}, 754: {element, <<"set">>}, {element, <<"first">>}, cdata]), 755: Params2 = #{box => inbox, before => BeforeLast}, 756: inbox_helper:check_inbox(Alice, [ConvWithMike, ConvWithKate], Params2) 757: end). 758: 759: max_queries_can_be_limited(Config) -> 760: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 761: fun(Alice, Bob, Kate, Mike) -> 762: % Several people write to Alice 763: #{Alice := AliceConvs} = 764: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 765: % Alice has some messages in her inbox 766: inbox_helper:check_inbox(Alice, AliceConvs), 767: % Then Alice queries her inbox setting a limit to only one conversation, 768: % and she gets the newest one 769: ConvWithMike = lists:keyfind(Mike, #conv.to, AliceConvs), 770: Inbox1 = inbox_helper:check_inbox(Alice, [ConvWithMike], #{limit => 1, box => inbox}), 771: verify_rsm(Inbox1), 772: % And a limit to two also works fine 773: ConvWithKate = lists:keyfind(Kate, #conv.to, AliceConvs), 774: Inbox2 = inbox_helper:check_inbox(Alice, [ConvWithMike, ConvWithKate], #{limit => 2, box => inbox}), 775: verify_rsm(Inbox2) 776: end). 777: 778: max_queries_can_fetch_ahead(Config) -> 779: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}, {kate, 1}, {mike, 1}], 780: fun(Alice, Bob, Kate, Mike) -> 781: #{Alice := AliceConvs} = 782: inbox_helper:given_conversations_between(Alice, [Bob, Kate, Mike]), 783: ConvWithBob = lists:keyfind(Bob, #conv.to, AliceConvs), 784: ConvWithKate = lists:keyfind(Kate, #conv.to, AliceConvs), 785: % ConvWithMike = lists:keyfind(Mike, #conv.to, AliceConvs), 786: TimeAfterKate = ConvWithKate#conv.time_after, 787: inbox_helper:check_inbox(Alice, [ConvWithKate, ConvWithBob], 788: #{limit => 2, 'end' => TimeAfterKate, box => inbox}) 789: end). 790: 791: timestamp_is_not_reset_with_setting_properties(Config) -> 792: escalus:fresh_story(Config, [{alice, 1}, {bob, 1}], fun(Alice, Bob) -> 793: % Alice sends a message to Bob 794: inbox_helper:send_msg(Alice, Bob), 795: %% We capture the timestamp 796: #{respond_messages := [Item1]} = inbox_helper:get_inbox(Bob, #{count => 1}), 797: TStamp1 = inbox_helper:timestamp_from_item(Item1), 798: % Bob sets a bunch of properties 799: set_inbox_properties(Bob, Alice, [{read, true}, {mute, 24*?HOUR}]), 800: % Bob gets the inbox again, and timestamp should be the same 801: #{respond_messages := [Item2]} = inbox_helper:get_inbox(Bob, #{count => 1}), 802: TStamp2 = inbox_helper:timestamp_from_item(Item2), 803: ?assertEqual(TStamp1, TStamp2) 804: end). 805: 806: % muclight 807: groupchat_setunread_stanza_sets_inbox(Config) -> 808: escalus:story(Config, [{alice, 1}, {bob, 1}, {kate, 1}], fun(Alice, Bob, Kate) -> 809: %%% DATA 810: MsgBody = <<"marker time!">>, 811: AliceJid = inbox_helper:to_bare_lower(Alice), 812: BobJid = inbox_helper:to_bare_lower(Bob), 813: KateJid = inbox_helper:to_bare_lower(Kate), 814: RoomJid = muc_light_helper:room_bin_jid(?ROOM_MARKERS_RESET), 815: AliceRoomJid = <<RoomJid/binary,"/", AliceJid/binary>>, 816: %%% WHEN A MESSAGE IS SENT (two times the same message) 817: MsgStanza1 = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomJid, MsgBody), <<"id-1">>), 818: MsgStanza2 = escalus_stanza:set_id(escalus_stanza:groupchat_to(RoomJid, MsgBody), <<"id-2">>), 819: escalus:send(Alice, MsgStanza1), 820: inbox_helper:wait_for_groupchat_msg([Alice, Bob, Kate]), 821: escalus:send(Alice, MsgStanza2), 822: inbox_helper:wait_for_groupchat_msg([Alice, Bob, Kate]), 823: % verify that Bob has the message on inbox, reset it, and verify is still there but read 824: inbox_helper:check_inbox(Bob, [#conv{unread = 2, from = AliceRoomJid, to = BobJid, content = MsgBody}]), 825: set_inbox_properties(Bob, RoomJid, [{read, true}]), 826: inbox_helper:check_inbox(Bob, [#conv{unread = 0, from = AliceRoomJid, to = BobJid, content = MsgBody}]), 827: % Bob sets the inbox as unread again and has so in his inbox 828: set_inbox_properties(Bob, RoomJid, [{read, false}]), 829: inbox_helper:check_inbox(Bob, [#conv{unread = 1, from = AliceRoomJid, to = BobJid, content = MsgBody}]), 830: %% Alice has 0 unread messages because she was the sender 831: inbox_helper:check_inbox(Alice, [#conv{unread = 0, from = AliceRoomJid, to = AliceJid, content = MsgBody}]), 832: %% Kate still has unread messages, and setting the entry as unread keeps the count to two 833: set_inbox_properties(Kate, RoomJid, [{read, false}]), 834: inbox_helper:check_inbox(Kate, [#conv{unread = 2, from = AliceRoomJid, to = KateJid, content = MsgBody}]), 835: %% And nobody received any other stanza 836: inbox_helper:assert_has_no_stanzas([Alice, Bob, Kate]) 837: end). 838: 839: 840: %%-------------------------------------------------------------------- 841: %% Helpers 842: %%-------------------------------------------------------------------- 843: 844: -type maybe_client() :: undefined | escalus:client(). 845: 846: verify_rsm(#{respond_iq := Iq}) -> 847: Set = exml_query:path(Iq, [{element_with_ns, <<"fin">>, inbox_helper:inbox_ns()}, 848: {element_with_ns, <<"set">>, ?NS_RSM}]), 849: ?assertNotEqual(undefined, exml_query:subelement(Set, <<"first">>)), 850: ?assertNotEqual(undefined, exml_query:subelement(Set, <<"last">>)). 851: 852: to_int(Bin) -> 853: calendar:rfc3339_to_system_time(binary_to_list(Bin), [{unit, microsecond}]). 854: 855: -spec query_properties(escalus:client(), escalus:client(), proplists:proplist()) -> [exml:element()]. 856: query_properties(From, To, Expected) -> 857: query_properties(From, To, Expected, none). 858: 859: -spec query_properties(escalus:client(), escalus:client(), proplists:proplist(), none | full_entry) -> 860: [exml:element()]. 861: query_properties(From, To, Expected, FullEntry) -> 862: Stanza = make_inbox_get_properties(To, FullEntry), 863: escalus:send(From, Stanza), 864: Result = escalus:wait_for_stanza(From), 865: ?assert(escalus_pred:is_iq_result(Stanza, Result)), 866: [Props] = exml_query:subelements(Result, <<"query">>), 867: ?assertEqual(inbox_helper:inbox_ns_conversation(), exml_query:attr(Props, <<"xmlns">>)), 868: maybe_assert_full_entry(Props, FullEntry), 869: lists:foreach(fun({Key, Val}) -> assert_property(Props, Key, Val) end, Expected). 870: 871: maybe_assert_full_entry(_, none) -> 872: ok; 873: maybe_assert_full_entry(Props, full_entry) -> 874: ?assertNotEqual(undefined, exml_query:path(Props, [{element, <<"forwarded">>}])). 875: 876: -spec make_inbox_get_properties(escalus:client(), boolean()) -> exml:element(). 877: make_inbox_get_properties(To, none) -> 878: Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), jid_attr(To), []), 879: escalus_stanza:iq(<<"get">>, [Query]); 880: make_inbox_get_properties(To, full_entry) -> 881: Attrs = [{<<"complete">>, <<"true">>} | jid_attr(To)], 882: Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), Attrs, []), 883: escalus_stanza:iq(<<"get">>, [Query]). 884: 885: -spec set_inbox_properties(escalus:client(), escalus:client(), proplists:proplist()) -> ok. 886: set_inbox_properties(From, To, Properties) -> 887: set_inbox_properties(From, To, Properties, #{}). 888: 889: -spec set_inbox_properties(escalus:client(), escalus:client(), proplists:proplist(), map()) -> ok. 890: set_inbox_properties(From, To, Properties, QueryOpts) -> 891: Stanza = make_inbox_iq_request_with_query_id(To, Properties, QueryOpts), 892: escalus:send(From, Stanza), 893: check_message_with_properties(From, Stanza, Properties, QueryOpts), 894: check_iq_result_for_property(From, Stanza). 895: 896: -spec check_message_with_properties(escalus:client(), exml:element(), proplists:proplist(), map()) -> ok. 897: check_message_with_properties(From, Stanza, Properties, QueryOpts) -> 898: Message = escalus:wait_for_stanza(From), 899: ?assert(escalus_pred:is_message(Message)), 900: ?assert(has_same_id(Stanza, Message)), 901: [X] = exml_query:subelements(Message, <<"x">>), 902: ?assertEqual(inbox_helper:inbox_ns_conversation(), exml_query:attr(X, <<"xmlns">>)), 903: inbox_helper:maybe_check_queryid(X, QueryOpts), 904: % ?assertEqual(QueryId, exml_query:attr(X, <<"queryid">>)), 905: lists:foreach(fun({Key, Val}) -> assert_property(X, Key, Val) end, Properties). 906: 907: -spec check_iq_result_for_property(escalus:client(), exml:element()) -> ok. 908: check_iq_result_for_property(From, Stanza) -> 909: Result = escalus:wait_for_stanza(From), 910: ?assert(escalus_pred:is_iq_result(Stanza, Result)). 911: 912: -spec make_inbox_iq_request(maybe_client(), atom(), atom()) -> exml:element(). 913: make_inbox_iq_request(ToClient, Key, Value) -> 914: make_inbox_iq_request(ToClient, [{Key, Value}]). 915: 916: -spec make_inbox_iq_request(maybe_client(), proplists:proplist()) -> exml:element(). 917: make_inbox_iq_request(ToClient, Properties) when is_list(Properties) -> 918: make_inbox_iq_request_with_query_id(ToClient, Properties, #{}). 919: 920: -spec make_inbox_iq_request_with_query_id(maybe_client(), proplists:proplist(), map()) -> exml:element(). 921: make_inbox_iq_request_with_query_id(ToClient, Properties, QueryId) -> 922: JidAttr = jid_attr(ToClient), 923: IqAttr = id_attr(QueryId), 924: Children = props_to_children(Properties), 925: Query = escalus_stanza:query_el(inbox_helper:inbox_ns_conversation(), JidAttr ++ IqAttr, Children), 926: inbox_iq(QueryId, Query). 927: 928: inbox_iq(#{iq_id := IqId}, Query) -> 929: IQ = escalus_stanza:iq(<<"set">>, [Query]), 930: escalus_stanza:set_id(IQ, IqId); 931: inbox_iq(_, Query) -> 932: escalus_stanza:iq(<<"set">>, [Query]). 933: 934: assert_invalid_request(From, Stanza, Value) -> 935: inbox_helper:assert_invalid_form(From, Stanza, Value, Value). 936: 937: -spec jid_attr(maybe_client()) -> proplists:proplist(). 938: jid_attr(undefined) -> []; 939: jid_attr(Client) -> [{<<"jid">>, escalus_utils:get_short_jid(Client)}]. 940: 941: id_attr(#{queryid := QueryId}) -> [{<<"queryid">>, QueryId}]; 942: id_attr(_) -> []. 943: 944: props_to_children(L) -> props_to_children(L, []). 945: props_to_children([], Acc) -> Acc; 946: props_to_children([{undefined, _} | Rest], Acc) -> 947: props_to_children(Rest, Acc); 948: props_to_children([{Key, undefined} | Rest], Acc) -> 949: props_to_children(Rest, [#xmlel{name = Key} | Acc]); 950: props_to_children([{Key, Value} | Rest], Acc) -> 951: props_to_children(Rest, 952: [#xmlel{name = to_bin(Key), children = [#xmlcdata{content = to_bin(Value)}]} | Acc]). 953: 954: assert_property(X, read, Val) -> 955: ?assertEqual(to_bin(Val), exml_query:path(X, [{element, <<"read">>}, cdata])); 956: assert_property(X, box, Val) -> 957: ?assertEqual(to_bin(Val), exml_query:path(X, [{element, <<"box">>}, cdata])); 958: assert_property(X, archive, Val) -> 959: ?assertEqual(to_bin(Val), exml_query:path(X, [{element, <<"archive">>}, cdata])); 960: assert_property(X, mute, 0) -> 961: ?assertEqual(0, binary_to_integer(exml_query:path(X, [{element, <<"mute">>}, cdata]))); 962: assert_property(X, mute, _) -> 963: Cal = binary_to_list(exml_query:path(X, [{element, <<"mute">>}, cdata])), 964: calendar:rfc3339_to_system_time(Cal, [{unit, microsecond}]). 965: 966: -spec to_bin(term()) -> binary(). 967: to_bin(Value) when is_binary(Value) -> Value; 968: to_bin(Value) when is_atom(Value) -> atom_to_binary(Value, utf8); 969: to_bin(Value) when is_integer(Value) -> integer_to_binary(Value). 970: 971: -spec has_same_id(exml:element(), exml:element()) -> boolean(). 972: has_same_id(OrigStanza, Stanza) -> 973: OrigId = exml_query:attr(OrigStanza, <<"id">>), 974: Id = exml_query:attr(Stanza, <<"id">>), 975: OrigId =:= Id. 976: 977: muted_status(unmuted, Outer) -> 978: Res = exml_query:path(Outer, [{element, <<"result">>}, {element, <<"mute">>}, cdata]), 979: ?assertEqual(<<"0">>, Res); 980: muted_status(MutedOrUnmuted, Outer) -> 981: GivenRfcTimestamp = exml_query:path(Outer, [{element, <<"result">>}, {element, <<"mute">>}, cdata]), 982: GivenMutedUntil = calendar:rfc3339_to_system_time(binary_to_list(GivenRfcTimestamp), [{offset, "Z"}, {unit, microsecond}]), 983: Now = erlang:system_time(microsecond), 984: case MutedOrUnmuted of 985: unmuted -> 986: ?assert(Now > GivenMutedUntil); 987: MutedDiff -> 988: Diff = erlang:convert_time_unit(MutedDiff, second, microsecond), 989: ?assert(Now + Diff < GivenMutedUntil) 990: end. 991: 992: verify_returns_error(User, Params, Error) -> 993: Stanza = inbox_helper:make_inbox_stanza(Params), 994: escalus:send(User, Stanza), 995: [ResIQ] = escalus:wait_for_stanzas(User, 1), 996: escalus:assert(is_iq_error, [Stanza], ResIQ), 997: Type = exml_query:path(ResIQ, [{element, <<"error">>}, {element, Error}]), 998: ?assertNotEqual(undefined, Type).