chore(emqx_management): update test case

This commit is contained in:
zhanghongtong 2021-02-25 14:10:30 +08:00 committed by turtleDeng
parent 033f8619eb
commit 24d954282d
10 changed files with 222 additions and 142 deletions

View File

@ -3,7 +3,7 @@
{vsn, "4.3.0"}, % strict semver, bump manually!
{modules, []},
{registered, [emqx_management_sup]},
{applications, [kernel,stdlib,minirest,emqx_modules]},
{applications, [kernel,stdlib,minirest]},
{mod, {emqx_mgmt_app,[]}},
{env, []},
{licenses, ["Apache-2.0"]},

View File

@ -483,16 +483,6 @@ delete_banned(Who) ->
emqx_banned:delete(Who).
any_to_atom(L) when is_list(L) -> list_to_atom(L);
any_to_atom(B) when is_binary(B) -> binary_to_atom(B, utf8);
any_to_atom(A) when is_atom(A) -> A.
to_version(Version) when is_integer(Version) ->
integer_to_list(Version);
to_version(Version) when is_binary(Version) ->
binary_to_list(Version);
to_version(Version) when is_list(Version) ->
Version.
%%--------------------------------------------------------------------
%% Telemtry API
@ -561,20 +551,4 @@ max_row_limit() ->
table_size(Tab) -> ets:info(Tab, size).
map_to_actions(Maps) ->
[map_to_action(M) || M <- Maps].
map_to_action(Map = #{<<"id">> := ActionInstId, <<"name">> := Name, <<"args">> := Args}) ->
#{id => ActionInstId,
name => any_to_atom(Name),
args => Args,
fallbacks => map_to_actions(maps:get(<<"fallbacks">>, Map, []))}.
actions_to_prop_list(Actions) ->
[action_to_prop_list(Act) || Act <- Actions].
action_to_prop_list({action_instance, ActionInstId, Name, FallbackActions, Args}) ->
[{id, ActionInstId},
{name, Name},
{fallbacks, actions_to_prop_list(FallbackActions)},
{args, Args}].

View File

@ -75,7 +75,11 @@
]).
export(_Bindings, _Params) ->
return(emqx_mgmt_data_backup:export()).
case emqx_mgmt_data_backup:export() of
{ok, File = #{filename := Filename}} ->
return({ok, File#{filename => filename:basename(Filename)}});
Return -> return(Return)
end.
list_exported(_Bindings, _Params) ->
List = [ rpc:call(Node, ?MODULE, get_list_exported, []) || Node <- ekka_mnesia:running_nodes() ],

View File

@ -58,7 +58,7 @@
-spec(load() -> ok).
load() ->
Cmds = [Fun || {Fun, _} <- ?MODULE:module_info(exports), is_cmd(Fun)],
lists:foreach(fun(Cmd) -> emqx_ctl:register_command(Cmd, {?MODULE, Cmd}, []) end, Cmds).
foreach(fun(Cmd) -> emqx_ctl:register_command(Cmd, {?MODULE, Cmd}, []) end, Cmds).
is_cmd(Fun) ->
not lists:member(Fun, [init, load, module_info]).
@ -100,7 +100,7 @@ mgmt(["delete", AppId]) ->
end;
mgmt(["list"]) ->
lists:foreach(fun({AppId, AppSecret, Name, Desc, Status, Expired}) ->
foreach(fun({AppId, AppSecret, Name, Desc, Status, Expired}) ->
emqx_ctl:print("app_id: ~s, secret: ~s, name: ~s, desc: ~s, status: ~s, expired: ~p~n",
[AppId, AppSecret, Name, Desc, Status, Expired])
end, emqx_mgmt_auth:list_apps());
@ -229,7 +229,7 @@ routes(_) ->
{"routes show <Topic>", "Show a route"}]).
subscriptions(["list"]) ->
lists:foreach(fun(Suboption) ->
foreach(fun(Suboption) ->
print({emqx_suboption, Suboption})
end, ets:tab2list(emqx_suboption));
@ -544,8 +544,8 @@ stop_listener(#{listen_on := ListenOn} = Listener, _Input) ->
data(["export"]) ->
case emqx_mgmt_data_backup:export() of
{ok, _} ->
emqx_ctl:print("The emqx data has been successfully exported to ~s.~n", [NFilename]);
{ok, #{filename := Filename}} ->
emqx_ctl:print("The emqx data has been successfully exported to ~s.~n", [Filename]);
{error, Reason} ->
emqx_ctl:print("The emqx data export failed due to ~p.~n", [Reason])
end;
@ -555,9 +555,9 @@ data(["import", Filename]) ->
ok ->
emqx_ctl:print("The emqx data has been imported successfully.~n");
{error, import_failed} ->
emqx_ctl:print("The emqx data import failed due: ~0p~n", [{Class,Reason,Stack}]);
emqx_ctl:print("The emqx data import failed. ~n");
{error, unsupported_version} ->
emqx_ctl:print("Unsupported version: ~p~n", [Version]);
emqx_ctl:print("The emqx data import failed: Unsupported version. ~n");
{error, Reason} ->
emqx_ctl:print("The emqx data import failed: ~0p while reading ~s.~n", [Reason, Filename])
end;
@ -636,10 +636,6 @@ print(#plugin{name = Name, descr = Descr, active = Active}) ->
emqx_ctl:print("Plugin(~s, description=~s, active=~s)~n",
[Name, Descr, Active]);
print({module, {Name, Active}}) ->
emqx_ctl:print("Module(~s, description=~s, active=~s)~n",
[Name, Name:description(), Active]);
print({emqx_suboption, {{Pid, Topic}, Options}}) when is_pid(Pid) ->
emqx_ctl:print("~s -> ~s~n", [maps:get(subid, Options), Topic]).

View File

@ -16,6 +16,10 @@
-module(emqx_mgmt_data_backup).
-include("emqx_mgmt.hrl").
-include_lib("emqx/include/emqx.hrl").
-include_lib("kernel/include/file.hrl").
-ifdef(EMQX_ENTERPISE).
-export([ export_modules/0
, export_schemas/0
@ -266,6 +270,14 @@ apply_new_config([Action = #{<<"name">> := <<"data_to_webserver">>,
apply_new_config(More, Configs, [Action#{<<"args">> := Args} | Acc])
end.
actions_to_prop_list(Actions) ->
[action_to_prop_list(Act) || Act <- Actions].
action_to_prop_list({action_instance, ActionInstId, Name, FallbackActions, Args}) ->
[{id, ActionInstId},
{name, Name},
{fallbacks, actions_to_prop_list(FallbackActions)},
{args, Args}].
-endif.
@ -415,6 +427,26 @@ import_confs(Configs, ListenersState) ->
-endif.
any_to_atom(L) when is_list(L) -> list_to_atom(L);
any_to_atom(B) when is_binary(B) -> binary_to_atom(B, utf8);
any_to_atom(A) when is_atom(A) -> A.
map_to_actions(Maps) ->
[map_to_action(M) || M <- Maps].
map_to_action(Map = #{<<"id">> := ActionInstId, <<"name">> := Name, <<"args">> := Args}) ->
#{id => ActionInstId,
name => any_to_atom(Name),
args => Args,
fallbacks => map_to_actions(maps:get(<<"fallbacks">>, Map, []))}.
to_version(Version) when is_integer(Version) ->
integer_to_list(Version);
to_version(Version) when is_binary(Version) ->
binary_to_list(Version);
to_version(Version) when is_list(Version) ->
Version.
-ifdef(EMQX_ENTERPRISE).
export() ->
Modules = export_modules(),
@ -507,7 +539,7 @@ export() ->
write_file(NFilename, Data).
import(Filename) ->
case file:read_file(FullFilename) of
case file:read_file(Filename) of
{ok, Json} ->
Data = emqx_json:decode(Json, [return_maps]),
Version = to_version(maps:get(<<"version">>, Data)),
@ -532,8 +564,7 @@ import(Filename) ->
logger:error("Unsupported version: ~p", [Version]),
{error, unsupported_version}
end;
{error, Reason} ->
{error, Reason}
Error -> Error
end.
-endif.
@ -544,14 +575,12 @@ write_file(Filename, Data) ->
case file:read_file_info(Filename) of
{ok, #file_info{size = Size, ctime = {{Y, M, D}, {H, MM, S}}}} ->
CreatedAt = io_lib:format("~p-~p-~p ~p:~p:~p", [Y, M, D, H, MM, S]),
{ok, [{filename, list_to_binary(Filename)},
{size, Size},
{created_at, list_to_binary(CreatedAt)},
{node, node()}
]};
{error, Reason} ->
{error, Reason}
{ok, #{filename => list_to_binary(Filename),
size => Size,
created_at => list_to_binary(CreatedAt),
node => node()
}};
Error -> Error
end;
{error, Reason} ->
{error, Reason}
Error -> Error
end.

View File

@ -44,7 +44,6 @@ groups() ->
t_clients_cmd,
t_vm_cmd,
t_plugins_cmd,
t_modules_cmd,
t_trace_cmd,
t_broker_cmd,
t_router_cmd,
@ -59,11 +58,11 @@ apps() ->
init_per_suite(Config) ->
ekka_mnesia:start(),
emqx_mgmt_auth:mnesia(boot),
emqx_ct_helpers:start_apps([emqx_modules, emqx_management, emqx_auth_mnesia]),
emqx_ct_helpers:start_apps([emqx_management, emqx_auth_mnesia]),
Config.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([emqx_management, emqx_auth_mnesia, emqx_modules]).
emqx_ct_helpers:stop_apps([emqx_management, emqx_auth_mnesia]).
t_app(_Config) ->
{ok, AppSecret} = emqx_mgmt_auth:add_app(<<"app_id">>, <<"app_name">>),
@ -329,19 +328,6 @@ t_plugins_cmd(_) ->
),
unmock_print().
t_modules_cmd(_) ->
mock_print(),
meck:new(emqx_modules, [non_strict, passthrough]),
meck:expect(emqx_modules, load, fun(_) -> ok end),
meck:expect(emqx_modules, unload, fun(_) -> ok end),
meck:expect(emqx_modules, reload, fun(_) -> ok end),
?assertEqual(emqx_mgmt_cli:modules(["list"]), ok),
?assertEqual(emqx_mgmt_cli:modules(["load", "emqx_mod_presence"]),
"Module emqx_mod_presence loaded successfully.\n"),
?assertEqual(emqx_mgmt_cli:modules(["unload", "emqx_mod_presence"]),
"Module emqx_mod_presence unloaded successfully.\n"),
unmock_print().
t_cli(_) ->
mock_print(),
?assertMatch({match, _}, re:run(emqx_mgmt_cli:status([""]), "status")),

View File

@ -48,7 +48,6 @@ groups() ->
, metrics
, nodes
, plugins
, modules
, acl_cache
, pubsub
, routes_and_subscriptions
@ -58,13 +57,13 @@ groups() ->
}].
init_per_suite(Config) ->
emqx_ct_helpers:start_apps([emqx_modules, emqx_management, emqx_auth_mnesia]),
emqx_ct_helpers:start_apps([emqx_management, emqx_auth_mnesia]),
ekka_mnesia:start(),
emqx_mgmt_auth:mnesia(boot),
Config.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([emqx_auth_mnesia, emqx_management, emqx_modules]),
emqx_ct_helpers:stop_apps([emqx_auth_mnesia, emqx_management]),
ekka_mnesia:ensure_stopped().
init_per_testcase(data, Config) ->
@ -382,64 +381,6 @@ plugins(_) ->
auth_header_()),
?assertEqual(<<"not_started">>, get(<<"message">>, Error2)).
modules(_) ->
emqx_modules:load_module(emqx_mod_presence, false),
timer:sleep(50),
{ok, Modules1} = request_api(get, api_path(["modules"]), auth_header_()),
[Modules11] = filter(get(<<"data">>, Modules1), <<"node">>, atom_to_binary(node(), utf8)),
[Module1] = filter(maps:get(<<"modules">>, Modules11), <<"name">>, <<"emqx_mod_presence">>),
?assertEqual(<<"emqx_mod_presence">>, maps:get(<<"name">>, Module1)),
?assertEqual(true, maps:get(<<"active">>, Module1)),
{ok, _} = request_api(put,
api_path(["modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
{ok, Error1} = request_api(put,
api_path(["modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
?assertEqual(<<"not_started">>, get(<<"message">>, Error1)),
{ok, Modules2} = request_api(get,
api_path(["nodes", atom_to_list(node()), "modules"]),
auth_header_()),
[Module2] = filter(get(<<"data">>, Modules2), <<"name">>, <<"emqx_mod_presence">>),
?assertEqual(<<"emqx_mod_presence">>, maps:get(<<"name">>, Module2)),
?assertEqual(false, maps:get(<<"active">>, Module2)),
{ok, _} = request_api(put,
api_path(["nodes",
atom_to_list(node()),
"modules",
atom_to_list(emqx_mod_presence),
"load"]),
auth_header_()),
{ok, Modules3} = request_api(get,
api_path(["nodes", atom_to_list(node()), "modules"]),
auth_header_()),
[Module3] = filter(get(<<"data">>, Modules3), <<"name">>, <<"emqx_mod_presence">>),
?assertEqual(<<"emqx_mod_presence">>, maps:get(<<"name">>, Module3)),
?assertEqual(true, maps:get(<<"active">>, Module3)),
{ok, _} = request_api(put,
api_path(["nodes",
atom_to_list(node()),
"modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
{ok, Error2} = request_api(put,
api_path(["nodes",
atom_to_list(node()),
"modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
?assertEqual(<<"not_started">>, get(<<"message">>, Error2)),
emqx_modules:unload(emqx_mod_presence).
acl_cache(_) ->
ClientId = <<"client1">>,
Topic = <<"mytopic">>,

View File

@ -176,7 +176,10 @@ write_loaded(false) -> ok.
%%--------------------------------------------------------------------
%% @doc Modules Command
cli(["list"]) ->
foreach(fun(Module) -> print({module, Module}) end, emqx_modules:list());
lists:foreach(fun({Name, Active}) ->
emqx_ctl:print("Module(~s, description=~s, active=~s)~n",
[Name, Name:description(), Active])
end, emqx_modules:list());
cli(["load", Name]) ->
case emqx_modules:load(list_to_atom(Name)) of

View File

@ -72,6 +72,10 @@
, reload/2
]).
-export([ do_load_module/2
, do_unload_module/2
]).
list(#{node := Node}, _Params) ->
return({ok, [format(Module) || Module <- list_modules(Node)]});
@ -79,10 +83,10 @@ list(_Bindings, _Params) ->
return({ok, [format(Node, Modules) || {Node, Modules} <- list_modules()]}).
load(#{node := Node, module := Module}, _Params) ->
return(load_module(Node, Module));
return(do_load_module(Node, Module));
load(#{module := Module}, _Params) ->
Results = [load_module(Node, Module) || {Node, _Info} <- list_nodes()],
Results = [do_load_module(Node, Module) || {Node, _Info} <- emqx_mgmt:list_nodes()],
case lists:filter(fun(Item) -> Item =/= ok end, Results) of
[] ->
return(ok);
@ -91,10 +95,10 @@ load(#{module := Module}, _Params) ->
end.
unload(#{node := Node, module := Module}, _Params) ->
return(unload_module(Node, Module));
return(do_unload_module(Node, Module));
unload(#{module := Module}, _Params) ->
Results = [unload_module(Node, Module) || {Node, _Info} <- list_nodes()],
Results = [do_unload_module(Node, Module) || {Node, _Info} <- emqx_mgmt:list_nodes()],
case lists:filter(fun(Item) -> Item =/= ok end, Results) of
[] ->
return(ok);
@ -109,7 +113,7 @@ reload(#{node := Node, module := Module}, _Params) ->
end;
reload(#{module := Module}, _Params) ->
Results = [reload_module(Node, Module) || {Node, _Info} <- list_nodes()],
Results = [reload_module(Node, Module) || {Node, _Info} <- emqx_mgmt:list_nodes()],
case lists:filter(fun(Item) -> Item =/= ok end, Results) of
[] ->
return(ok);
@ -137,15 +141,15 @@ list_modules(Node) when Node =:= node() ->
list_modules(Node) ->
rpc_call(Node, list_modules, [Node]).
load_module(Node, Module) when Node =:= node() ->
do_load_module(Node, Module) when Node =:= node() ->
emqx_modules:load(Module);
load_module(Node, Module) ->
rpc_call(Node, load_module, [Node, Module]).
do_load_module(Node, Module) ->
rpc_call(Node, do_load_module, [Node, Module]).
unload_module(Node, Module) when Node =:= node() ->
do_unload_module(Node, Module) when Node =:= node() ->
emqx_modules:unload(Module);
unload_module(Node, Module) ->
rpc_call(Node, unload_module, [Node, Module]).
do_unload_module(Node, Module) ->
rpc_call(Node, do_unload_module, [Node, Module]).
reload_module(Node, Module) when Node =:= node() ->
emqx_modules:reload(Module);

View File

@ -21,10 +21,19 @@
-include_lib("eunit/include/eunit.hrl").
-define(CONTENT_TYPE, "application/x-www-form-urlencoded").
-define(HOST, "http://127.0.0.1:8081/").
-define(API_VERSION, "v4").
-define(BASE_PATH, "api").
all() -> emqx_ct:all(?MODULE).
init_per_suite(Config) ->
emqx_ct_helpers:start_apps([emqx_modules], fun set_sepecial_cfg/1),
emqx_ct_helpers:start_apps([emqx_management, emqx_modules], fun set_sepecial_cfg/1),
emqx_ct_http:create_default_app(),
Config.
set_sepecial_cfg(_) ->
@ -32,7 +41,8 @@ set_sepecial_cfg(_) ->
ok.
end_per_suite(_Config) ->
emqx_ct_helpers:stop_apps([emqx_modules]).
emqx_ct_http:delete_default_app(),
emqx_ct_helpers:stop_apps([emqx_modules, emqx_management]).
t_load(_) ->
?assertEqual(ok, emqx_modules:unload()),
@ -45,3 +55,136 @@ t_load(_) ->
t_list(_) ->
?assertMatch([{_, _} | _ ], emqx_modules:list()).
t_modules_api(_) ->
emqx_modules:load_module(emqx_mod_presence, false),
timer:sleep(50),
{ok, Modules1} = request_api(get, api_path(["modules"]), auth_header_()),
[Modules11] = filter(get(<<"data">>, Modules1), <<"node">>, atom_to_binary(node(), utf8)),
[Module1] = filter(maps:get(<<"modules">>, Modules11), <<"name">>, <<"emqx_mod_presence">>),
?assertEqual(<<"emqx_mod_presence">>, maps:get(<<"name">>, Module1)),
?assertEqual(true, maps:get(<<"active">>, Module1)),
{ok, _} = request_api(put,
api_path(["modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
{ok, Error1} = request_api(put,
api_path(["modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
?assertEqual(<<"not_started">>, get(<<"message">>, Error1)),
{ok, Modules2} = request_api(get,
api_path(["nodes", atom_to_list(node()), "modules"]),
auth_header_()),
[Module2] = filter(get(<<"data">>, Modules2), <<"name">>, <<"emqx_mod_presence">>),
?assertEqual(<<"emqx_mod_presence">>, maps:get(<<"name">>, Module2)),
?assertEqual(false, maps:get(<<"active">>, Module2)),
{ok, _} = request_api(put,
api_path(["nodes",
atom_to_list(node()),
"modules",
atom_to_list(emqx_mod_presence),
"load"]),
auth_header_()),
{ok, Modules3} = request_api(get,
api_path(["nodes", atom_to_list(node()), "modules"]),
auth_header_()),
[Module3] = filter(get(<<"data">>, Modules3), <<"name">>, <<"emqx_mod_presence">>),
?assertEqual(<<"emqx_mod_presence">>, maps:get(<<"name">>, Module3)),
?assertEqual(true, maps:get(<<"active">>, Module3)),
{ok, _} = request_api(put,
api_path(["nodes",
atom_to_list(node()),
"modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
{ok, Error2} = request_api(put,
api_path(["nodes",
atom_to_list(node()),
"modules",
atom_to_list(emqx_mod_presence),
"unload"]),
auth_header_()),
?assertEqual(<<"not_started">>, get(<<"message">>, Error2)),
emqx_modules:unload(emqx_mod_presence).
t_modules_cmd(_) ->
mock_print(),
meck:new(emqx_modules, [non_strict, passthrough]),
meck:expect(emqx_modules, load, fun(_) -> ok end),
meck:expect(emqx_modules, unload, fun(_) -> ok end),
meck:expect(emqx_modules, reload, fun(_) -> ok end),
?assertEqual(emqx_modules:cli(["list"]), ok),
?assertEqual(emqx_modules:cli(["load", "emqx_mod_presence"]),
"Module emqx_mod_presence loaded successfully.\n"),
?assertEqual(emqx_modules:cli(["unload", "emqx_mod_presence"]),
"Module emqx_mod_presence unloaded successfully.\n"),
unmock_print().
mock_print() ->
catch meck:unload(emqx_ctl),
meck:new(emqx_ctl, [non_strict, passthrough]),
meck:expect(emqx_ctl, print, fun(Arg) -> emqx_ctl:format(Arg) end),
meck:expect(emqx_ctl, print, fun(Msg, Arg) -> emqx_ctl:format(Msg, Arg) end),
meck:expect(emqx_ctl, usage, fun(Usages) -> emqx_ctl:format_usage(Usages) end),
meck:expect(emqx_ctl, usage, fun(Cmd, Descr) -> emqx_ctl:format_usage(Cmd, Descr) end).
unmock_print() ->
meck:unload(emqx_ctl).
get(Key, ResponseBody) ->
maps:get(Key, jiffy:decode(list_to_binary(ResponseBody), [return_maps])).
request_api(Method, Url, Auth) ->
request_api(Method, Url, [], Auth, []).
request_api(Method, Url, QueryParams, Auth) ->
request_api(Method, Url, QueryParams, Auth, []).
request_api(Method, Url, QueryParams, Auth, []) ->
NewUrl = case QueryParams of
"" -> Url;
_ -> Url ++ "?" ++ QueryParams
end,
do_request_api(Method, {NewUrl, [Auth]});
request_api(Method, Url, QueryParams, Auth, Body) ->
NewUrl = case QueryParams of
"" -> Url;
_ -> Url ++ "?" ++ QueryParams
end,
do_request_api(Method, {NewUrl, [Auth], "application/json", emqx_json:encode(Body)}).
do_request_api(Method, Request)->
ct:pal("Method: ~p, Request: ~p", [Method, Request]),
case httpc:request(Method, Request, [], []) of
{error, socket_closed_remotely} ->
{error, socket_closed_remotely};
{ok, {{"HTTP/1.1", Code, _}, _, Return} }
when Code =:= 200 orelse Code =:= 201 ->
{ok, Return};
{ok, {Reason, _, _}} ->
{error, Reason}
end.
auth_header_() ->
AppId = <<"admin">>,
AppSecret = <<"public">>,
auth_header_(binary_to_list(AppId), binary_to_list(AppSecret)).
auth_header_(User, Pass) ->
Encoded = base64:encode_to_string(lists:append([User,":",Pass])),
{"Authorization","Basic " ++ Encoded}.
api_path(Parts)->
?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts).
filter(List, Key, Value) ->
lists:filter(fun(Item) ->
maps:get(Key, Item) == Value
end, List).