emqx_exhook_api_SUITE.erl 8.1 KB

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