1: -module(graphql_stanza_SUITE).
    2: 
    3: -include_lib("common_test/include/ct.hrl").
    4: -include_lib("eunit/include/eunit.hrl").
    5: -include_lib("exml/include/exml.hrl").
    6: 
    7: -compile([export_all, nowarn_export_all]).
    8: 
    9: -import(distributed_helper, [require_rpc_nodes/1]).
   10: -import(graphql_helper, [execute_auth/2]).
   11: 
   12: suite() ->
   13:     require_rpc_nodes([mim]) ++ escalus:suite().
   14: 
   15: all() ->
   16:     [{group, admin_stanza_category},
   17:      {group, user_stanza_caregory}].
   18: 
   19: groups() ->
   20:     [{admin_stanza_category, [parallel], admin_stanza_category()},
   21:      {user_stanza_caregory, [parallel], user_stanza_caregory()}].
   22: 
   23: admin_stanza_category() ->
   24:     [admin_send_message,
   25:      admin_send_message_to_unparsable_jid,
   26:      admin_send_message_headline,
   27:      admin_send_stanza,
   28:      admin_send_unparsable_stanza,
   29:      admin_send_stanza_from_unknown_user,
   30:      admin_send_stanza_from_unknown_domain]
   31:     ++ admin_get_last_messages_cases().
   32: 
   33: admin_get_last_messages_cases() ->
   34:     [admin_get_last_messages,
   35:      admin_get_last_messages_for_unknown_user,
   36:      admin_get_last_messages_with,
   37:      admin_get_last_messages_limit,
   38:      admin_get_last_messages_limit_enforced,
   39:      admin_get_last_messages_before].
   40: 
   41: user_stanza_caregory() ->
   42:     [user_send_message,
   43:      user_send_message_without_from,
   44:      user_send_message_with_spoofed_from,
   45:      user_send_message_headline,
   46:      user_send_message_headline_with_spoofed_from,
   47:      user_send_stanza,
   48:      user_send_stanza_with_spoofed_from]
   49:     ++ user_get_last_messages_cases().
   50: 
   51: user_get_last_messages_cases() ->
   52:     [user_get_last_messages].
   53: 
   54: init_per_suite(Config) ->
   55:     Config1 = escalus:init_per_suite(Config),
   56:     dynamic_modules:save_modules(domain_helper:host_type(), Config1).
   57: 
   58: end_per_suite(Config) ->
   59:     escalus_fresh:clean(),
   60:     dynamic_modules:restore_modules(Config),
   61:     escalus:end_per_suite(Config).
   62: 
   63: init_per_group(admin_stanza_category, Config) ->
   64:     Config2 = graphql_helper:init_admin_handler(Config),
   65:     init_mam(Config2);
   66: init_per_group(user_stanza_caregory, Config) ->
   67:     init_mam(Config).
   68: 
   69: end_per_group(_, _Config) ->
   70:     ok.
   71: 
   72: init_per_testcase(CaseName, Config) ->
   73:     HasMam = proplists:get_value(has_mam, Config, false),
   74:     MamOnly = lists:member(CaseName,
   75:                            user_get_last_messages_cases()
   76:                            ++ admin_get_last_messages_cases()),
   77:     case {HasMam, MamOnly} of
   78:         {false, true} ->
   79:             {skip, no_mam};
   80:         _ ->
   81:             escalus:init_per_testcase(CaseName, Config)
   82:     end.
   83: 
   84: end_per_testcase(CaseName, Config) ->
   85:     escalus:end_per_testcase(CaseName, Config).
   86: 
   87: init_mam(Config) when is_list(Config) ->
   88:     case mam_helper:backend() of
   89:         disabled ->
   90:             Config;
   91:         Backend ->
   92:             Mods = [{mod_mam_meta, mam_helper:config_opts(#{backend => Backend, pm => #{}})}],
   93:             dynamic_modules:ensure_modules(domain_helper:host_type(), Mods),
   94:             [{has_mam, true}|Config]
   95:     end;
   96: init_mam(Other) ->
   97:     Other.
   98: 
   99: %% Test Cases
  100: 
  101: admin_send_message(Config) ->
  102:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  103:                                     fun admin_send_message_story/3).
  104: 
  105: admin_send_message_story(Config, Alice, Bob) ->
  106:     Body = <<"Hi!">>,
  107:     Vars = #{from => escalus_client:full_jid(Alice),
  108:              to => escalus_client:short_jid(Bob),
  109:              body => Body},
  110:     Res = ok_result(<<"stanza">>, <<"sendMessage">>,
  111:                     execute_send_message(Vars, Config)),
  112:     #{<<"id">> := MamID} = Res,
  113:     assert_not_empty(MamID, Config).
  114: 
  115: user_send_message(Config) ->
  116:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  117:                                     fun user_send_message_story/3).
  118: 
  119: user_send_message_story(Config, Alice, Bob) ->
  120:     Body = <<"Hi!">>,
  121:     Vars = #{from => escalus_client:full_jid(Alice),
  122:              to => escalus_client:short_jid(Bob),
  123:              body => Body},
  124:     Res = ok_result(<<"stanza">>, <<"sendMessage">>,
  125:                     execute_user_send_message(Alice, Vars, Config)),
  126:     #{<<"id">> := MamID} = Res,
  127:     assert_not_empty(MamID, Config),
  128:     escalus:assert(is_message, escalus:wait_for_stanza(Bob)).
  129: 
  130: user_send_message_without_from(Config) ->
  131:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  132:                                     fun user_send_message_without_from_story/3).
  133: 
  134: user_send_message_without_from_story(Config, Alice, Bob) ->
  135:     Body = <<"Hi!">>,
  136:     Vars = #{to => escalus_client:short_jid(Bob),
  137:              body => Body},
  138:     Res = ok_result(<<"stanza">>, <<"sendMessage">>,
  139:                     execute_user_send_message(Alice, Vars, Config)),
  140:     #{<<"id">> := MamID} = Res,
  141:     assert_not_empty(MamID, Config),
  142:     escalus:assert(is_message, escalus:wait_for_stanza(Bob)).
  143: 
  144: user_send_message_with_spoofed_from(Config) ->
  145:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  146:                                     fun user_send_message_with_spoofed_from_story/3).
  147: 
  148: user_send_message_with_spoofed_from_story(Config, Alice, Bob) ->
  149:     Body = <<"Hi!">>,
  150:     Vars = #{from => escalus_client:short_jid(Bob),
  151:              to => escalus_client:short_jid(Bob),
  152:              body => Body},
  153:     Res = execute_user_send_message(Alice, Vars, Config),
  154:     spoofed_error(<<"sendMessage">>, Res).
  155: 
  156: admin_send_message_to_unparsable_jid(Config) ->
  157:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  158:                                     fun admin_send_message_to_unparsable_jid_story/2).
  159: 
  160: admin_send_message_to_unparsable_jid_story(Config, Alice) ->
  161:     Body = <<"Hi!">>,
  162:     Vars = #{from => escalus_client:full_jid(Alice),
  163:              to => <<"test@">>,
  164:              body => Body},
  165:     Res = execute_send_message(Vars, Config),
  166:     {{<<"400">>, <<"Bad Request">>}, #{<<"errors">> := Errors}} = Res,
  167:     [#{<<"extensions">> := #{<<"code">> := <<"input_coercion">>},
  168:        <<"message">> := ErrMsg, <<"path">> := ErrPath}] = Errors,
  169:     ?assertEqual([<<"M1">>, <<"to">>], ErrPath),
  170:     ?assertEqual(<<"Input coercion failed for type JID with value "
  171:                    "<<\"test@\">>. The reason it failed is: failed_to_parse_jid">>,
  172:                  ErrMsg).
  173: 
  174: admin_send_message_headline(Config) ->
  175:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  176:                                     fun admin_send_message_headline_story/3).
  177: 
  178: admin_send_message_headline_story(Config, Alice, Bob) ->
  179:     Subject = <<"Welcome">>,
  180:     Body = <<"Hi!">>,
  181:     Vars = #{from => escalus_client:full_jid(Alice),
  182:              to => escalus_client:short_jid(Bob),
  183:              subject => Subject, body => Body},
  184:     Res = ok_result(<<"stanza">>, <<"sendMessageHeadLine">>,
  185:                     execute_send_message_headline(Vars, Config)),
  186:     #{<<"id">> := MamID} = Res,
  187:     %% Headlines are not stored in MAM
  188:     <<>> = MamID.
  189: 
  190: user_send_message_headline(Config) ->
  191:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  192:                                     fun user_send_message_headline_story/3).
  193: 
  194: user_send_message_headline_story(Config, Alice, Bob) ->
  195:     Subject = <<"Welcome">>,
  196:     Body = <<"Hi!">>,
  197:     Vars = #{from => escalus_client:full_jid(Alice),
  198:              to => escalus_client:short_jid(Bob),
  199:              subject => Subject, body => Body},
  200:     Res = ok_result(<<"stanza">>, <<"sendMessageHeadLine">>,
  201:                     execute_user_send_message_headline(Alice, Vars, Config)),
  202:     #{<<"id">> := MamID} = Res,
  203:     %% Headlines are not stored in MAM
  204:     <<>> = MamID,
  205:     escalus:assert(is_message, escalus:wait_for_stanza(Bob)).
  206: 
  207: user_send_message_headline_with_spoofed_from(Config) ->
  208:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  209:                                     fun user_send_message_headline_with_spoofed_from_story/3).
  210: 
  211: user_send_message_headline_with_spoofed_from_story(Config, Alice, Bob) ->
  212:     Subject = <<"Welcome">>,
  213:     Body = <<"Hi!">>,
  214:     Vars = #{from => escalus_client:short_jid(Bob),
  215:              to => escalus_client:short_jid(Bob),
  216:              subject => Subject, body => Body},
  217:     Res = execute_user_send_message_headline(Alice, Vars, Config),
  218:     spoofed_error(<<"sendMessageHeadLine">>, Res).
  219: 
  220: admin_send_stanza(Config) ->
  221:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  222:                                     fun admin_send_stanza_story/3).
  223: 
  224: admin_send_stanza_story(Config, Alice, Bob) ->
  225:     Body = <<"Hi!">>,
  226:     Stanza = escalus_stanza:from(escalus_stanza:chat_to_short_jid(Bob, Body), Alice),
  227:     Vars = #{stanza => exml:to_binary(Stanza)},
  228:     Res = ok_result(<<"stanza">>, <<"sendStanza">>, execute_send_stanza(Vars, Config)),
  229:     #{<<"id">> := MamID} = Res,
  230:     assert_not_empty(MamID, Config).
  231: 
  232: user_send_stanza(Config) ->
  233:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  234:                                     fun user_send_stanza_story/3).
  235: 
  236: user_send_stanza_story(Config, Alice, Bob) ->
  237:     Body = <<"Hi!">>,
  238:     Stanza = escalus_stanza:from(escalus_stanza:chat_to_short_jid(Bob, Body), Alice),
  239:     Vars = #{stanza => exml:to_binary(Stanza)},
  240:     Res = ok_result(<<"stanza">>, <<"sendStanza">>,
  241:                     execute_user_send_stanza(Alice, Vars, Config)),
  242:     #{<<"id">> := MamID} = Res,
  243:     assert_not_empty(MamID, Config).
  244: 
  245: user_send_stanza_with_spoofed_from(Config) ->
  246:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  247:                                     fun user_send_stanza_with_spoofed_from_story/3).
  248: 
  249: user_send_stanza_with_spoofed_from_story(Config, Alice, Bob) ->
  250:     Body = <<"Hi!">>,
  251:     Stanza = escalus_stanza:from(escalus_stanza:chat_to_short_jid(Bob, Body), Bob),
  252:     Vars = #{stanza => exml:to_binary(Stanza)},
  253:     Res = execute_user_send_stanza(Alice, Vars, Config),
  254:     spoofed_error(<<"sendStanza">>, Res).
  255: 
  256: admin_send_unparsable_stanza(Config) ->
  257:     Vars = #{stanza => <<"<test">>},
  258:     Res = execute_send_stanza(Vars, Config),
  259:     {{<<"400">>, <<"Bad Request">>}, #{<<"errors">> := Errors}} = Res,
  260:     [#{<<"extensions">> := #{<<"code">> := <<"input_coercion">>},
  261:        <<"message">> := ErrMsg, <<"path">> := ErrPath}] = Errors,
  262:     ?assertEqual(<<"Input coercion failed for type Stanza with value <<\"<test\">>. "
  263:                    "The reason it failed is: \"expected >\"">>, ErrMsg),
  264:     ?assertEqual([<<"M1">>, <<"stanza">>], ErrPath).
  265: 
  266: admin_send_stanza_from_unknown_user(Config) ->
  267:     escalus:fresh_story_with_config(Config, [{bob, 1}],
  268:                                     fun admin_send_stanza_from_unknown_user_story/2).
  269: 
  270: admin_send_stanza_from_unknown_user_story(Config, Bob) ->
  271:     Body = <<"Hi!">>,
  272:     Server = escalus_client:server(Bob),
  273:     From = <<"YeeeAH@", Server/binary>>,
  274:     LFrom = jid:str_tolower(From),
  275:     Stanza = escalus_stanza:from(escalus_stanza:chat_to_short_jid(Bob, Body), From),
  276:     Vars = #{stanza => exml:to_binary(Stanza)},
  277:     Res = execute_send_stanza(Vars, Config),
  278:     {{<<"200">>,<<"OK">>},
  279:          #{<<"data">> := #{<<"stanza">> := #{<<"sendStanza">> := null}},
  280:            <<"errors">> := Errors}} = Res,
  281:     [#{<<"extensions">> := #{<<"code">> := <<"unknown_user">>,
  282:                              <<"jid">> := LFrom},
  283:        <<"message">> := ErrMsg, <<"path">> := ErrPath}] = Errors,
  284:     ?assertEqual(<<"Given user does not exist">>, ErrMsg),
  285:     ?assertEqual([<<"stanza">>, <<"sendStanza">>], ErrPath).
  286: 
  287: admin_send_stanza_from_unknown_domain(Config) ->
  288:     escalus:fresh_story_with_config(Config, [{bob, 1}],
  289:                                     fun admin_send_stanza_from_unknown_domain_story/2).
  290: 
  291: admin_send_stanza_from_unknown_domain_story(Config, Bob) ->
  292:     Body = <<"Hi!">>,
  293:     From = <<"YeeeAH@oopsie">>,
  294:     Stanza = escalus_stanza:from(escalus_stanza:chat_to_short_jid(Bob, Body), From),
  295:     Vars = #{stanza => exml:to_binary(Stanza)},
  296:     Res = execute_send_stanza(Vars, Config),
  297:     {{<<"200">>, <<"OK">>},
  298:      #{<<"data">> := #{<<"stanza">> := #{<<"sendStanza">> := null}},
  299:        <<"errors">> := Errors}} = Res,
  300:     [#{<<"extensions">> := #{<<"code">> := <<"unknown_domain">>,
  301:                              <<"domain">> := <<"oopsie">>},
  302:        <<"message">> := ErrMsg, <<"path">> := ErrPath}] = Errors,
  303:     ?assertEqual([<<"stanza">>, <<"sendStanza">>], ErrPath),
  304:     ?assertEqual(<<"Given domain does not exist">>, ErrMsg).
  305: 
  306: admin_get_last_messages(Config) ->
  307:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  308:                                     fun admin_get_last_messages_story/3).
  309: 
  310: admin_get_last_messages_story(Config, Alice, Bob) ->
  311:     admin_send_message_story(Config, Alice, Bob),
  312:     mam_helper:wait_for_archive_size(Alice, 1),
  313:     mam_helper:wait_for_archive_size(Bob, 1),
  314:     Vars1 = #{caller => escalus_client:full_jid(Alice)},
  315:     Vars2 = #{caller => escalus_client:full_jid(Bob)},
  316:     Res1 = ok_result(<<"stanza">>, <<"getLastMessages">>,
  317:                      execute_get_last_messages(Vars1, Config)),
  318:     #{<<"stanzas">> := [M1], <<"limit">> := 50} = Res1,
  319:     check_stanza_map(M1, Alice),
  320:     Res2 = ok_result(<<"stanza">>, <<"getLastMessages">>,
  321:                      execute_get_last_messages(Vars2, Config)),
  322:     #{<<"stanzas">> := [M2], <<"limit">> := 50} = Res2,
  323:     check_stanza_map(M2, Alice).
  324: 
  325: user_get_last_messages(Config) ->
  326:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  327:                                     fun user_get_last_messages_story/3).
  328: 
  329: user_get_last_messages_story(Config, Alice, Bob) ->
  330:     user_send_message_story(Config, Alice, Bob),
  331:     mam_helper:wait_for_archive_size(Alice, 1),
  332:     mam_helper:wait_for_archive_size(Bob, 1),
  333:     Vars1 = #{caller => escalus_client:full_jid(Alice)},
  334:     Vars2 = #{caller => escalus_client:full_jid(Bob)},
  335:     Res1 = ok_result(<<"stanza">>, <<"getLastMessages">>,
  336:                      execute_user_get_last_messages(Alice, Vars1, Config)),
  337:     #{<<"stanzas">> := [M1], <<"limit">> := 50} = Res1,
  338:     check_stanza_map(M1, Alice),
  339:     Res2 = ok_result(<<"stanza">>, <<"getLastMessages">>,
  340:                      execute_user_get_last_messages(Alice, Vars2, Config)),
  341:     #{<<"stanzas">> := [M2], <<"limit">> := 50} = Res2,
  342:     check_stanza_map(M2, Alice).
  343: 
  344: admin_get_last_messages_for_unknown_user(Config) ->
  345:     Domain = domain_helper:domain(),
  346:     Jid = <<"maybemaybebutnot@", Domain/binary>>,
  347:     Vars = #{caller => Jid},
  348:     Res = execute_get_last_messages(Vars, Config),
  349:     {{<<"200">>, <<"OK">>},
  350:      #{<<"data">> := #{<<"stanza">> := #{<<"getLastMessages">> := null}},
  351:        <<"errors">> := Errors}} = Res,
  352:     [#{<<"extensions">> := #{<<"code">> := <<"unknown_user">>,
  353:                              <<"jid">> := Jid},
  354:        <<"message">> := ErrMsg, <<"path">> := ErrPath}] = Errors,
  355:     ?assertEqual([<<"stanza">>, <<"getLastMessages">>], ErrPath),
  356:     ?assertEqual(<<"Given user does not exist">>, ErrMsg).
  357: 
  358: admin_get_last_messages_with(Config) ->
  359:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}, {kate, 1}],
  360:                                     fun admin_get_last_messages_with_story/4).
  361: 
  362: admin_get_last_messages_with_story(Config, Alice, Bob, Kate) ->
  363:     admin_send_message_story(Config, Alice, Bob),
  364:     mam_helper:wait_for_archive_size(Alice, 1),
  365:     admin_send_message_story(Config, Kate, Alice),
  366:     mam_helper:wait_for_archive_size(Alice, 2),
  367:     Vars = #{caller => escalus_client:full_jid(Alice),
  368:              with => escalus_client:short_jid(Bob)},
  369:     Res = ok_result(<<"stanza">>, <<"getLastMessages">>,
  370:                     execute_get_last_messages(Vars, Config)),
  371:     #{<<"stanzas">> := [M1], <<"limit">> := 50} = Res,
  372:     check_stanza_map(M1, Alice).
  373: 
  374: admin_get_last_messages_limit(Config) ->
  375:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  376:                                     fun admin_get_last_messages_limit_story/3).
  377: 
  378: admin_get_last_messages_limit_story(Config, Alice, Bob) ->
  379:     admin_send_message_story(Config, Alice, Bob),
  380:     mam_helper:wait_for_archive_size(Alice, 1),
  381:     admin_send_message_story(Config, Bob, Alice),
  382:     mam_helper:wait_for_archive_size(Alice, 2),
  383:     Vars = #{caller => escalus_client:full_jid(Alice), limit => 1},
  384:     Res = ok_result(<<"stanza">>, <<"getLastMessages">>,
  385:                     execute_get_last_messages(Vars, Config)),
  386:     #{<<"stanzas">> := [M1], <<"limit">> := 1} = Res,
  387:     check_stanza_map(M1, Bob).
  388: 
  389: admin_get_last_messages_limit_enforced(Config) ->
  390:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  391:                                     fun admin_get_last_messages_limit_enforced_story/3).
  392: 
  393: admin_get_last_messages_limit_enforced_story(Config, Alice, Bob) ->
  394:     admin_send_message_story(Config, Alice, Bob),
  395:     mam_helper:wait_for_archive_size(Alice, 1),
  396:     Vars = #{caller => escalus_client:full_jid(Alice), limit => 1000},
  397:     Res = ok_result(<<"stanza">>, <<"getLastMessages">>,
  398:                     execute_get_last_messages(Vars, Config)),
  399:     %% The actual limit is returned
  400:     #{<<"stanzas">> := [M1], <<"limit">> := 500} = Res,
  401:     check_stanza_map(M1, Alice).
  402: 
  403: admin_get_last_messages_before(Config) ->
  404:     escalus:fresh_story_with_config(Config, [{alice, 1}, {bob, 1}],
  405:                                     fun admin_get_last_messages_before_story/3).
  406: 
  407: admin_get_last_messages_before_story(Config, Alice, Bob) ->
  408:     admin_send_message_story(Config, Alice, Bob),
  409:     mam_helper:wait_for_archive_size(Alice, 1),
  410:     admin_send_message_story(Config, Bob, Alice),
  411:     mam_helper:wait_for_archive_size(Alice, 2),
  412:     admin_send_message_story(Config, Bob, Alice),
  413:     mam_helper:wait_for_archive_size(Alice, 3),
  414:     Vars1 = #{caller => escalus_client:full_jid(Alice)},
  415:     Res1 = ok_result(<<"stanza">>, <<"getLastMessages">>,
  416:                      execute_get_last_messages(Vars1, Config)),
  417:     #{<<"stanzas">> := [M1, M2, _M3], <<"limit">> := 50} = Res1,
  418:     Vars2 = #{caller => escalus_client:full_jid(Alice),
  419:               before => maps:get(<<"timestamp">>, M2)},
  420:     Res2 = ok_result(<<"stanza">>, <<"getLastMessages">>,
  421:                      execute_get_last_messages(Vars2, Config)),
  422:     #{<<"stanzas">> := [M1, M2], <<"limit">> := 50} = Res2.
  423: 
  424: %% Helpers
  425: 
  426: execute_send_message(Vars, Config) ->
  427:     Q = <<"mutation M1($from: JID!, $to: JID!, $body: String!) "
  428:           "{ stanza { sendMessage(from: $from, to: $to, body: $body) { id } } }">>,
  429:     execute_auth(#{query => Q, variables => Vars,
  430:                    operationName => <<"M1">>}, Config).
  431: 
  432: execute_user_send_message(User, Vars, _Config) ->
  433:     Creds = graphql_helper:make_creds(User),
  434:     Q = <<"mutation M1($from: JID, $to: JID!, $body: String!) "
  435:           "{ stanza { sendMessage(from: $from, to: $to, body: $body) { id } } }">>,
  436:     QQ = #{query => Q, variables => Vars, operationName => <<"M1">>},
  437:     graphql_helper:execute(user, QQ, Creds).
  438: 
  439: execute_send_message_headline(Vars, Config) ->
  440:     Q = <<"mutation M1($from: JID!, $to: JID!, $subject: String, $body: String) "
  441:           "{ stanza { sendMessageHeadLine("
  442:             "from: $from, to: $to, subject: $subject, body: $body) { id } } }">>,
  443:     execute_auth(#{query => Q, variables => Vars,
  444:                    operationName => <<"M1">>}, Config).
  445: 
  446: execute_user_send_message_headline(User, Vars, _Config) ->
  447:     Creds = graphql_helper:make_creds(User),
  448:     Q = <<"mutation M1($from: JID, $to: JID!, $subject: String, $body: String) "
  449:           "{ stanza { sendMessageHeadLine("
  450:             "from: $from, to: $to, subject: $subject, body: $body) { id } } }">>,
  451:     QQ = #{query => Q, variables => Vars, operationName => <<"M1">>},
  452:     graphql_helper:execute(user, QQ, Creds).
  453: 
  454: execute_send_stanza(Vars, Config) ->
  455:     Q = <<"mutation M1($stanza: Stanza!) "
  456:           "{ stanza { sendStanza(stanza: $stanza) { id } } }">>,
  457:     execute_auth(#{query => Q, variables => Vars,
  458:                    operationName => <<"M1">>}, Config).
  459: 
  460: execute_user_send_stanza(User, Vars, _Config) ->
  461:     Creds = graphql_helper:make_creds(User),
  462:     Q = <<"mutation M1($stanza: Stanza!) "
  463:           "{ stanza { sendStanza(stanza: $stanza) { id } } }">>,
  464:     QQ = #{query => Q, variables => Vars, operationName => <<"M1">>},
  465:     graphql_helper:execute(user, QQ, Creds).
  466: 
  467: execute_get_last_messages(Vars, Config) ->
  468:     Q = <<"query Q1($caller: JID!, $with: JID, $limit: Int, $before: DateTime) "
  469:           "{ stanza { getLastMessages(caller: $caller, with: $with, "
  470:                  " limit: $limit, before: $before) "
  471:                      "{ stanzas { stanza_id stanza sender timestamp } limit } } }">>,
  472:     execute_auth(#{query => Q, variables => Vars,
  473:                    operationName => <<"Q1">>}, Config).
  474: 
  475: execute_user_get_last_messages(User, Vars, _Config) ->
  476:     Creds = graphql_helper:make_creds(User),
  477:     Q = <<"query Q1($with: JID, $limit: Int, $before: DateTime) "
  478:           "{ stanza { getLastMessages(with: $with, limit: $limit, before: $before) "
  479:                      "{ stanzas { stanza_id stanza sender timestamp } limit } } }">>,
  480:     QQ = #{query => Q, variables => Vars, operationName => <<"Q1">>},
  481:     graphql_helper:execute(user, QQ, Creds).
  482: 
  483: assert_not_empty(Bin, Config) ->
  484:     case proplists:get_value(has_mam, Config) of
  485:         true ->
  486:             assert_not_empty(Bin);
  487:         _ ->
  488:             skip
  489:     end.
  490: 
  491: assert_not_empty(Bin) when byte_size(Bin) > 0 -> ok.
  492: 
  493: ok_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"data">> := Data}}) ->
  494:     maps:get(What2, maps:get(What1, Data)).
  495: 
  496: check_stanza_map(#{<<"sender">> := SenderJID,
  497:                    <<"stanza">> := XML,
  498:                    <<"stanza_id">> := StanzaID,
  499:                    <<"timestamp">> := TS}, SenderClient) ->
  500:     ?assertEqual(escalus_utils:jid_to_lower(escalus_client:full_jid(SenderClient)),
  501:                  escalus_utils:jid_to_lower(SenderJID)),
  502:      true = byte_size(StanzaID) > 6,
  503:      true = is_integer(calendar:rfc3339_to_system_time(binary_to_list(TS))),
  504:      {ok, #xmlel{name = <<"message">>}} = exml:parse(XML).
  505: 
  506: spoofed_error(Call, Res) ->
  507:     {{<<"200">>, <<"OK">>},
  508:      #{<<"data">> := #{<<"stanza">> := #{Call := null}},
  509:        <<"errors">> := Errors}} = Res,
  510:     [#{<<"extensions">> := #{<<"code">> := <<"bad_from_jid">>},
  511:        <<"message">> := ErrMsg, <<"path">> := ErrPath}] = Errors,
  512:     ?assertEqual([<<"stanza">>, Call], ErrPath),
  513:     ?assertEqual(<<"Sending from this JID is not allowed">>, ErrMsg).