1: -module(graphql_http_upload_SUITE).
    2: 
    3: -compile([export_all, nowarn_export_all]).
    4: 
    5: -import(distributed_helper, [require_rpc_nodes/1]).
    6: -import(domain_helper, [host_type/0, domain/0]).
    7: -import(graphql_helper, [execute_user/3, execute_auth/2, user_to_bin/1]).
    8: 
    9: -include_lib("common_test/include/ct.hrl").
   10: -include_lib("eunit/include/eunit.hrl").
   11: -include_lib("exml/include/exml.hrl").
   12: -include_lib("escalus/include/escalus.hrl").
   13: -include("../../include/mod_roster.hrl").
   14: 
   15: -define(S3_HOSTNAME, <<"http://bucket.s3-eu-east-25.example.com">>).
   16: 
   17: suite() ->
   18:     require_rpc_nodes([mim]) ++ escalus:suite().
   19: 
   20: all() ->
   21:     [{group, user_http_upload},
   22:      {group, user_http_upload_not_configured},
   23:      {group, admin_http_upload},
   24:      {group, admin_http_upload_not_configured}].
   25: 
   26: groups() ->
   27:     [{user_http_upload, [], user_http_upload_handler()},
   28:      {user_http_upload_not_configured, [], user_http_upload_not_configured_handler()},
   29:      {admin_http_upload, [], admin_http_upload_handler()},
   30:      {admin_http_upload_not_configured, [], admin_http_upload_not_configured_handler()}].
   31: 
   32: user_http_upload_handler() ->
   33:     [user_get_url_test,
   34:      user_get_url_zero_size,
   35:      user_get_url_too_large_size,
   36:      user_get_url_zero_timeout].
   37: 
   38: user_http_upload_not_configured_handler() ->
   39:     [user_http_upload_not_configured].
   40: 
   41: admin_http_upload_handler() ->
   42:     [admin_get_url_test,
   43:      admin_get_url_zero_size,
   44:      admin_get_url_too_large_size,
   45:      admin_get_url_zero_timeout,
   46:      admin_get_url_no_domain].
   47: 
   48: admin_http_upload_not_configured_handler() ->
   49:     [admin_http_upload_not_configured].
   50: 
   51: init_per_suite(Config) ->
   52:     Config1 = dynamic_modules:save_modules(host_type(), Config),
   53:     escalus:init_per_suite(Config1).
   54: 
   55: end_per_suite(Config) ->
   56:     dynamic_modules:restore_modules(Config),
   57:     escalus:end_per_suite(Config).
   58: 
   59: init_per_group(user_http_upload, Config) ->
   60:     dynamic_modules:ensure_modules(host_type(),
   61:         [{mod_http_upload, create_opts(?S3_HOSTNAME, true)}]),
   62:     [{schema_endpoint, user} | Config];
   63: init_per_group(user_http_upload_not_configured, Config) ->
   64:     dynamic_modules:ensure_modules(host_type(), [{mod_http_upload, stopped}]),
   65:     [{schema_endpoint, user} | Config];
   66: init_per_group(admin_http_upload, Config) ->
   67:     dynamic_modules:ensure_modules(host_type(),
   68:         [{mod_http_upload, create_opts(?S3_HOSTNAME, true)}]),
   69:     graphql_helper:init_admin_handler(Config);
   70: init_per_group(admin_http_upload_not_configured, Config) ->
   71:     dynamic_modules:ensure_modules(host_type(), [{mod_http_upload, stopped}]),
   72:     graphql_helper:init_admin_handler(Config).
   73: 
   74: end_per_group(_, _Config) ->
   75:     escalus_fresh:clean().
   76: 
   77: init_per_testcase(CaseName, Config) ->
   78:     escalus:init_per_testcase(CaseName, Config).
   79: 
   80: end_per_testcase(CaseName, Config) ->
   81:     escalus:end_per_testcase(CaseName, Config).
   82: 
   83: create_opts(Host, AddAcl) ->
   84:     config_parser_helper:mod_config(mod_http_upload,
   85:         #{
   86:             max_file_size => 1234,
   87:             s3 => #{
   88:                 bucket_url => Host,
   89:                 add_acl => AddAcl,
   90:                 region => <<"eu-east-25">>,
   91:                 access_key_id => <<"AKIAIAOAONIULXQGMOUA">>,
   92:                 secret_access_key => <<"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">>
   93:             }
   94:         }).
   95: 
   96: % User test cases
   97: 
   98: user_get_url_test(Config) ->
   99:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  100:                                     fun user_get_url_test/2).
  101: 
  102: user_get_url_test(Config, Alice) ->
  103:     Vars = #{<<"filename">> => <<"test">>, <<"size">> => 123,
  104:              <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  105:     GraphQlRequest = user_send_request(Config, Vars, Alice),
  106:     ParsedResult = ok_result(<<"httpUpload">>, <<"getUrl">>, GraphQlRequest),
  107:     #{<<"PutUrl">> := PutURL, <<"GetUrl">> := GetURL, <<"Header">> := _Headers} = ParsedResult,
  108:     ?assertMatch({_, _}, binary:match(PutURL, [?S3_HOSTNAME])),
  109:     ?assertMatch({_, _}, binary:match(GetURL, [?S3_HOSTNAME])).
  110: 
  111: user_get_url_zero_size(Config) ->
  112:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  113:                                     fun user_get_url_zero_size/2).
  114: 
  115: user_get_url_zero_size(Config, Alice) ->
  116:     Vars = #{<<"filename">> => <<"test">>, <<"size">> => 0,
  117:              <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  118:     GraphQlRequest = user_send_request(Config, Vars, Alice),
  119:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  120:     ?assertEqual(<<"size_error">>, ParsedResult).
  121: 
  122: user_get_url_too_large_size(Config) ->
  123:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  124:                                     fun user_get_url_too_large_size/2).
  125: 
  126: user_get_url_too_large_size(Config, Alice) ->
  127:     Vars = #{<<"filename">> => <<"test">>, <<"size">> => 100000,
  128:              <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  129:     GraphQlRequest = user_send_request(Config, Vars, Alice),
  130:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  131:     ?assertEqual(<<"file_too_large_error">>, ParsedResult).
  132: 
  133: user_get_url_zero_timeout(Config) ->
  134:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  135:                                     fun user_get_url_zero_timeout/2).
  136: 
  137: user_get_url_zero_timeout(Config, Alice) ->
  138:     Vars = #{<<"filename">> => <<"test">>, <<"size">> => 123,
  139:              <<"contentType">> => <<"Test">>, <<"timeout">> => 0},
  140:     GraphQlRequest = user_send_request(Config, Vars, Alice),
  141:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  142:     ?assertEqual(<<"timeout_error">>, ParsedResult).
  143: 
  144: user_http_upload_not_configured(Config) ->
  145:     escalus:fresh_story_with_config(Config, [{alice, 1}],
  146:                                     fun user_http_upload_not_configured/2).
  147: 
  148: user_http_upload_not_configured(Config, Alice) ->
  149:     Vars = #{<<"filename">> => <<"test">>, <<"size">> => 123,
  150:              <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  151:     GraphQlRequest = user_send_request(Config, Vars, Alice),
  152:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  153:     ?assertEqual(<<"module_not_loaded_error">>, ParsedResult).
  154: 
  155: % Admin test cases
  156: 
  157: admin_get_url_test(Config) ->
  158:     Vars = #{<<"domain">> => domain(), <<"filename">> => <<"test">>,
  159:              <<"size">> => 123, <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  160:     GraphQlRequest = admin_send_request(Config, Vars),
  161:     ParsedResult = ok_result(<<"httpUpload">>, <<"getUrl">>, GraphQlRequest),
  162:     #{<<"PutUrl">> := PutURL, <<"GetUrl">> := GetURL, <<"Header">> := _Headers} = ParsedResult,
  163:     ?assertMatch({_, _}, binary:match(PutURL, [?S3_HOSTNAME])),
  164:     ?assertMatch({_, _}, binary:match(GetURL, [?S3_HOSTNAME])).
  165: 
  166: admin_get_url_zero_size(Config) ->
  167:     Vars = #{<<"domain">> => domain(), <<"filename">> => <<"test">>,
  168:              <<"size">> => 0, <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  169:     GraphQlRequest = admin_send_request(Config, Vars),
  170:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  171:     ?assertEqual(<<"size_error">>, ParsedResult).
  172: 
  173: admin_get_url_too_large_size(Config) ->
  174:     Vars = #{<<"domain">> => domain(), <<"filename">> => <<"test">>,
  175:              <<"size">> => 1000000, <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  176:     GraphQlRequest = admin_send_request(Config, Vars),
  177:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  178:     ?assertEqual(<<"file_too_large_error">>, ParsedResult).
  179: 
  180: admin_get_url_zero_timeout(Config) ->
  181:     Vars = #{<<"domain">> => domain(), <<"filename">> => <<"test">>,
  182:              <<"size">> => 123, <<"contentType">> => <<"Test">>, <<"timeout">> => 0},
  183:     GraphQlRequest = admin_send_request(Config, Vars),
  184:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  185:     ?assertEqual(<<"timeout_error">>, ParsedResult).
  186: 
  187: admin_get_url_no_domain(Config) ->
  188:     Vars = #{<<"domain">> => <<"AAAAA">>, <<"filename">> => <<"test">>,
  189:              <<"size">> => 123, <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  190:     GraphQlRequest = admin_send_request(Config, Vars),
  191:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  192:     ?assertEqual(<<"domain_not_found">>, ParsedResult).
  193: 
  194: admin_http_upload_not_configured(Config) ->
  195:     Vars = #{<<"domain">> => domain(), <<"filename">> => <<"test">>,
  196:              <<"size">> => 123, <<"contentType">> => <<"Test">>, <<"timeout">> => 123},
  197:     GraphQlRequest = admin_send_request(Config, Vars),
  198:     ParsedResult = error_result(<<"extensions">>, <<"code">>, GraphQlRequest),
  199:     ?assertEqual(<<"module_not_loaded_error">>, ParsedResult).
  200: 
  201: % Helpers
  202: 
  203: user_get_url() ->
  204:     <<"mutation M1($filename: String!, $size: Int!,
  205:                    $contentType: String!, $timeout: Int!)",
  206:           "{httpUpload{getUrl(filename: $filename, size: $size,
  207:                               contentType: $contentType, timeout: $timeout)
  208:                 {PutUrl, GetUrl, Header}}}">>.
  209: 
  210: admin_get_url() ->
  211:     <<"mutation M1($domain: String!, $filename: String!, $size: Int!,
  212:                    $contentType: String!, $timeout: Int!)",
  213:           "{httpUpload{getUrl(domain: $domain, filename: $filename,
  214:                        size: $size, contentType: $contentType, timeout: $timeout)
  215:                 {PutUrl, GetUrl, Header}}}">>.
  216: 
  217: user_send_request(Config, Vars, User) ->
  218:     Body = #{query => user_get_url(), operationName => <<"M1">>, variables => Vars},
  219:     execute_user(Body, User, Config).
  220: 
  221: admin_send_request(Config, Vars) ->
  222:     Body = #{query => admin_get_url(), operationName => <<"M1">>, variables => Vars},
  223:     execute_auth(Body, Config).
  224: 
  225: error_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"errors">> := [Data]}}) ->
  226:     maps:get(What2, maps:get(What1, Data)).
  227: 
  228: ok_result(What1, What2, {{<<"200">>, <<"OK">>}, #{<<"data">> := Data}}) ->
  229:     maps:get(What2, maps:get(What1, Data)).
  230: 
  231: url_contains(UrlType, Filename, Result) ->
  232:     Url = extract_url(Result, UrlType),
  233:     binary:match(Url, Filename) =/= nomatch.
  234: 
  235: extract_url(Result, UrlType) ->
  236:     exml_query:path(Result, [{element, <<"slot">>}, {element, UrlType}, {attr, <<"url">>}]).