Add APIs for subscription add / delete
This commit is contained in:
parent
599f5c8d4f
commit
786a6eb696
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
-export_type([config/0, connection/0]).
|
-export_type([config/0, connection/0]).
|
||||||
|
|
||||||
-optional_callbacks([]).
|
-optional_callbacks([ensure_subscribed/3, ensure_unsubscribed/2]).
|
||||||
|
|
||||||
%% map fields depend on implementation
|
%% map fields depend on implementation
|
||||||
-type config() :: map().
|
-type config() :: map().
|
||||||
|
@ -26,6 +26,8 @@
|
||||||
-type conn_ref() :: term().
|
-type conn_ref() :: term().
|
||||||
-type batch() :: emqx_protal:batch().
|
-type batch() :: emqx_protal:batch().
|
||||||
-type ack_ref() :: emqx_portal:ack_ref().
|
-type ack_ref() :: emqx_portal:ack_ref().
|
||||||
|
-type topic() :: emqx_topic:topic().
|
||||||
|
-type qos() :: emqx_mqtt_types:qos().
|
||||||
|
|
||||||
-include("logger.hrl").
|
-include("logger.hrl").
|
||||||
|
|
||||||
|
@ -42,6 +44,10 @@
|
||||||
%% called when owner is shutting down.
|
%% called when owner is shutting down.
|
||||||
-callback stop(conn_ref(), connection()) -> ok.
|
-callback stop(conn_ref(), connection()) -> ok.
|
||||||
|
|
||||||
|
-callback ensure_subscribed(connection(), topic(), qos()) -> ok.
|
||||||
|
|
||||||
|
-callback ensure_unsubscribed(connection(), topic()) -> ok.
|
||||||
|
|
||||||
start(Module, Config) ->
|
start(Module, Config) ->
|
||||||
case Module:start(Config) of
|
case Module:start(Config) of
|
||||||
{ok, Ref, Conn} ->
|
{ok, Ref, Conn} ->
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
|
|
||||||
%% management APIs
|
%% management APIs
|
||||||
-export([get_forwards/1, ensure_forward_present/2, ensure_forward_absent/2]).
|
-export([get_forwards/1, ensure_forward_present/2, ensure_forward_absent/2]).
|
||||||
-export([get_subscriptions/1]). %, add_subscription/3, del_subscription/2]).
|
-export([get_subscriptions/1, ensure_subscription_present/3, ensure_subscription_absent/2]).
|
||||||
|
|
||||||
-export_type([config/0,
|
-export_type([config/0,
|
||||||
batch/0,
|
batch/0,
|
||||||
|
@ -142,17 +142,32 @@ handle_ack(Pid, Ref) when node() =:= node(Pid) ->
|
||||||
-spec get_forwards(id()) -> [topic()].
|
-spec get_forwards(id()) -> [topic()].
|
||||||
get_forwards(Id) -> gen_statem:call(id(Id), get_forwards, timer:seconds(1000)).
|
get_forwards(Id) -> gen_statem:call(id(Id), get_forwards, timer:seconds(1000)).
|
||||||
|
|
||||||
|
%% @doc Return all subscriptions (subscription over mqtt connection to remote broker).
|
||||||
|
-spec get_subscriptions(id()) -> [{emqx_topic:topic(), qos()}].
|
||||||
|
get_subscriptions(Id) -> gen_statem:call(id(Id), get_subscriptions).
|
||||||
|
|
||||||
%% @doc Add a new forward (local topic subscription).
|
%% @doc Add a new forward (local topic subscription).
|
||||||
-spec ensure_forward_present(id(), topic()) -> ok.
|
-spec ensure_forward_present(id(), topic()) -> ok.
|
||||||
ensure_forward_present(Id, Topic) ->
|
ensure_forward_present(Id, Topic) ->
|
||||||
gen_statem:call(id(Id), {ensure_forward_present, topic(Topic)}).
|
gen_statem:call(id(Id), {ensure_present, forwards, topic(Topic)}).
|
||||||
|
|
||||||
|
%% @doc Ensure a forward topic is deleted.
|
||||||
-spec ensure_forward_absent(id(), topic()) -> ok.
|
-spec ensure_forward_absent(id(), topic()) -> ok.
|
||||||
ensure_forward_absent(Id, Topic) ->
|
ensure_forward_absent(Id, Topic) ->
|
||||||
gen_statem:call(id(Id), {ensure_forward_absent, topic(Topic)}).
|
gen_statem:call(id(Id), {ensure_absent, forwards, topic(Topic)}).
|
||||||
|
|
||||||
-spec get_subscriptions(id()) -> [{emqx_topic:topic(), qos()}].
|
%% @doc Ensure subscribed to remote topic.
|
||||||
get_subscriptions(Id) -> gen_statem:call(id(Id), get_subscriptions).
|
%% NOTE: only applicable when connection module is emqx_portal_mqtt
|
||||||
|
%% return `{error, no_remote_subscription_support}' otherwise.
|
||||||
|
-spec ensure_subscription_present(id(), topic(), qos()) -> ok | {error, any()}.
|
||||||
|
ensure_subscription_present(Id, Topic, QoS) ->
|
||||||
|
gen_statem:call(id(Id), {ensure_present, subscriptions, {topic(Topic), QoS}}).
|
||||||
|
|
||||||
|
%% @doc Ensure unsubscribed from remote topic.
|
||||||
|
%% NOTE: only applicable when connection module is emqx_portal_mqtt
|
||||||
|
-spec ensure_subscription_absent(id(), topic()) -> ok.
|
||||||
|
ensure_subscription_absent(Id, Topic) ->
|
||||||
|
gen_statem:call(id(Id), {ensure_absent, subscriptions, topic(Topic)}).
|
||||||
|
|
||||||
callback_mode() -> [state_functions, state_enter].
|
callback_mode() -> [state_functions, state_enter].
|
||||||
|
|
||||||
|
@ -187,7 +202,7 @@ init(Config) ->
|
||||||
mountpoint,
|
mountpoint,
|
||||||
forwards
|
forwards
|
||||||
], Config#{subscriptions => Subs}),
|
], Config#{subscriptions => Subs}),
|
||||||
ConnectFun = fun() -> emqx_portal_connect:start(ConnectModule, ConnectConfig) end,
|
ConnectFun = fun(SubsX) -> emqx_portal_connect:start(ConnectModule, ConnectConfig#{subscriptions := SubsX}) end,
|
||||||
{ok, connecting,
|
{ok, connecting,
|
||||||
#{connect_module => ConnectModule,
|
#{connect_module => ConnectModule,
|
||||||
connect_fun => ConnectFun,
|
connect_fun => ConnectFun,
|
||||||
|
@ -217,8 +232,10 @@ connecting(enter, connected, #{reconnect_delay_ms := Timeout}) ->
|
||||||
Action = {state_timeout, Timeout, reconnect},
|
Action = {state_timeout, Timeout, reconnect},
|
||||||
{keep_state_and_data, Action};
|
{keep_state_and_data, Action};
|
||||||
connecting(enter, connecting, #{reconnect_delay_ms := Timeout,
|
connecting(enter, connecting, #{reconnect_delay_ms := Timeout,
|
||||||
connect_fun := ConnectFun} = State) ->
|
connect_fun := ConnectFun,
|
||||||
case ConnectFun() of
|
subscriptions := Subs
|
||||||
|
} = State) ->
|
||||||
|
case ConnectFun(Subs) of
|
||||||
{ok, ConnRef, Conn} ->
|
{ok, ConnRef, Conn} ->
|
||||||
Action = {state_timeout, 0, connected},
|
Action = {state_timeout, 0, connected},
|
||||||
{keep_state, State#{conn_ref => ConnRef, connection => Conn}, Action};
|
{keep_state, State#{conn_ref => ConnRef, connection => Conn}, Action};
|
||||||
|
@ -277,7 +294,7 @@ connected(info, {batch_ack, Ref}, State) ->
|
||||||
%% try re-connect then re-send
|
%% try re-connect then re-send
|
||||||
{next_state, connecting, disconnect(State)};
|
{next_state, connecting, disconnect(State)};
|
||||||
{ok, NewState} ->
|
{ok, NewState} ->
|
||||||
{keep_state, NewState}
|
{keep_state, NewState, ?maybe_send}
|
||||||
end;
|
end;
|
||||||
connected(Type, Content, State) ->
|
connected(Type, Content, State) ->
|
||||||
common(connected, Type, Content, State).
|
common(connected, Type, Content, State).
|
||||||
|
@ -285,28 +302,14 @@ connected(Type, Content, State) ->
|
||||||
%% Common handlers
|
%% Common handlers
|
||||||
common(_StateName, {call, From}, get_forwards, #{forwards := Forwards}) ->
|
common(_StateName, {call, From}, get_forwards, #{forwards := Forwards}) ->
|
||||||
{keep_state_and_data, [{reply, From, Forwards}]};
|
{keep_state_and_data, [{reply, From, Forwards}]};
|
||||||
common(_StateName, {call, From}, {ensure_forward_present, Topic},
|
|
||||||
#{forwards := Forwards} = State) ->
|
|
||||||
case lists:member(Topic, Forwards) of
|
|
||||||
true ->
|
|
||||||
{keep_state_and_data, [{reply, From, ok}]};
|
|
||||||
false ->
|
|
||||||
ok = subscribe_local_topic(Topic),
|
|
||||||
{keep_state, State#{forwards := lists:usort([Topic | Forwards])},
|
|
||||||
[{reply, From, ok}]}
|
|
||||||
end;
|
|
||||||
common(_StateName, {call, From}, {ensure_forward_absent, Topic},
|
|
||||||
#{forwards := Forwards} = State) ->
|
|
||||||
case lists:member(Topic, Forwards) of
|
|
||||||
true ->
|
|
||||||
emqx_broker:unsubscribe(Topic),
|
|
||||||
{keep_state, State#{forwards := lists:delete(Topic, Forwards)},
|
|
||||||
[{reply, From, ok}]};
|
|
||||||
false ->
|
|
||||||
{keep_state_and_data, [{reply, From, ok}]}
|
|
||||||
end;
|
|
||||||
common(_StateName, {call, From}, get_subscriptions, #{subscriptions := Subs}) ->
|
common(_StateName, {call, From}, get_subscriptions, #{subscriptions := Subs}) ->
|
||||||
{keep_state_and_data, [{reply, From, Subs}]};
|
{keep_state_and_data, [{reply, From, Subs}]};
|
||||||
|
common(_StateName, {call, From}, {ensure_present, What, Topic}, State) ->
|
||||||
|
{Result, NewState} = ensure_present(What, Topic, State),
|
||||||
|
{keep_state, NewState, [{reply, From, Result}]};
|
||||||
|
common(_StateName, {call, From}, {ensure_absent, What, Topic}, State) ->
|
||||||
|
{Result, NewState} = ensure_absent(What, Topic, State),
|
||||||
|
{keep_state, NewState, [{reply, From, Result}]};
|
||||||
common(_StateName, info, {dispatch, _, Msg},
|
common(_StateName, info, {dispatch, _, Msg},
|
||||||
#{replayq := Q} = State) ->
|
#{replayq := Q} = State) ->
|
||||||
NewQ = replayq:append(Q, collect([Msg])),
|
NewQ = replayq:append(Q, collect([Msg])),
|
||||||
|
@ -316,6 +319,53 @@ common(StateName, Type, Content, State) ->
|
||||||
[name(), Type, StateName, Content]),
|
[name(), Type, StateName, Content]),
|
||||||
{keep_state, State}.
|
{keep_state, State}.
|
||||||
|
|
||||||
|
ensure_present(Key, Topic, State) ->
|
||||||
|
Topics = maps:get(Key, State),
|
||||||
|
case is_topic_present(Topic, Topics) of
|
||||||
|
true ->
|
||||||
|
{ok, State};
|
||||||
|
false ->
|
||||||
|
R = do_ensure_present(Key, Topic, State),
|
||||||
|
{R, State#{Key := lists:usort([Topic | Topics])}}
|
||||||
|
end.
|
||||||
|
|
||||||
|
ensure_absent(Key, Topic, State) ->
|
||||||
|
Topics = maps:get(Key, State),
|
||||||
|
case is_topic_present(Topic, Topics) of
|
||||||
|
true ->
|
||||||
|
R = do_ensure_absent(Key, Topic, State),
|
||||||
|
{R, State#{Key := ensure_topic_absent(Topic, Topics)}};
|
||||||
|
false ->
|
||||||
|
{ok, State}
|
||||||
|
end.
|
||||||
|
|
||||||
|
ensure_topic_absent(_Topic, []) -> [];
|
||||||
|
ensure_topic_absent(Topic, [{_, _} | _] = L) -> lists:keydelete(Topic, 1, L);
|
||||||
|
ensure_topic_absent(Topic, L) -> lists:delete(Topic, L).
|
||||||
|
|
||||||
|
is_topic_present({Topic, _QoS}, Topics) ->
|
||||||
|
is_topic_present(Topic, Topics);
|
||||||
|
is_topic_present(Topic, Topics) ->
|
||||||
|
lists:member(Topic, Topics) orelse false =/= lists:keyfind(Topic, 1, Topics).
|
||||||
|
|
||||||
|
do_ensure_present(forwards, Topic, _) ->
|
||||||
|
ok = subscribe_local_topic(Topic);
|
||||||
|
do_ensure_present(subscriptions, {Topic, QoS},
|
||||||
|
#{connect_module := ConnectModule, connection := Conn}) ->
|
||||||
|
case erlang:function_exported(ConnectModule, ensure_subscribed, 3) of
|
||||||
|
true -> ConnectModule:ensure_subscribed(Conn, Topic, QoS);
|
||||||
|
false -> {error, no_remote_subscription_support}
|
||||||
|
end.
|
||||||
|
|
||||||
|
do_ensure_absent(forwards, Topic, _) ->
|
||||||
|
ok = emqx_broker:unsubscribe(Topic);
|
||||||
|
do_ensure_absent(subscriptions, Topic, #{connect_module := ConnectModule,
|
||||||
|
connection := Conn}) ->
|
||||||
|
case erlang:function_exported(ConnectModule, ensure_unsubscribed, 2) of
|
||||||
|
true -> ConnectModule:ensure_unsubscribed(Conn, Topic);
|
||||||
|
false -> {error, no_remote_subscription_support}
|
||||||
|
end.
|
||||||
|
|
||||||
collect(Acc) ->
|
collect(Acc) ->
|
||||||
receive
|
receive
|
||||||
{dispatch, _, Msg} ->
|
{dispatch, _, Msg} ->
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
stop/2
|
stop/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% optional behaviour callbacks
|
||||||
|
-export([ensure_subscribed/3,
|
||||||
|
ensure_unsubscribed/2
|
||||||
|
]).
|
||||||
|
|
||||||
-include("emqx_mqtt.hrl").
|
-include("emqx_mqtt.hrl").
|
||||||
|
|
||||||
-define(ACK_REF(ClientPid, PktId), {ClientPid, PktId}).
|
-define(ACK_REF(ClientPid, PktId), {ClientPid, PktId}).
|
||||||
|
@ -66,6 +71,18 @@ stop(Ref, #{ack_collector := AckCollector, client_pid := Pid}) ->
|
||||||
safe_stop(Pid, fun() -> emqx_client:stop(Pid) end, 1000),
|
safe_stop(Pid, fun() -> emqx_client:stop(Pid) end, 1000),
|
||||||
ok.
|
ok.
|
||||||
|
|
||||||
|
ensure_subscribed(#{client_pid := Pid}, Topic, QoS) when is_pid(Pid) ->
|
||||||
|
emqx_client:subscribe(Pid, Topic, QoS);
|
||||||
|
ensure_subscribed(_Conn, _Topic, _QoS) ->
|
||||||
|
%% return ok for now, next re-connect should should call start with new topic added to config
|
||||||
|
ok.
|
||||||
|
|
||||||
|
ensure_unsubscribed(#{client_pid := Pid}, Topic) when is_pid(Pid) ->
|
||||||
|
emqx_client:unsubscribe(Pid, Topic);
|
||||||
|
ensure_unsubscribed(_, _) ->
|
||||||
|
%% return ok for now, next re-connect should should call start with this topic deleted from config
|
||||||
|
ok.
|
||||||
|
|
||||||
safe_stop(Pid, StopF, Timeout) ->
|
safe_stop(Pid, StopF, Timeout) ->
|
||||||
MRef = monitor(process, Pid),
|
MRef = monitor(process, Pid),
|
||||||
unlink(Pid),
|
unlink(Pid),
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
-export([all/0, init_per_suite/1, end_per_suite/1]).
|
-export([all/0, init_per_suite/1, end_per_suite/1]).
|
||||||
-export([t_rpc/1,
|
-export([t_rpc/1,
|
||||||
t_mqtt/1,
|
t_mqtt/1,
|
||||||
t_forwards_mngr/1
|
t_mngr/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
all() -> [t_rpc,
|
all() -> [t_rpc,
|
||||||
t_mqtt,
|
t_mqtt,
|
||||||
t_forwards_mngr
|
t_mngr
|
||||||
].
|
].
|
||||||
|
|
||||||
init_per_suite(Config) ->
|
init_per_suite(Config) ->
|
||||||
|
@ -44,7 +44,7 @@ init_per_suite(Config) ->
|
||||||
end_per_suite(_Config) ->
|
end_per_suite(_Config) ->
|
||||||
emqx_ct_broker_helpers:run_teardown_steps().
|
emqx_ct_broker_helpers:run_teardown_steps().
|
||||||
|
|
||||||
t_forwards_mngr(Config) when is_list(Config) ->
|
t_mngr(Config) when is_list(Config) ->
|
||||||
Subs = [{<<"a">>, 1}, {<<"b">>, 2}],
|
Subs = [{<<"a">>, 1}, {<<"b">>, 2}],
|
||||||
Cfg = #{address => node(),
|
Cfg = #{address => node(),
|
||||||
forwards => [<<"mngr">>],
|
forwards => [<<"mngr">>],
|
||||||
|
@ -62,6 +62,10 @@ t_forwards_mngr(Config) when is_list(Config) ->
|
||||||
?assertEqual(ok, emqx_portal:ensure_forward_absent(Name, "mngr2")),
|
?assertEqual(ok, emqx_portal:ensure_forward_absent(Name, "mngr2")),
|
||||||
?assertEqual(ok, emqx_portal:ensure_forward_absent(Name, "mngr3")),
|
?assertEqual(ok, emqx_portal:ensure_forward_absent(Name, "mngr3")),
|
||||||
?assertEqual([<<"mngr">>], emqx_portal:get_forwards(Pid)),
|
?assertEqual([<<"mngr">>], emqx_portal:get_forwards(Pid)),
|
||||||
|
?assertEqual({error, no_remote_subscription_support},
|
||||||
|
emqx_portal:ensure_subscription_present(Pid, <<"t">>, 0)),
|
||||||
|
?assertEqual({error, no_remote_subscription_support},
|
||||||
|
emqx_portal:ensure_subscription_absent(Pid, <<"t">>)),
|
||||||
?assertEqual(Subs, emqx_portal:get_subscriptions(Pid))
|
?assertEqual(Subs, emqx_portal:get_subscriptions(Pid))
|
||||||
after
|
after
|
||||||
ok = emqx_portal:stop(Pid)
|
ok = emqx_portal:stop(Pid)
|
||||||
|
@ -93,10 +97,16 @@ t_rpc(Config) when is_list(Config) ->
|
||||||
ok = emqx_portal:stop(Pid)
|
ok = emqx_portal:stop(Pid)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% Full data loopback flow explained:
|
||||||
|
%% test-pid ---> mock-cleint ----> local-broker ---(local-subscription)--->
|
||||||
|
%% portal(export) --- (mqtt-connection)--> local-broker ---(remote-subscription) -->
|
||||||
|
%% portal(import) --(mecked message sending)--> test-pid
|
||||||
t_mqtt(Config) when is_list(Config) ->
|
t_mqtt(Config) when is_list(Config) ->
|
||||||
SendToTopic = <<"t_mqtt/one">>,
|
SendToTopic = <<"t_mqtt/one">>,
|
||||||
|
SendToTopic2 = <<"t_mqtt/two">>,
|
||||||
Mountpoint = <<"forwarded/${node}/">>,
|
Mountpoint = <<"forwarded/${node}/">>,
|
||||||
ForwardedTopic = emqx_topic:join(["forwarded", atom_to_list(node()), SendToTopic]),
|
ForwardedTopic = emqx_topic:join(["forwarded", atom_to_list(node()), SendToTopic]),
|
||||||
|
ForwardedTopic2 = emqx_topic:join(["forwarded", atom_to_list(node()), SendToTopic2]),
|
||||||
Cfg = #{address => "127.0.0.1:1883",
|
Cfg = #{address => "127.0.0.1:1883",
|
||||||
forwards => [SendToTopic],
|
forwards => [SendToTopic],
|
||||||
connect_module => emqx_portal_mqtt,
|
connect_module => emqx_portal_mqtt,
|
||||||
|
@ -118,7 +128,7 @@ t_mqtt(Config) when is_list(Config) ->
|
||||||
start_type => manual,
|
start_type => manual,
|
||||||
%% Consume back to forwarded message for verification
|
%% Consume back to forwarded message for verification
|
||||||
%% NOTE: this is a indefenite loopback without mocking emqx_portal:import_batch/2
|
%% NOTE: this is a indefenite loopback without mocking emqx_portal:import_batch/2
|
||||||
subscriptions => [{ForwardedTopic, 1}]
|
subscriptions => [{ForwardedTopic, _QoS = 1}]
|
||||||
},
|
},
|
||||||
Tester = self(),
|
Tester = self(),
|
||||||
Ref = make_ref(),
|
Ref = make_ref(),
|
||||||
|
@ -131,15 +141,27 @@ t_mqtt(Config) when is_list(Config) ->
|
||||||
{ok, Pid} = emqx_portal:start_link(?FUNCTION_NAME, Cfg),
|
{ok, Pid} = emqx_portal:start_link(?FUNCTION_NAME, Cfg),
|
||||||
ClientId = <<"client-1">>,
|
ClientId = <<"client-1">>,
|
||||||
try
|
try
|
||||||
|
?assertEqual([{ForwardedTopic, 1}], emqx_portal:get_subscriptions(Pid)),
|
||||||
|
emqx_portal:ensure_subscription_present(Pid, ForwardedTopic2, _QoS = 1),
|
||||||
|
?assertEqual([{ForwardedTopic, 1},
|
||||||
|
{ForwardedTopic2, 1}], emqx_portal:get_subscriptions(Pid)),
|
||||||
{ok, ConnPid} = emqx_mock_client:start_link(ClientId),
|
{ok, ConnPid} = emqx_mock_client:start_link(ClientId),
|
||||||
{ok, SPid} = emqx_mock_client:open_session(ConnPid, ClientId, internal),
|
{ok, SPid} = emqx_mock_client:open_session(ConnPid, ClientId, internal),
|
||||||
%% message from a different client, to avoid getting terminated by no-local
|
%% message from a different client, to avoid getting terminated by no-local
|
||||||
Msgs = lists:seq(1, 10),
|
Max = 100,
|
||||||
|
Msgs = lists:seq(1, Max),
|
||||||
lists:foreach(fun(I) ->
|
lists:foreach(fun(I) ->
|
||||||
Msg = emqx_message:make(<<"client-2">>, ?QOS_1, SendToTopic, integer_to_binary(I)),
|
Msg = emqx_message:make(<<"client-2">>, ?QOS_1, SendToTopic, integer_to_binary(I)),
|
||||||
emqx_session:publish(SPid, I, Msg)
|
emqx_session:publish(SPid, I, Msg)
|
||||||
end, Msgs),
|
end, Msgs),
|
||||||
ok = receive_and_match_messages(Ref, Msgs),
|
ok = receive_and_match_messages(Ref, Msgs),
|
||||||
|
ok = emqx_portal:ensure_forward_present(Pid, SendToTopic2),
|
||||||
|
Msgs2 = lists:seq(Max + 1, Max * 2),
|
||||||
|
lists:foreach(fun(I) ->
|
||||||
|
Msg = emqx_message:make(<<"client-2">>, ?QOS_1, SendToTopic2, integer_to_binary(I)),
|
||||||
|
emqx_session:publish(SPid, I, Msg)
|
||||||
|
end, Msgs2),
|
||||||
|
ok = receive_and_match_messages(Ref, Msgs2),
|
||||||
emqx_mock_client:close_session(ConnPid)
|
emqx_mock_client:close_session(ConnPid)
|
||||||
after
|
after
|
||||||
ok = emqx_portal:stop(Pid),
|
ok = emqx_portal:stop(Pid),
|
||||||
|
@ -147,7 +169,7 @@ t_mqtt(Config) when is_list(Config) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
receive_and_match_messages(Ref, Msgs) ->
|
receive_and_match_messages(Ref, Msgs) ->
|
||||||
TRef = erlang:send_after(timer:seconds(4), self(), {Ref, timeout}),
|
TRef = erlang:send_after(timer:seconds(5), self(), {Ref, timeout}),
|
||||||
try
|
try
|
||||||
do_receive_and_match_messages(Ref, Msgs)
|
do_receive_and_match_messages(Ref, Msgs)
|
||||||
after
|
after
|
||||||
|
|
Loading…
Reference in New Issue