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