emqx_exhook_api_SUITE.erl 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. %%--------------------------------------------------------------------
  2. %% Copyright (c) 2021-2023 EMQ Technologies Co., Ltd. All Rights Reserved.
  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(emqx_exhook_api_SUITE).
  17. -compile(export_all).
  18. -compile(nowarn_export_all).
  19. -include_lib("eunit/include/eunit.hrl").
  20. -define(HOST, "http://127.0.0.1:18083/").
  21. -define(API_VERSION, "v5").
  22. -define(BASE_PATH, "api").
  23. -define(CLUSTER_RPC_SHARD, emqx_cluster_rpc_shard).
  24. -define(DEFAULT_CLUSTER_NAME_ATOM, emqxcl).
  25. -define(CONF_DEFAULT, <<
  26. "\n"
  27. "exhook {\n"
  28. " servers =\n"
  29. " [ { name = default,\n"
  30. " url = \"http://127.0.0.1:9000\",\n"
  31. " ssl = {\"enable\": false}"
  32. " }\n"
  33. " ]\n"
  34. "}\n"
  35. >>).
  36. all() ->
  37. [
  38. t_list,
  39. t_get,
  40. t_add,
  41. t_add_duplicate,
  42. t_move_front,
  43. t_move_rear,
  44. t_move_before,
  45. t_move_after,
  46. t_delete,
  47. t_hooks,
  48. t_update
  49. ].
  50. init_per_suite(Config) ->
  51. application:load(emqx_conf),
  52. ok = ekka:start(),
  53. application:set_env(ekka, cluster_name, ?DEFAULT_CLUSTER_NAME_ATOM),
  54. ok = mria_rlog:wait_for_shards([?CLUSTER_RPC_SHARD], infinity),
  55. meck:new(emqx_alarm, [non_strict, passthrough, no_link]),
  56. meck:expect(emqx_alarm, activate, 3, ok),
  57. meck:expect(emqx_alarm, deactivate, 3, ok),
  58. _ = emqx_exhook_demo_svr:start(),
  59. load_cfg(?CONF_DEFAULT),
  60. emqx_mgmt_api_test_util:init_suite([emqx_exhook]),
  61. [Conf] = emqx:get_raw_config([exhook, servers]),
  62. [{template, Conf} | Config].
  63. end_per_suite(Config) ->
  64. application:set_env(ekka, cluster_name, ?DEFAULT_CLUSTER_NAME_ATOM),
  65. ekka:stop(),
  66. mria:stop(),
  67. mria_mnesia:delete_schema(),
  68. meck:unload(emqx_alarm),
  69. emqx_mgmt_api_test_util:end_suite([emqx_exhook]),
  70. emqx_exhook_demo_svr:stop(),
  71. emqx_exhook_demo_svr:stop(<<"test1">>),
  72. Config.
  73. init_per_testcase(t_add, Config) ->
  74. {ok, _} = emqx_cluster_rpc:start_link(),
  75. _ = emqx_exhook_demo_svr:start(<<"test1">>, 9001),
  76. timer:sleep(200),
  77. Config;
  78. init_per_testcase(_, Config) ->
  79. {ok, _} = emqx_cluster_rpc:start_link(),
  80. timer:sleep(200),
  81. Config.
  82. end_per_testcase(_, Config) ->
  83. case erlang:whereis(node()) of
  84. undefined ->
  85. ok;
  86. P ->
  87. erlang:unlink(P),
  88. erlang:exit(P, kill)
  89. end,
  90. Config.
  91. load_cfg(Cfg) ->
  92. ok = emqx_common_test_helpers:load_config(emqx_exhook_schema, Cfg).
  93. %%--------------------------------------------------------------------
  94. %% Test cases
  95. %%--------------------------------------------------------------------
  96. t_list(_) ->
  97. {ok, Data} = request_api(
  98. get,
  99. api_path(["exhooks"]),
  100. "",
  101. auth_header_()
  102. ),
  103. List = decode_json(Data),
  104. ?assertEqual(1, length(List)),
  105. [Svr] = List,
  106. ?assertMatch(
  107. #{
  108. name := <<"default">>,
  109. metrics := _,
  110. node_metrics := _,
  111. node_status := _,
  112. hooks := _
  113. },
  114. Svr
  115. ).
  116. t_get(_) ->
  117. {ok, Data} = request_api(
  118. get,
  119. api_path(["exhooks", "default"]),
  120. "",
  121. auth_header_()
  122. ),
  123. Svr = decode_json(Data),
  124. ?assertMatch(
  125. #{
  126. name := <<"default">>,
  127. metrics := _,
  128. node_metrics := _,
  129. node_status := _,
  130. hooks := _
  131. },
  132. Svr
  133. ).
  134. t_add(Cfg) ->
  135. Template = proplists:get_value(template, Cfg),
  136. Instance = Template#{
  137. <<"name">> => <<"test1">>,
  138. <<"url">> => "http://127.0.0.1:9001"
  139. },
  140. {ok, Data} = request_api(
  141. post,
  142. api_path(["exhooks"]),
  143. "",
  144. auth_header_(),
  145. Instance
  146. ),
  147. Svr = decode_json(Data),
  148. ?assertMatch(
  149. #{
  150. name := <<"test1">>,
  151. metrics := _,
  152. node_metrics := _,
  153. node_status := _,
  154. hooks := _
  155. },
  156. Svr
  157. ),
  158. ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()).
  159. t_add_duplicate(Cfg) ->
  160. Template = proplists:get_value(template, Cfg),
  161. Instance = Template#{
  162. <<"name">> => <<"test1">>,
  163. <<"url">> => "http://127.0.0.1:9001"
  164. },
  165. {error, _Reason} = request_api(
  166. post,
  167. api_path(["exhooks"]),
  168. "",
  169. auth_header_(),
  170. Instance
  171. ),
  172. ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()).
  173. t_add_with_bad_name(Cfg) ->
  174. Template = proplists:get_value(template, Cfg),
  175. Instance = Template#{
  176. <<"name">> => <<"🤔">>,
  177. <<"url">> => "http://127.0.0.1:9001"
  178. },
  179. {error, _Reason} = request_api(
  180. post,
  181. api_path(["exhooks"]),
  182. "",
  183. auth_header_(),
  184. Instance
  185. ),
  186. ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()).
  187. t_move_front(_) ->
  188. Result = request_api(
  189. post,
  190. api_path(["exhooks", "default", "move"]),
  191. "",
  192. auth_header_(),
  193. #{position => <<"front">>}
  194. ),
  195. ?assertMatch({ok, <<>>}, Result),
  196. ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()).
  197. t_move_rear(_) ->
  198. Result = request_api(
  199. post,
  200. api_path(["exhooks", "default", "move"]),
  201. "",
  202. auth_header_(),
  203. #{position => <<"rear">>}
  204. ),
  205. ?assertMatch({ok, <<>>}, Result),
  206. ?assertMatch([<<"test1">>, <<"default">>], emqx_exhook_mgr:running()).
  207. t_move_before(_) ->
  208. Result = request_api(
  209. post,
  210. api_path(["exhooks", "default", "move"]),
  211. "",
  212. auth_header_(),
  213. #{position => <<"before:test1">>}
  214. ),
  215. ?assertMatch({ok, <<>>}, Result),
  216. ?assertMatch([<<"default">>, <<"test1">>], emqx_exhook_mgr:running()).
  217. t_move_after(_) ->
  218. Result = request_api(
  219. post,
  220. api_path(["exhooks", "default", "move"]),
  221. "",
  222. auth_header_(),
  223. #{position => <<"after:test1">>}
  224. ),
  225. ?assertMatch({ok, <<>>}, Result),
  226. ?assertMatch([<<"test1">>, <<"default">>], emqx_exhook_mgr:running()).
  227. t_delete(_) ->
  228. Result = request_api(
  229. delete,
  230. api_path(["exhooks", "test1"]),
  231. "",
  232. auth_header_()
  233. ),
  234. ?assertMatch({ok, <<>>}, Result),
  235. ?assertMatch([<<"default">>], emqx_exhook_mgr:running()).
  236. t_hooks(_Cfg) ->
  237. {ok, Data} = request_api(
  238. get,
  239. api_path(["exhooks", "default", "hooks"]),
  240. "",
  241. auth_header_()
  242. ),
  243. [Hook1 | _] = decode_json(Data),
  244. ?assertMatch(
  245. #{
  246. name := _,
  247. params := _,
  248. metrics := _,
  249. node_metrics := _
  250. },
  251. Hook1
  252. ).
  253. t_update(Cfg) ->
  254. Template = proplists:get_value(template, Cfg),
  255. Instance = Template#{<<"enable">> => false},
  256. {ok, <<"{\"", _/binary>>} = request_api(
  257. put,
  258. api_path(["exhooks", "default"]),
  259. "",
  260. auth_header_(),
  261. Instance
  262. ),
  263. ?assertMatch([], emqx_exhook_mgr:running()).
  264. decode_json(Data) ->
  265. BinJosn = emqx_utils_json:decode(Data, [return_maps]),
  266. emqx_utils_maps:unsafe_atom_key_map(BinJosn).
  267. request_api(Method, Url, Auth) ->
  268. request_api(Method, Url, [], Auth, []).
  269. request_api(Method, Url, QueryParams, Auth) ->
  270. request_api(Method, Url, QueryParams, Auth, []).
  271. request_api(Method, Url, QueryParams, Auth, []) ->
  272. NewUrl =
  273. case QueryParams of
  274. "" -> Url;
  275. _ -> Url ++ "?" ++ QueryParams
  276. end,
  277. do_request_api(Method, {NewUrl, [Auth]});
  278. request_api(Method, Url, QueryParams, Auth, Body) ->
  279. NewUrl =
  280. case QueryParams of
  281. "" -> Url;
  282. _ -> Url ++ "?" ++ QueryParams
  283. end,
  284. do_request_api(Method, {NewUrl, [Auth], "application/json", emqx_utils_json:encode(Body)}).
  285. do_request_api(Method, Request) ->
  286. case httpc:request(Method, Request, [], [{body_format, binary}]) of
  287. {error, socket_closed_remotely} ->
  288. {error, socket_closed_remotely};
  289. {ok, {{"HTTP/1.1", Code, _}, _, Return}} when
  290. Code =:= 200 orelse Code =:= 204 orelse Code =:= 201
  291. ->
  292. {ok, Return};
  293. {ok, {Reason, _, _}} ->
  294. {error, Reason}
  295. end.
  296. auth_header_() ->
  297. emqx_mgmt_api_test_util:auth_header_().
  298. api_path(Parts) ->
  299. ?HOST ++ filename:join([?BASE_PATH, ?API_VERSION] ++ Parts).