test(bridge): switch `emqx_bridge_api_SUITE` to use new tooling

This commit is contained in:
Andrew Mayorov 2023-06-27 12:52:19 +02:00
parent 687db0893e
commit d568d43fe4
No known key found for this signature in database
GPG Key ID: 2837C62ACFBFED5D
2 changed files with 58 additions and 110 deletions

View File

@ -26,6 +26,7 @@
create_default_app/0,
delete_default_app/0,
default_auth_header/0,
auth_header/1,
auth_header/2
]).
@ -72,6 +73,9 @@ do_request_api(Method, Request, HttpOpts) ->
get_http_data(ResponseBody) ->
emqx_utils_json:decode(ResponseBody, [return_maps]).
auth_header(#{api_key := ApiKey, api_secret := Secret}) ->
auth_header(binary_to_list(ApiKey), binary_to_list(Secret)).
auth_header(User, Pass) ->
Encoded = base64:encode_to_string(lists:append([User, ":", Pass])),
{"Authorization", "Basic " ++ Encoded}.

View File

@ -24,8 +24,6 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("snabbkaffe/include/test_macros.hrl").
-define(SUITE_APPS, [emqx_conf, emqx_authn, emqx_management, emqx_rule_engine, emqx_bridge]).
-define(BRIDGE_TYPE_HTTP, <<"webhook">>).
-define(BRIDGE_NAME, (atom_to_binary(?FUNCTION_NAME))).
-define(URL(PORT, PATH),
@ -74,6 +72,19 @@
}).
-define(HTTP_BRIDGE(URL), ?HTTP_BRIDGE(URL, ?BRIDGE_NAME)).
-define(APPSPECS, [
emqx_conf,
emqx,
emqx_authn,
emqx_management,
{emqx_rule_engine, "rule_engine { rules {} }"},
{emqx_bridge, "bridges {}"}
]).
-define(APPSPEC_DASHBOARD,
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
).
all() ->
[
{group, single},
@ -104,106 +115,43 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
init_per_group(cluster, Config) ->
Cluster = mk_cluster_specs(Config),
ct:pal("Starting ~p", [Cluster]),
Nodes = [
emqx_common_test_helpers:start_slave(Name, Opts)
|| {Name, Opts} <- Cluster
],
[NodePrimary | NodesRest] = Nodes,
ok = erpc:call(NodePrimary, fun() -> init_node(primary) end),
_ = [ok = erpc:call(Node, fun() -> init_node(regular) end) || Node <- NodesRest],
[{group, cluster}, {cluster_nodes, Nodes}, {api_node, NodePrimary} | Config];
init_per_group(cluster_later_join, Config) ->
Cluster = mk_cluster_specs(Config, #{join_to => undefined}),
ct:pal("Starting ~p", [Cluster]),
Nodes = [
emqx_common_test_helpers:start_slave(Name, Opts)
|| {Name, Opts} <- Cluster
],
[NodePrimary | NodesRest] = Nodes,
ok = erpc:call(NodePrimary, fun() -> init_node(primary) end),
_ = [ok = erpc:call(Node, fun() -> init_node(regular) end) || Node <- NodesRest],
[{group, cluster_later_join}, {cluster_nodes, Nodes}, {api_node, NodePrimary} | Config];
init_per_group(_, Config) ->
ok = emqx_mgmt_api_test_util:init_suite(?SUITE_APPS),
ok = load_suite_config(emqx_rule_engine),
ok = load_suite_config(emqx_bridge),
[{group, single}, {api_node, node()} | Config].
init_per_group(cluster = Name, Config) ->
Nodes = [NodePrimary | _] = mk_cluster(Name, Config),
init_api([{group, Name}, {cluster_nodes, Nodes}, {node, NodePrimary} | Config]);
init_per_group(cluster_later_join = Name, Config) ->
Nodes = [NodePrimary | _] = mk_cluster(Name, Config, #{join_to => undefined}),
init_api([{group, Name}, {cluster_nodes, Nodes}, {node, NodePrimary} | Config]);
init_per_group(Name, Config) ->
WorkDir = filename:join(?config(priv_dir, Config), Name),
Apps = emqx_cth_suite:start(?APPSPECS ++ [?APPSPEC_DASHBOARD], #{work_dir => WorkDir}),
init_api([{group, single}, {group_apps, Apps}, {node, node()} | Config]).
mk_cluster_specs(Config) ->
mk_cluster_specs(Config, #{}).
init_api(Config) ->
APINode = ?config(node, Config),
{ok, App} = erpc:call(APINode, emqx_common_test_http, create_default_app, []),
[{api, App} | Config].
mk_cluster_specs(Config, Opts) ->
Specs = [
{core, emqx_bridge_api_SUITE1, #{}},
{core, emqx_bridge_api_SUITE2, #{}}
],
CommonOpts = Opts#{
env => [{emqx, boot_modules, [broker]}],
apps => [],
% NOTE
% We need to start all those apps _after_ the cluster becomes stable, in the
% `init_node/1`. This is because usual order is broken in very subtle way:
% 1. Node starts apps including `mria` and `emqx_conf` which starts `emqx_cluster_rpc`.
% 2. The `emqx_cluster_rpc` sets up a mnesia table subscription during initialization.
% 3. In the meantime `mria` joins the cluster and notices it should restart.
% 4. Mnesia subscription becomes lost during restarts (god knows why).
% Yet we need to load them before, so that mria / mnesia will know which tables
% should be created in the cluster.
% TODO
% We probably should hide these intricacies behind the `emqx_common_test_helpers`.
load_apps => ?SUITE_APPS ++ [emqx_dashboard],
env_handler => fun load_suite_config/1,
load_schema => false,
priv_data_dir => ?config(priv_dir, Config)
},
emqx_common_test_helpers:emqx_cluster(Specs, CommonOpts).
mk_cluster(Name, Config) ->
mk_cluster(Name, Config, #{}).
init_node(Type) ->
ok = emqx_common_test_helpers:start_apps(?SUITE_APPS, fun load_suite_config/1),
case Type of
primary ->
ok = emqx_dashboard_desc_cache:init(),
ok = emqx_config:put(
[dashboard, listeners],
#{http => #{bind => 18083, proxy_header => false}}
),
ok = emqx_dashboard:start_listeners(),
ready = emqx_dashboard_listener:regenerate_minirest_dispatch(),
{ok, _App} = emqx_common_test_http:create_default_app(),
ok;
regular ->
ok
end.
load_suite_config(emqx_rule_engine) ->
ok = emqx_common_test_helpers:load_config(
emqx_rule_engine_schema,
<<"rule_engine { rules {} }">>
);
load_suite_config(emqx_bridge) ->
ok = emqx_common_test_helpers:load_config(
emqx_bridge_schema,
<<"bridges {}">>
);
load_suite_config(_) ->
ok.
mk_cluster(Name, Config, Opts) ->
Node1Apps = ?APPSPECS ++ [?APPSPEC_DASHBOARD],
Node2Apps = ?APPSPECS,
emqx_cth_cluster:start(
[
{emqx_bridge_api_SUITE1, Opts#{role => core, apps => Node1Apps}},
{emqx_bridge_api_SUITE2, Opts#{role => core, apps => Node2Apps}}
],
#{work_dir => filename:join(?config(priv_dir, Config), Name)}
).
end_per_group(Group, Config) when
Group =:= cluster;
Group =:= cluster_later_join
->
ok = lists:foreach(
fun(Node) ->
_ = erpc:call(Node, emqx_common_test_helpers, stop_apps, [?SUITE_APPS]),
emqx_common_test_helpers:stop_slave(Node)
end,
?config(cluster_nodes, Config)
);
end_per_group(_, _Config) ->
emqx_mgmt_api_test_util:end_suite(?SUITE_APPS),
ok = emqx_cth_cluster:stop(?config(cluster_nodes, Config));
end_per_group(_, Config) ->
emqx_cth_suite:stop(?config(group_apps, Config)),
ok.
init_per_testcase(t_broken_bpapi_vsn, Config) ->
@ -229,7 +177,7 @@ end_per_testcase(t_old_bpapi_vsn, Config) ->
end_per_testcase(_, Config) ->
Sock = ?config(sock, Config),
Acceptor = ?config(acceptor, Config),
Node = ?config(api_node, Config),
Node = ?config(node, Config),
ok = emqx_common_test_helpers:call_janitor(),
ok = stop_http_server(Sock, Acceptor),
ok = erpc:call(Node, fun clear_resources/0),
@ -901,7 +849,7 @@ t_start_stop_inconsistent_bridge_cluster(Config) ->
start_stop_inconsistent_bridge(Type, Config) ->
Port = ?config(port, Config),
URL = ?URL(Port, "abc"),
Node = ?config(api_node, Config),
Node = ?config(node, Config),
erpc:call(Node, fun() ->
meck:new(emqx_bridge_resource, [passthrough, no_link]),
@ -1352,9 +1300,7 @@ t_inconsistent_webhook_request_timeouts(Config) ->
t_cluster_later_join_metrics(Config) ->
Port = ?config(port, Config),
APINode = ?config(api_node, Config),
ClusterNodes = ?config(cluster_nodes, Config),
[OtherNode | _] = ClusterNodes -- [APINode],
[PrimaryNode, OtherNode | _] = ?config(cluster_nodes, Config),
URL1 = ?URL(Port, "path1"),
Name = ?BRIDGE_NAME,
BridgeParams = ?HTTP_BRIDGE(URL1, Name),
@ -1372,7 +1318,7 @@ t_cluster_later_join_metrics(Config) ->
request_json(get, uri(["bridges", BridgeID, "metrics"]), Config)
),
%% Now join the other node join with the api node.
ok = erpc:call(OtherNode, ekka, join, [APINode]),
ok = erpc:call(OtherNode, ekka, join, [PrimaryNode]),
%% Check metrics; shouldn't crash even if the bridge is not
%% ready on the node that just joined the cluster.
?assertMatch(
@ -1420,8 +1366,9 @@ request(Method, {operation, Type, Op, BridgeID}, Body, Config) ->
URL = operation_path(Type, Op, BridgeID, Config),
request(Method, URL, Body, Config);
request(Method, URL, Body, Config) ->
AuthHeader = emqx_common_test_http:auth_header(?config(api, Config)),
Opts = #{compatible_mode => true, httpc_req_opts => [{body_format, binary}]},
emqx_mgmt_api_test_util:request_api(Method, URL, [], auth_header(Config), Body, Opts).
emqx_mgmt_api_test_util:request_api(Method, URL, [], AuthHeader, Body, Opts).
request(Method, URL, Body, Decoder, Config) ->
case request(Method, URL, Body, Config) of
@ -1437,11 +1384,8 @@ request_json(Method, URLLike, Config) ->
request_json(Method, URLLike, Body, Config) ->
request(Method, URLLike, Body, fun json/1, Config).
auth_header(Config) ->
erpc:call(?config(api_node, Config), emqx_common_test_http, default_auth_header, []).
operation_path(node, Oper, BridgeID, Config) ->
uri(["nodes", ?config(api_node, Config), "bridges", BridgeID, Oper]);
uri(["nodes", ?config(node, Config), "bridges", BridgeID, Oper]);
operation_path(cluster, Oper, BridgeID, _Config) ->
uri(["bridges", BridgeID, Oper]).
@ -1449,23 +1393,23 @@ enable_path(Enable, BridgeID) ->
uri(["bridges", BridgeID, "enable", Enable]).
publish_message(Topic, Body, Config) ->
Node = ?config(api_node, Config),
Node = ?config(node, Config),
erpc:call(Node, emqx, publish, [emqx_message:make(Topic, Body)]).
update_config(Path, Value, Config) ->
Node = ?config(api_node, Config),
Node = ?config(node, Config),
erpc:call(Node, emqx, update_config, [Path, Value]).
get_raw_config(Path, Config) ->
Node = ?config(api_node, Config),
Node = ?config(node, Config),
erpc:call(Node, emqx, get_raw_config, [Path]).
add_user_auth(Chain, AuthenticatorID, User, Config) ->
Node = ?config(api_node, Config),
Node = ?config(node, Config),
erpc:call(Node, emqx_authentication, add_user, [Chain, AuthenticatorID, User]).
delete_user_auth(Chain, AuthenticatorID, User, Config) ->
Node = ?config(api_node, Config),
Node = ?config(node, Config),
erpc:call(Node, emqx_authentication, delete_user, [Chain, AuthenticatorID, User]).
str(S) when is_list(S) -> S;