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:     ok.
   79: 
   80: init_per_testcase(CaseName, Config) ->
   81:     escalus:init_per_testcase(CaseName, Config).
   82: 
   83: end_per_testcase(CaseName, Config) ->
   84:     escalus:end_per_testcase(CaseName, Config).
   85: 
   86: %%--------------------------------------------------------------------
   87: %% mongoose_graphql tests
   88: %%--------------------------------------------------------------------
   89: 
   90: can_execute_admin_queries_with_permissions(Config) ->
   91:     Query = "query { checkAuth { authStatus } }",
   92:     Res = mongooseimctl("graphql", [Query], Config),
   93:     ?assertMatch({_, 0}, Res),
   94:     Data = element(1, Res),
   95:     ?assertNotEqual(nomatch, string:find(Data, "AUTHORIZED")).
   96: 
   97: can_handle_execution_error(Config) ->
   98:     Query = "{}",
   99:     Res = mongooseimctl("graphql", [Query], Config),
  100:     ?assertMatch({_, 1}, Res),
  101:     Data = element(1, Res),
  102:     ?assertNotEqual(nomatch, string:find(Data, "parser_error")).
  103: 
  104: graphql_wrong_arguments_number(Config) ->
  105:     ExpectedFragment = "This command requires",
  106:     ResNoArgs = mongooseimctl("graphql", [], Config),
  107:     ?assertMatch({_, 1}, ResNoArgs),
  108:     Data1 = element(1, ResNoArgs),
  109:     ?assertNotEqual(nomatch, string:find(Data1, ExpectedFragment)),
  110: 
  111:     ResTooManyArgs = mongooseimctl("graphql", ["{}", "{}"], Config),
  112:     ?assertMatch({_, 1}, ResTooManyArgs),
  113:     Data2 = element(1, ResTooManyArgs),
  114:     ?assertNotEqual(nomatch, string:find(Data2, ExpectedFragment)).
  115: 
  116: %% Generic GraphQL command tests
  117: %% Specific commands are tested in graphql_*_SUITE
  118: 
  119: graphql_error_unknown_command_with_args(Config) ->
  120:     {Res, 1} = mongooseimctl("account", ["makeCoffee", "--strength", "medium"], Config),
  121:     ?assertMatch({match, _}, re:run(Res, "Unknown command")),
  122:     expect_existing_commands(Res).
  123: 
  124: graphql_error_unknown_command_without_args(Config) ->
  125:     {Res, 1} = mongooseimctl("account", ["makeCoffee"], Config),
  126:     ?assertMatch({match, _}, re:run(Res, "Unknown command")),
  127:     expect_existing_commands(Res).
  128: 
  129: graphql_error_unknown_category_with_args(Config) ->
  130:     {Res, 1} = mongooseimctl("cafe", ["makeCoffee"], Config),
  131:     ?assertMatch({match, _}, re:run(Res, "Unknown category")),
  132:     expect_category_list(Res).
  133: 
  134: graphql_error_unknown_category_without_args(Config) ->
  135:     {Res, 1} = mongooseimctl("cafe", [], Config),
  136:     ?assertMatch({match, _}, re:run(Res, "Unknown category")),
  137:     expect_category_list(Res).
  138: 
  139: graphql_no_command(Config) ->
  140:     %% Not an error - lists commands in the given category
  141:     {Res, 0} = mongooseimctl("account", [], Config),
  142:     expect_existing_commands(Res).
  143: 
  144: graphql_error_invalid_args(Config) ->
  145:     {Res, 1} = mongooseimctl("account", ["countUsers", "now"], Config),
  146:     ?assertMatch({match, _}, re:run(Res, "Could not parse")),
  147:     expect_command_arguments(Res).
  148: 
  149: graphql_error_invalid_arg_value(Config) ->
  150:     {Res, 1} = mongooseimctl("vcard", ["setVcard", "--user", "user@host", "--vcard", "x"], Config),
  151:     %% vCard should be provided in JSON
  152:     ?assertMatch({match, _}, re:run(Res, "Invalid value 'x' of argument 'vcard'")),
  153:     ?assertMatch({match, _}, re:run(Res, "vcard\s+VcardInput!")).
  154: 
  155: graphql_error_no_arg_value(Config) ->
  156:     {Res, 1} = mongooseimctl("account", ["countUsers", "--domain"], Config),
  157:     ?assertMatch({match, _}, re:run(Res, "Could not parse")),
  158:     expect_command_arguments(Res).
  159: 
  160: graphql_error_missing_args(Config) ->
  161:     {Res, 1} = mongooseimctl("account", ["countUsers"], Config),
  162:     ?assertMatch({match, _}, re:run(Res, "Missing mandatory arguments")),
  163:     expect_command_arguments(Res).
  164: 
  165: graphql_error_unknown_arg(Config) ->
  166:     {Res, 1} = mongooseimctl("account", ["countUsers", "--domain", "localhost",
  167:                                          "--x", "y"], Config),
  168:     ?assertMatch({match, _}, re:run(Res, "Unknown argument")),
  169:     expect_command_arguments(Res).
  170: 
  171: graphql_arg_help(Config) ->
  172:     {Res, 0} = mongooseimctl("account", ["countUsers", "--help"], Config),
  173:     expect_command_arguments(Res).
  174: 
  175: graphql_command(Config) ->
  176:     {ResJSON, 0} = mongooseimctl("account", ["countUsers", "--domain", "localhost"], Config),
  177:     #{<<"data">> := Data} = rest_helper:decode(ResJSON, #{return_maps => true}),
  178:     ?assertMatch(#{<<"account">> := #{<<"countUsers">> := _}}, Data).
  179: 
  180: expect_existing_commands(Res) ->
  181:     ?assertMatch({match, _}, re:run(Res, "countUsers")).
  182: 
  183: expect_command_arguments(Res) ->
  184:     ?assertMatch({match, _}, re:run(Res, "domain\s+DomainName!")).
  185: 
  186: %%-----------------------------------------------------------------
  187: %% Help tests
  188: %%-----------------------------------------------------------------
  189: 
  190: default_help(Config) ->
  191:     #{node := Node} = mim(),
  192:     CtlCmd = distributed_helper:ctl_path(Node, Config),
  193:     {Res, 2} = mongooseimctl_helper:run(CtlCmd, []),
  194:     expect_category_list(Res).
  195: 
  196: help_with_dual_mode(Config) ->
  197:     {Res1, 2} = mongooseimctl("help", ["--dual"], Config),
  198:     expect_category_list(Res1),
  199:     {Res2, 2} = mongooseimctl("help", ["--nonexistent"], Config),
  200:     expect_category_list(Res2),
  201:     {Res3, 2} = mongooseimctl("help", [], Config),
  202:     expect_category_list(Res3).
  203: 
  204: help_with_long_mode(Config) ->
  205:     {Res, 2} = mongooseimctl("help", ["--long"], Config),
  206:     io:format("Res: ~p", [Res]),
  207:     ?assertMatch({match, _}, re:run(Res, "Usage")),
  208:     ?assertMatch({match, _}, re:run(Res, "account \n\s+Account management")).
  209: 
  210: %%-----------------------------------------------------------------
  211: %% Server management tests
  212: %%-----------------------------------------------------------------
  213: 
  214: server_status(Config) ->
  215:     {Res, 0} = mongooseimctl("status", [], Config),
  216:     ?assertMatch({match, _}, re:run(Res, "Erlang VM status: started")).
  217: 
  218: server_is_started(Config) ->
  219:     %% Wait for the server to start, but it is already running
  220:     {Res, 0} = mongooseimctl("started", [], Config),
  221:     %% Expect only whitespace
  222:     ?assertMatch(nomatch, re:run(Res, "\S")).
  223: 
  224: %%-----------------------------------------------------------------
  225: %% Helpers
  226: %%-----------------------------------------------------------------
  227: 
  228: expect_category_list(Res) ->
  229:     ?assertMatch({match, _}, re:run(Res, "Usage")),
  230:     ?assertMatch({match, _}, re:run(Res, "account\s+Account management")).