|
@@ -27,6 +27,7 @@
|
|
|
|
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
-include_lib("common_test/include/ct.hrl").
|
|
-include_lib("common_test/include/ct.hrl").
|
|
|
|
|
+-include_lib("snabbkaffe/include/snabbkaffe.hrl").
|
|
|
|
|
|
|
|
-define(BRIDGE_TYPE, <<"webhook">>).
|
|
-define(BRIDGE_TYPE, <<"webhook">>).
|
|
|
-define(BRIDGE_NAME, atom_to_binary(?MODULE)).
|
|
-define(BRIDGE_NAME, atom_to_binary(?MODULE)).
|
|
@@ -60,10 +61,23 @@ init_per_testcase(t_send_async_connection_timeout, Config) ->
|
|
|
ResponseDelayMS = 500,
|
|
ResponseDelayMS = 500,
|
|
|
Server = start_http_server(#{response_delay_ms => ResponseDelayMS}),
|
|
Server = start_http_server(#{response_delay_ms => ResponseDelayMS}),
|
|
|
[{http_server, Server}, {response_delay_ms, ResponseDelayMS} | Config];
|
|
[{http_server, Server}, {response_delay_ms, ResponseDelayMS} | Config];
|
|
|
|
|
+init_per_testcase(t_path_not_found, Config) ->
|
|
|
|
|
+ HTTPPath = <<"/nonexisting/path">>,
|
|
|
|
|
+ ServerSSLOpts = false,
|
|
|
|
|
+ {ok, {HTTPPort, _Pid}} = emqx_connector_web_hook_server:start_link(
|
|
|
|
|
+ _Port = random, HTTPPath, ServerSSLOpts
|
|
|
|
|
+ ),
|
|
|
|
|
+ ok = emqx_connector_web_hook_server:set_handler(not_found_http_handler()),
|
|
|
|
|
+ [{http_server, #{port => HTTPPort, path => HTTPPath}} | Config];
|
|
|
init_per_testcase(_TestCase, Config) ->
|
|
init_per_testcase(_TestCase, Config) ->
|
|
|
Server = start_http_server(#{response_delay_ms => 0}),
|
|
Server = start_http_server(#{response_delay_ms => 0}),
|
|
|
[{http_server, Server} | Config].
|
|
[{http_server, Server} | Config].
|
|
|
|
|
|
|
|
|
|
+end_per_testcase(t_path_not_found, _Config) ->
|
|
|
|
|
+ ok = emqx_connector_web_hook_server:stop(),
|
|
|
|
|
+ emqx_bridge_testlib:delete_all_bridges(),
|
|
|
|
|
+ emqx_common_test_helpers:call_janitor(),
|
|
|
|
|
+ ok;
|
|
|
end_per_testcase(_TestCase, Config) ->
|
|
end_per_testcase(_TestCase, Config) ->
|
|
|
case ?config(http_server, Config) of
|
|
case ?config(http_server, Config) of
|
|
|
undefined -> ok;
|
|
undefined -> ok;
|
|
@@ -176,24 +190,34 @@ parse_http_request_assertive(ReqStr0) ->
|
|
|
bridge_async_config(#{port := Port} = Config) ->
|
|
bridge_async_config(#{port := Port} = Config) ->
|
|
|
Type = maps:get(type, Config, ?BRIDGE_TYPE),
|
|
Type = maps:get(type, Config, ?BRIDGE_TYPE),
|
|
|
Name = maps:get(name, Config, ?BRIDGE_NAME),
|
|
Name = maps:get(name, Config, ?BRIDGE_NAME),
|
|
|
|
|
+ Path = maps:get(path, Config, ""),
|
|
|
PoolSize = maps:get(pool_size, Config, 1),
|
|
PoolSize = maps:get(pool_size, Config, 1),
|
|
|
QueryMode = maps:get(query_mode, Config, "async"),
|
|
QueryMode = maps:get(query_mode, Config, "async"),
|
|
|
ConnectTimeout = maps:get(connect_timeout, Config, "1s"),
|
|
ConnectTimeout = maps:get(connect_timeout, Config, "1s"),
|
|
|
RequestTimeout = maps:get(request_timeout, Config, "10s"),
|
|
RequestTimeout = maps:get(request_timeout, Config, "10s"),
|
|
|
ResumeInterval = maps:get(resume_interval, Config, "1s"),
|
|
ResumeInterval = maps:get(resume_interval, Config, "1s"),
|
|
|
ResourceRequestTTL = maps:get(resource_request_ttl, Config, "infinity"),
|
|
ResourceRequestTTL = maps:get(resource_request_ttl, Config, "infinity"),
|
|
|
|
|
+ LocalTopic =
|
|
|
|
|
+ case maps:find(local_topic, Config) of
|
|
|
|
|
+ {ok, LT} ->
|
|
|
|
|
+ lists:flatten(["local_topic = \"", LT, "\""]);
|
|
|
|
|
+ error ->
|
|
|
|
|
+ ""
|
|
|
|
|
+ end,
|
|
|
ConfigString = io_lib:format(
|
|
ConfigString = io_lib:format(
|
|
|
"bridges.~s.~s {\n"
|
|
"bridges.~s.~s {\n"
|
|
|
- " url = \"http://localhost:~p\"\n"
|
|
|
|
|
|
|
+ " url = \"http://localhost:~p~s\"\n"
|
|
|
" connect_timeout = \"~p\"\n"
|
|
" connect_timeout = \"~p\"\n"
|
|
|
" enable = true\n"
|
|
" enable = true\n"
|
|
|
|
|
+ %% local_topic
|
|
|
|
|
+ " ~s\n"
|
|
|
" enable_pipelining = 100\n"
|
|
" enable_pipelining = 100\n"
|
|
|
" max_retries = 2\n"
|
|
" max_retries = 2\n"
|
|
|
" method = \"post\"\n"
|
|
" method = \"post\"\n"
|
|
|
" pool_size = ~p\n"
|
|
" pool_size = ~p\n"
|
|
|
" pool_type = \"random\"\n"
|
|
" pool_type = \"random\"\n"
|
|
|
" request_timeout = \"~s\"\n"
|
|
" request_timeout = \"~s\"\n"
|
|
|
- " body = \"${id}\""
|
|
|
|
|
|
|
+ " body = \"${id}\"\n"
|
|
|
" resource_opts {\n"
|
|
" resource_opts {\n"
|
|
|
" inflight_window = 100\n"
|
|
" inflight_window = 100\n"
|
|
|
" health_check_interval = \"15s\"\n"
|
|
" health_check_interval = \"15s\"\n"
|
|
@@ -213,7 +237,9 @@ bridge_async_config(#{port := Port} = Config) ->
|
|
|
Type,
|
|
Type,
|
|
|
Name,
|
|
Name,
|
|
|
Port,
|
|
Port,
|
|
|
|
|
+ Path,
|
|
|
ConnectTimeout,
|
|
ConnectTimeout,
|
|
|
|
|
+ LocalTopic,
|
|
|
PoolSize,
|
|
PoolSize,
|
|
|
RequestTimeout,
|
|
RequestTimeout,
|
|
|
QueryMode,
|
|
QueryMode,
|
|
@@ -244,6 +270,20 @@ make_bridge(Config) ->
|
|
|
),
|
|
),
|
|
|
emqx_bridge_resource:bridge_id(Type, Name).
|
|
emqx_bridge_resource:bridge_id(Type, Name).
|
|
|
|
|
|
|
|
|
|
+not_found_http_handler() ->
|
|
|
|
|
+ TestPid = self(),
|
|
|
|
|
+ fun(Req0, State) ->
|
|
|
|
|
+ {ok, Body, Req} = cowboy_req:read_body(Req0),
|
|
|
|
|
+ TestPid ! {http, cowboy_req:headers(Req), Body},
|
|
|
|
|
+ Rep = cowboy_req:reply(
|
|
|
|
|
+ 404,
|
|
|
|
|
+ #{<<"content-type">> => <<"text/plain">>},
|
|
|
|
|
+ <<"not found">>,
|
|
|
|
|
+ Req
|
|
|
|
|
+ ),
|
|
|
|
|
+ {ok, Rep, State}
|
|
|
|
|
+ end.
|
|
|
|
|
+
|
|
|
%%------------------------------------------------------------------------------
|
|
%%------------------------------------------------------------------------------
|
|
|
%% Testcases
|
|
%% Testcases
|
|
|
%%------------------------------------------------------------------------------
|
|
%%------------------------------------------------------------------------------
|
|
@@ -367,6 +407,51 @@ t_start_stop(Config) ->
|
|
|
?BRIDGE_TYPE, ?BRIDGE_NAME, BridgeConfig, emqx_connector_http_stopped
|
|
?BRIDGE_TYPE, ?BRIDGE_NAME, BridgeConfig, emqx_connector_http_stopped
|
|
|
).
|
|
).
|
|
|
|
|
|
|
|
|
|
+t_path_not_found(Config) ->
|
|
|
|
|
+ ?check_trace(
|
|
|
|
|
+ begin
|
|
|
|
|
+ #{port := Port, path := Path} = ?config(http_server, Config),
|
|
|
|
|
+ MQTTTopic = <<"t/webhook">>,
|
|
|
|
|
+ BridgeConfig = bridge_async_config(#{
|
|
|
|
|
+ type => ?BRIDGE_TYPE,
|
|
|
|
|
+ name => ?BRIDGE_NAME,
|
|
|
|
|
+ local_topic => MQTTTopic,
|
|
|
|
|
+ port => Port,
|
|
|
|
|
+ path => Path
|
|
|
|
|
+ }),
|
|
|
|
|
+ {ok, _} = emqx_bridge:create(?BRIDGE_TYPE, ?BRIDGE_NAME, BridgeConfig),
|
|
|
|
|
+ Msg = emqx_message:make(MQTTTopic, <<"{}">>),
|
|
|
|
|
+ emqx:publish(Msg),
|
|
|
|
|
+ receive
|
|
|
|
|
+ {http, _Headers, _Req} ->
|
|
|
|
|
+ ok
|
|
|
|
|
+ after 1_000 ->
|
|
|
|
|
+ ct:pal("mailbox: ~p", [process_info(self(), messages)]),
|
|
|
|
|
+ ct:fail("http request not made")
|
|
|
|
|
+ end,
|
|
|
|
|
+ ?retry(
|
|
|
|
|
+ _Interval = 500,
|
|
|
|
|
+ _NAttempts = 20,
|
|
|
|
|
+ ?assertMatch(
|
|
|
|
|
+ #{
|
|
|
|
|
+ counters := #{
|
|
|
|
|
+ matched := 1,
|
|
|
|
|
+ failed := 1,
|
|
|
|
|
+ success := 0
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ emqx_bridge:get_metrics(?BRIDGE_TYPE, ?BRIDGE_NAME)
|
|
|
|
|
+ )
|
|
|
|
|
+ ),
|
|
|
|
|
+ ok
|
|
|
|
|
+ end,
|
|
|
|
|
+ fun(Trace) ->
|
|
|
|
|
+ ?assertEqual([], ?of_kind(webhook_will_retry_async, Trace)),
|
|
|
|
|
+ ok
|
|
|
|
|
+ end
|
|
|
|
|
+ ),
|
|
|
|
|
+ ok.
|
|
|
|
|
+
|
|
|
%% helpers
|
|
%% helpers
|
|
|
do_t_async_retries(TestContext, Error, Fn) ->
|
|
do_t_async_retries(TestContext, Error, Fn) ->
|
|
|
#{error_attempts := ErrorAttempts} = TestContext,
|
|
#{error_attempts := ErrorAttempts} = TestContext,
|