1: %%==============================================================================
    2: %% Copyright 2013 Erlang Solutions Ltd.
    3: %%
    4: %% Licensed under the Apache License, Version 2.0 (the "License");
    5: %% you may not use this file except in compliance with the License.
    6: %% You may obtain a copy of the License at
    7: %%
    8: %% http://www.apache.org/licenses/LICENSE-2.0
    9: %%
   10: %% Unless required by applicable law or agreed to in writing, software
   11: %% distributed under the License is distributed on an "AS IS" BASIS,
   12: %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13: %% See the License for the specific language governing permissions and
   14: %% limitations under the License.
   15: %%==============================================================================
   16: -module(mongooseimctl_SUITE).
   17: -compile([export_all, nowarn_export_all, nowarn_shadow_vars]).
   18: -include_lib("escalus/include/escalus.hrl").
   19: -include_lib("common_test/include/ct.hrl").
   20: -include_lib("exml/include/exml.hrl").
   21: -include_lib("eunit/include/eunit.hrl").
   22: 
   23: -import(mongooseimctl_helper, [mongooseimctl/3]).
   24: -import(distributed_helper, [mim/0, require_rpc_nodes/1]).
   25: -import(domain_helper, [domain/0]).
   26: 
   27: %%--------------------------------------------------------------------
   28: %% Suite configuration
   29: %%--------------------------------------------------------------------
   30: 
   31: all() ->
   32:     [
   33:      {group, graphql},
   34:      {group, help},
   35:      {group, server}
   36:     ].
   37: 
   38: groups() ->
   39:     [{graphql, [], graphql()},
   40:      {help, [], help()},
   41:      {server, [], server()}].
   42: 
   43: graphql() ->
   44:     [graphql_wrong_arguments_number,
   45:      can_execute_admin_queries_with_permissions,
   46:      can_handle_execution_error,
   47:      graphql_error_unknown_command_with_args,
   48:      graphql_error_unknown_command_without_args,
   49:      graphql_error_unknown_category_with_args,
   50:      graphql_error_unknown_category_without_args,
   51:      graphql_no_command,
   52:      graphql_error_invalid_args,
   53:      graphql_error_invalid_arg_value,
   54:      graphql_error_no_arg_value,
   55:      graphql_error_missing_args,
   56:      graphql_error_unknown_arg,
   57:      graphql_arg_help,
   58:      graphql_command].
   59: 
   60: help() ->
   61:     [default_help,
   62:      help_with_dual_mode,
   63:      help_with_long_mode].
   64: 
   65: server() ->
   66:     [server_status,
   67:      server_is_started].
   68: 
   69: suite() ->
   70:     require_rpc_nodes([mim]) ++ escalus:suite().
   71: 
   72: init_per_suite(Config) ->
   73:     Node = mim(),
   74:     Config1 = ejabberd_node_utils:init(Node, Config),
   75:     Config1.
   76: 
   77: end_per_suite(Config) ->
   78:     Config.
   79: 
   80: init_per_group(_GroupName, Config) ->
   81:     Config.
   82: 
   83: end_per_group(_GroupName, Config) ->
   84:     Config.
   85: 
   86: init_per_testcase(CaseName, Config) ->
   87:     escalus:init_per_testcase(CaseName, Config).
   88: 
   89: end_per_testcase(CaseName, Config) ->
   90:     escalus:end_per_testcase(CaseName, Config).
   91: 
   92: %%--------------------------------------------------------------------
   93: %% mongoose_graphql tests
   94: %%--------------------------------------------------------------------
   95: 
   96: can_execute_admin_queries_with_permissions(Config) ->
   97:     Query = "query { checkAuth { authStatus } }",
   98:     Res = mongooseimctl("graphql", [Query], Config),
   99:     ?assertMatch({_, 0}, Res),
  100:     Data = element(1, Res),
  101:     ?assertNotEqual(nomatch, string:find(Data, "AUTHORIZED")).
  102: 
  103: can_handle_execution_error(Config) ->
  104:     Query = "{}",
  105:     Res = mongooseimctl("graphql", [Query], Config),
  106:     ?assertMatch({_, 1}, Res),
  107:     Data = element(1, Res),
  108:     ?assertNotEqual(nomatch, string:find(Data, "parser_error")).
  109: 
  110: graphql_wrong_arguments_number(Config) ->
  111:     ExpectedFragment = "This command requires",
  112:     ResNoArgs = mongooseimctl("graphql", [], Config),
  113:     ?assertMatch({_, 1}, ResNoArgs),
  114:     Data1 = element(1, ResNoArgs),
  115:     ?assertNotEqual(nomatch, string:find(Data1, ExpectedFragment)),
  116: 
  117:     ResTooManyArgs = mongooseimctl("graphql", ["{}", "{}"], Config),
  118:     ?assertMatch({_, 1}, ResTooManyArgs),
  119:     Data2 = element(1, ResTooManyArgs),
  120:     ?assertNotEqual(nomatch, string:find(Data2, ExpectedFragment)).
  121: 
  122: %% Generic GraphQL command tests
  123: %% Specific commands are tested in graphql_*_SUITE
  124: 
  125: graphql_error_unknown_command_with_args(Config) ->
  126:     {Res, 1} = mongooseimctl("account", ["makeCoffee", "--strength", "medium"], Config),
  127:     ?assertMatch({match, _}, re:run(Res, "Unknown command")),
  128:     expect_existing_commands(Res).
  129: 
  130: graphql_error_unknown_command_without_args(Config) ->
  131:     {Res, 1} = mongooseimctl("account", ["makeCoffee"], Config),
  132:     ?assertMatch({match, _}, re:run(Res, "Unknown command")),
  133:     expect_existing_commands(Res).
  134: 
  135: graphql_error_unknown_category_with_args(Config) ->
  136:     {Res, 1} = mongooseimctl("cafe", ["makeCoffee"], Config),
  137:     ?assertMatch({match, _}, re:run(Res, "Unknown category")),
  138:     expect_category_list(Res).
  139: 
  140: graphql_error_unknown_category_without_args(Config) ->
  141:     {Res, 1} = mongooseimctl("cafe", [], Config),
  142:     ?assertMatch({match, _}, re:run(Res, "Unknown category")),
  143:     expect_category_list(Res).
  144: 
  145: graphql_no_command(Config) ->
  146:     %% Not an error - lists commands in the given category
  147:     {Res, 0} = mongooseimctl("account", [], Config),
  148:     expect_existing_commands(Res).
  149: 
  150: graphql_error_invalid_args(Config) ->
  151:     {Res, 1} = mongooseimctl("account", ["countUsers", "now"], Config),
  152:     ?assertMatch({match, _}, re:run(Res, "Could not parse")),
  153:     expect_command_arguments(Res).
  154: 
  155: graphql_error_invalid_arg_value(Config) ->
  156:     {Res, 1} = mongooseimctl("vcard", ["setVcard", "--user", "user@host", "--vcard", "x"], Config),
  157:     %% vCard should be provided in JSON
  158:     ?assertMatch({match, _}, re:run(Res, "Invalid value 'x' of argument 'vcard'")),
  159:     ?assertMatch({match, _}, re:run(Res, "vcard\s+VcardInput!")).
  160: 
  161: graphql_error_no_arg_value(Config) ->
  162:     {Res, 1} = mongooseimctl("account", ["countUsers", "--domain"], Config),
  163:     ?assertMatch({match, _}, re:run(Res, "Could not parse")),
  164:     expect_command_arguments(Res).
  165: 
  166: graphql_error_missing_args(Config) ->
  167:     {Res, 1} = mongooseimctl("account", ["countUsers"], Config),
  168:     ?assertMatch({match, _}, re:run(Res, "Missing mandatory arguments")),
  169:     expect_command_arguments(Res).
  170: 
  171: graphql_error_unknown_arg(Config) ->
  172:     {Res, 1} = mongooseimctl("account", ["countUsers", "--domain", "localhost",
  173:                                          "--x", "y"], Config),
  174:     ?assertMatch({match, _}, re:run(Res, "Unknown argument")),
  175:     expect_command_arguments(Res).
  176: 
  177: graphql_arg_help(Config) ->
  178:     {Res, 0} = mongooseimctl("account", ["countUsers", "--help"], Config),
  179:     expect_command_arguments(Res).
  180: 
  181: graphql_command(Config) ->
  182:     {ResJSON, 0} = mongooseimctl("account", ["countUsers", "--domain", "localhost"], Config),
  183:     #{<<"data">> := Data} = rest_helper:decode(ResJSON, #{return_maps => true}),
  184:     ?assertMatch(#{<<"account">> := #{<<"countUsers">> := _}}, Data).
  185: 
  186: expect_existing_commands(Res) ->
  187:     ?assertMatch({match, _}, re:run(Res, "countUsers")).
  188: 
  189: expect_command_arguments(Res) ->
  190:     ?assertMatch({match, _}, re:run(Res, "domain\s+DomainName!")).
  191: 
  192: %%-----------------------------------------------------------------
  193: %% Help tests
  194: %%-----------------------------------------------------------------
  195: 
  196: default_help(Config) ->
  197:     #{node := Node} = mim(),
  198:     CtlCmd = distributed_helper:ctl_path(Node, Config),
  199:     {Res, 2} = mongooseimctl_helper:run(CtlCmd, []),
  200:     expect_category_list(Res).
  201: 
  202: help_with_dual_mode(Config) ->
  203:     {Res1, 2} = mongooseimctl("help", ["--dual"], Config),
  204:     expect_category_list(Res1),
  205:     {Res2, 2} = mongooseimctl("help", ["--nonexistent"], Config),
  206:     expect_category_list(Res2),
  207:     {Res3, 2} = mongooseimctl("help", [], Config),
  208:     expect_category_list(Res3).
  209: 
  210: help_with_long_mode(Config) ->
  211:     {Res, 2} = mongooseimctl("help", ["--long"], Config),
  212:     io:format("Res: ~p", [Res]),
  213:     ?assertMatch({match, _}, re:run(Res, "Usage")),
  214:     ?assertMatch({match, _}, re:run(Res, "account \n\s+Account management")).
  215: 
  216: %%-----------------------------------------------------------------
  217: %% Server management tests
  218: %%-----------------------------------------------------------------
  219: 
  220: server_status(Config) ->
  221:     {Res, 0} = mongooseimctl("status", [], Config),
  222:     ?assertMatch({match, _}, re:run(Res, "Erlang VM status: started")).
  223: 
  224: server_is_started(Config) ->
  225:     %% Wait for the server to start, but it is already running
  226:     {Res, 0} = mongooseimctl("started", [], Config),
  227:     %% Expect only whitespace
  228:     ?assertMatch(nomatch, re:run(Res, "\S")).
  229: 
  230: %%-----------------------------------------------------------------
  231: %% Helpers
  232: %%-----------------------------------------------------------------
  233: 
  234: expect_category_list(Res) ->
  235:     ?assertMatch({match, _}, re:run(Res, "Usage")),
  236:     ?assertMatch({match, _}, re:run(Res, "account\s+Account management")).