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