Merge pull request #12095 from thalesmg/fix-rule-test-api-m-20231204
fix(rule_api): fix sql test when testing against events
This commit is contained in:
commit
b6e8f5a37c
|
@ -172,44 +172,58 @@ fields("node_metrics") ->
|
||||||
[{"node", sc(binary(), #{desc => ?DESC("node_node"), example => "emqx@127.0.0.1"})}] ++
|
[{"node", sc(binary(), #{desc => ?DESC("node_node"), example => "emqx@127.0.0.1"})}] ++
|
||||||
fields("metrics");
|
fields("metrics");
|
||||||
fields("ctx_pub") ->
|
fields("ctx_pub") ->
|
||||||
|
Event = 'message.publish',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(message_publish)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"id", sc(binary(), #{desc => ?DESC("event_id")})}
|
{"id", sc(binary(), #{desc => ?DESC("event_id")})}
|
||||||
| msg_event_common_fields()
|
| msg_event_common_fields()
|
||||||
];
|
];
|
||||||
fields("ctx_sub") ->
|
fields("ctx_sub") ->
|
||||||
|
Event = 'session.subscribed',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(session_subscribed)}
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)}
|
||||||
| msg_event_common_fields()
|
| msg_event_common_fields()
|
||||||
];
|
];
|
||||||
fields("ctx_unsub") ->
|
fields("ctx_unsub") ->
|
||||||
|
Event = 'session.unsubscribed',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(session_unsubscribed)}
|
{"event_type", event_type_sc(Event)},
|
||||||
| proplists:delete("event_type", fields("ctx_sub"))
|
{"event", event_sc(Event)}
|
||||||
|
| without(["event_type", "event_topic", "event"], fields("ctx_sub"))
|
||||||
];
|
];
|
||||||
fields("ctx_delivered") ->
|
fields("ctx_delivered") ->
|
||||||
|
Event = 'message.delivered',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(message_delivered)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
||||||
{"from_clientid", sc(binary(), #{desc => ?DESC("event_from_clientid")})},
|
{"from_clientid", sc(binary(), #{desc => ?DESC("event_from_clientid")})},
|
||||||
{"from_username", sc(binary(), #{desc => ?DESC("event_from_username")})}
|
{"from_username", sc(binary(), #{desc => ?DESC("event_from_username")})}
|
||||||
| msg_event_common_fields()
|
| msg_event_common_fields()
|
||||||
];
|
];
|
||||||
fields("ctx_acked") ->
|
fields("ctx_acked") ->
|
||||||
|
Event = 'message.acked',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(message_acked)}
|
{"event_type", event_type_sc(Event)},
|
||||||
| proplists:delete("event_type", fields("ctx_delivered"))
|
{"event", event_sc(Event)}
|
||||||
|
| without(["event_type", "event_topic", "event"], fields("ctx_delivered"))
|
||||||
];
|
];
|
||||||
fields("ctx_dropped") ->
|
fields("ctx_dropped") ->
|
||||||
|
Event = 'message.dropped',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(message_dropped)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
||||||
{"reason", sc(binary(), #{desc => ?DESC("event_ctx_dropped")})}
|
{"reason", sc(binary(), #{desc => ?DESC("event_ctx_dropped")})}
|
||||||
| msg_event_common_fields()
|
| msg_event_common_fields()
|
||||||
];
|
];
|
||||||
fields("ctx_connected") ->
|
fields("ctx_connected") ->
|
||||||
|
Event = 'client.connected',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(client_connected)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
||||||
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
||||||
{"mountpoint", sc(binary(), #{desc => ?DESC("event_mountpoint")})},
|
{"mountpoint", sc(binary(), #{desc => ?DESC("event_mountpoint")})},
|
||||||
|
@ -227,8 +241,10 @@ fields("ctx_connected") ->
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
fields("ctx_disconnected") ->
|
fields("ctx_disconnected") ->
|
||||||
|
Event = 'client.disconnected',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(client_disconnected)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
||||||
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
||||||
{"reason", sc(binary(), #{desc => ?DESC("event_ctx_disconnected_reason")})},
|
{"reason", sc(binary(), #{desc => ?DESC("event_ctx_disconnected_reason")})},
|
||||||
|
@ -240,8 +256,10 @@ fields("ctx_disconnected") ->
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
fields("ctx_connack") ->
|
fields("ctx_connack") ->
|
||||||
|
Event = 'client.connack',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(client_connack)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"reason_code", sc(binary(), #{desc => ?DESC("event_ctx_connack_reason_code")})},
|
{"reason_code", sc(binary(), #{desc => ?DESC("event_ctx_connack_reason_code")})},
|
||||||
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
||||||
{"clean_start", sc(boolean(), #{desc => ?DESC("event_clean_start"), default => true})},
|
{"clean_start", sc(boolean(), #{desc => ?DESC("event_clean_start"), default => true})},
|
||||||
|
@ -258,8 +276,10 @@ fields("ctx_connack") ->
|
||||||
})}
|
})}
|
||||||
];
|
];
|
||||||
fields("ctx_check_authz_complete") ->
|
fields("ctx_check_authz_complete") ->
|
||||||
|
Event = 'client.check_authz_complete',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(client_check_authz_complete)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
{"clientid", sc(binary(), #{desc => ?DESC("event_clientid")})},
|
||||||
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
{"username", sc(binary(), #{desc => ?DESC("event_username")})},
|
||||||
{"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})},
|
{"peerhost", sc(binary(), #{desc => ?DESC("event_peerhost")})},
|
||||||
|
@ -269,8 +289,11 @@ fields("ctx_check_authz_complete") ->
|
||||||
{"result", sc(binary(), #{desc => ?DESC("event_result")})}
|
{"result", sc(binary(), #{desc => ?DESC("event_result")})}
|
||||||
];
|
];
|
||||||
fields("ctx_bridge_mqtt") ->
|
fields("ctx_bridge_mqtt") ->
|
||||||
|
Event = '$bridges/mqtt:*',
|
||||||
|
EventBin = atom_to_binary(Event),
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc('$bridges/mqtt:*')},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(EventBin)},
|
||||||
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
||||||
{"payload", sc(binary(), #{desc => ?DESC("event_payload")})},
|
{"payload", sc(binary(), #{desc => ?DESC("event_payload")})},
|
||||||
{"topic", sc(binary(), #{desc => ?DESC("event_topic")})},
|
{"topic", sc(binary(), #{desc => ?DESC("event_topic")})},
|
||||||
|
@ -281,8 +304,10 @@ fields("ctx_bridge_mqtt") ->
|
||||||
qos()
|
qos()
|
||||||
];
|
];
|
||||||
fields("ctx_delivery_dropped") ->
|
fields("ctx_delivery_dropped") ->
|
||||||
|
Event = 'delivery.dropped',
|
||||||
[
|
[
|
||||||
{"event_type", event_type_sc(delivery_dropped)},
|
{"event_type", event_type_sc(Event)},
|
||||||
|
{"event", event_sc(Event)},
|
||||||
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
{"id", sc(binary(), #{desc => ?DESC("event_id")})},
|
||||||
{"reason", sc(binary(), #{desc => ?DESC("event_ctx_dropped")})},
|
{"reason", sc(binary(), #{desc => ?DESC("event_ctx_dropped")})},
|
||||||
{"from_clientid", sc(binary(), #{desc => ?DESC("event_from_clientid")})},
|
{"from_clientid", sc(binary(), #{desc => ?DESC("event_from_clientid")})},
|
||||||
|
@ -309,7 +334,21 @@ sc(Type, Meta) -> hoconsc:mk(Type, Meta).
|
||||||
ref(Field) -> hoconsc:ref(?MODULE, Field).
|
ref(Field) -> hoconsc:ref(?MODULE, Field).
|
||||||
|
|
||||||
event_type_sc(Event) ->
|
event_type_sc(Event) ->
|
||||||
sc(Event, #{desc => ?DESC("event_event_type"), required => true}).
|
EventType = event_to_event_type(Event),
|
||||||
|
sc(EventType, #{desc => ?DESC("event_event_type"), required => true}).
|
||||||
|
|
||||||
|
-spec event_to_event_type(atom()) -> atom().
|
||||||
|
event_to_event_type(Event) ->
|
||||||
|
binary_to_atom(binary:replace(atom_to_binary(Event), <<".">>, <<"_">>)).
|
||||||
|
|
||||||
|
event_sc(Event) when is_binary(Event) ->
|
||||||
|
%% only exception is `$bridges/...'.
|
||||||
|
sc(binary(), #{default => Event, importance => ?IMPORTANCE_HIDDEN});
|
||||||
|
event_sc(Event) ->
|
||||||
|
sc(Event, #{default => Event, importance => ?IMPORTANCE_HIDDEN}).
|
||||||
|
|
||||||
|
without(FieldNames, Fields) ->
|
||||||
|
lists:foldl(fun proplists:delete/2, Fields, FieldNames).
|
||||||
|
|
||||||
publish_received_at_sc() ->
|
publish_received_at_sc() ->
|
||||||
sc(integer(), #{desc => ?DESC("event_publish_received_at")}).
|
sc(integer(), #{desc => ?DESC("event_publish_received_at")}).
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
test(#{sql := Sql, context := Context}) ->
|
test(#{sql := Sql, context := Context}) ->
|
||||||
case emqx_rule_sqlparser:parse(Sql) of
|
case emqx_rule_sqlparser:parse(Sql) of
|
||||||
{ok, Select} ->
|
{ok, Select} ->
|
||||||
InTopic = maps:get(topic, Context, <<>>),
|
InTopic = get_in_topic(Context),
|
||||||
EventTopics = emqx_rule_sqlparser:select_from(Select),
|
EventTopics = emqx_rule_sqlparser:select_from(Select),
|
||||||
case lists:all(fun is_publish_topic/1, EventTopics) of
|
case lists:all(fun is_publish_topic/1, EventTopics) of
|
||||||
true ->
|
true ->
|
||||||
|
@ -37,8 +37,13 @@ test(#{sql := Sql, context := Context}) ->
|
||||||
false -> {error, nomatch}
|
false -> {error, nomatch}
|
||||||
end;
|
end;
|
||||||
false ->
|
false ->
|
||||||
%% the rule is for both publish and events, test it directly
|
case lists:member(InTopic, EventTopics) of
|
||||||
test_rule(Sql, Select, Context, EventTopics)
|
true ->
|
||||||
|
%% the rule is for both publish and events, test it directly
|
||||||
|
test_rule(Sql, Select, Context, EventTopics);
|
||||||
|
false ->
|
||||||
|
{error, nomatch}
|
||||||
|
end
|
||||||
end;
|
end;
|
||||||
{error, Reason} ->
|
{error, Reason} ->
|
||||||
?SLOG(debug, #{
|
?SLOG(debug, #{
|
||||||
|
@ -92,15 +97,12 @@ flatten([D | L]) when is_list(D) ->
|
||||||
[D0 || {ok, D0} <- D] ++ flatten(L).
|
[D0 || {ok, D0} <- D] ++ flatten(L).
|
||||||
|
|
||||||
fill_default_values(Event, Context) ->
|
fill_default_values(Event, Context) ->
|
||||||
maps:merge(envs_examp(Event), Context).
|
maps:merge(envs_examp(Event, Context), Context).
|
||||||
|
|
||||||
envs_examp(EventTopic) ->
|
envs_examp(EventTopic, Context) ->
|
||||||
EventName = emqx_rule_events:event_name(EventTopic),
|
EventName = maps:get(event, Context, emqx_rule_events:event_name(EventTopic)),
|
||||||
emqx_rule_maps:atom_key_map(
|
Env = maps:from_list(emqx_rule_events:columns_with_exam(EventName)),
|
||||||
maps:from_list(
|
emqx_rule_maps:atom_key_map(Env).
|
||||||
emqx_rule_events:columns_with_exam(EventName)
|
|
||||||
)
|
|
||||||
).
|
|
||||||
|
|
||||||
is_test_runtime_env_atom() ->
|
is_test_runtime_env_atom() ->
|
||||||
'emqx_rule_sqltester:is_test_runtime_env'.
|
'emqx_rule_sqltester:is_test_runtime_env'.
|
||||||
|
@ -118,3 +120,26 @@ is_test_runtime_env() ->
|
||||||
true -> true;
|
true -> true;
|
||||||
_ -> false
|
_ -> false
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% Most events have the original `topic' input, but their own topic (i.e.: `$events/...')
|
||||||
|
%% is different from `topic'.
|
||||||
|
get_in_topic(Context) ->
|
||||||
|
case maps:find(event_topic, Context) of
|
||||||
|
{ok, EventTopic} ->
|
||||||
|
EventTopic;
|
||||||
|
error ->
|
||||||
|
case maps:find(event, Context) of
|
||||||
|
{ok, Event} ->
|
||||||
|
maybe_infer_in_topic(Context, Event);
|
||||||
|
error ->
|
||||||
|
maps:get(topic, Context, <<>>)
|
||||||
|
end
|
||||||
|
end.
|
||||||
|
|
||||||
|
maybe_infer_in_topic(Context, 'message.publish') ->
|
||||||
|
%% This is special because the common use in the frontend is to select this event, but
|
||||||
|
%% test the input `topic' field against MQTT topic filters in the `FROM' clause rather
|
||||||
|
%% than the corresponding `$events/message_publish'.
|
||||||
|
maps:get(topic, Context, <<>>);
|
||||||
|
maybe_infer_in_topic(_Context, Event) ->
|
||||||
|
emqx_rule_events:event_topic(Event).
|
||||||
|
|
|
@ -1990,7 +1990,10 @@ t_sqlparse_event_1(_Config) ->
|
||||||
emqx_rule_sqltester:test(
|
emqx_rule_sqltester:test(
|
||||||
#{
|
#{
|
||||||
sql => Sql,
|
sql => Sql,
|
||||||
context => #{topic => <<"t/tt">>}
|
context => #{
|
||||||
|
topic => <<"t/tt">>,
|
||||||
|
event => 'session.subscribed'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
@ -2004,7 +2007,10 @@ t_sqlparse_event_2(_Config) ->
|
||||||
emqx_rule_sqltester:test(
|
emqx_rule_sqltester:test(
|
||||||
#{
|
#{
|
||||||
sql => Sql,
|
sql => Sql,
|
||||||
context => #{clientid => <<"abc">>}
|
context => #{
|
||||||
|
clientid => <<"abc">>,
|
||||||
|
event => 'client.connected'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
).
|
).
|
||||||
|
|
|
@ -0,0 +1,385 @@
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
%% Copyright (c) 2023 EMQ Technologies Co., Ltd. All Rights Reserved.
|
||||||
|
%%
|
||||||
|
%% Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
%% you may not use this file except in compliance with the License.
|
||||||
|
%% You may obtain a copy of the License at
|
||||||
|
%%
|
||||||
|
%% http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
%%
|
||||||
|
%% Unless required by applicable law or agreed to in writing, software
|
||||||
|
%% distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
%% See the License for the specific language governing permissions and
|
||||||
|
%% limitations under the License.
|
||||||
|
%%--------------------------------------------------------------------
|
||||||
|
-module(emqx_rule_engine_api_2_SUITE).
|
||||||
|
|
||||||
|
-compile(nowarn_export_all).
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-include_lib("common_test/include/ct.hrl").
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% CT boilerplate
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
all() ->
|
||||||
|
emqx_common_test_helpers:all(?MODULE).
|
||||||
|
|
||||||
|
init_per_suite(Config) ->
|
||||||
|
Apps = emqx_cth_suite:start(
|
||||||
|
app_specs(),
|
||||||
|
#{work_dir => emqx_cth_suite:work_dir(Config)}
|
||||||
|
),
|
||||||
|
emqx_common_test_http:create_default_app(),
|
||||||
|
[{apps, Apps} | Config].
|
||||||
|
|
||||||
|
end_per_suite(Config) ->
|
||||||
|
Apps = ?config(apps, Config),
|
||||||
|
ok = emqx_cth_suite:stop(Apps),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
app_specs() ->
|
||||||
|
[
|
||||||
|
emqx_conf,
|
||||||
|
emqx_rule_engine,
|
||||||
|
emqx_management,
|
||||||
|
{emqx_dashboard, "dashboard.listeners.http { enable = true, bind = 18083 }"}
|
||||||
|
].
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Helper fns
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
maybe_json_decode(X) ->
|
||||||
|
case emqx_utils_json:safe_decode(X, [return_maps]) of
|
||||||
|
{ok, Decoded} -> Decoded;
|
||||||
|
{error, _} -> X
|
||||||
|
end.
|
||||||
|
|
||||||
|
request(Method, Path, Params) ->
|
||||||
|
AuthHeader = emqx_mgmt_api_test_util:auth_header_(),
|
||||||
|
Opts = #{return_all => true},
|
||||||
|
case emqx_mgmt_api_test_util:request_api(Method, Path, "", AuthHeader, Params, Opts) of
|
||||||
|
{ok, {Status, Headers, Body0}} ->
|
||||||
|
Body = maybe_json_decode(Body0),
|
||||||
|
{ok, {Status, Headers, Body}};
|
||||||
|
{error, {Status, Headers, Body0}} ->
|
||||||
|
Body =
|
||||||
|
case emqx_utils_json:safe_decode(Body0, [return_maps]) of
|
||||||
|
{ok, Decoded0 = #{<<"message">> := Msg0}} ->
|
||||||
|
Msg = maybe_json_decode(Msg0),
|
||||||
|
Decoded0#{<<"message">> := Msg};
|
||||||
|
{ok, Decoded0} ->
|
||||||
|
Decoded0;
|
||||||
|
{error, _} ->
|
||||||
|
Body0
|
||||||
|
end,
|
||||||
|
{error, {Status, Headers, Body}};
|
||||||
|
Error ->
|
||||||
|
Error
|
||||||
|
end.
|
||||||
|
|
||||||
|
sql_test_api(Params) ->
|
||||||
|
Method = post,
|
||||||
|
Path = emqx_mgmt_api_test_util:api_path(["rule_test"]),
|
||||||
|
ct:pal("sql test (http):\n ~p", [Params]),
|
||||||
|
Res = request(Method, Path, Params),
|
||||||
|
ct:pal("sql test (http) result:\n ~p", [Res]),
|
||||||
|
Res.
|
||||||
|
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
%% Test cases
|
||||||
|
%%------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
t_rule_test_smoke(_Config) ->
|
||||||
|
%% Example inputs recorded from frontend on 2023-12-04
|
||||||
|
Publish = [
|
||||||
|
#{
|
||||||
|
expected => #{code => 200},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"message_publish">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
hint => <<"wrong topic">>,
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"message_publish">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
hint => <<
|
||||||
|
"Currently, the frontend doesn't try to match against "
|
||||||
|
"$events/message_published, but it may start sending "
|
||||||
|
"the event topic in the future."
|
||||||
|
>>,
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"message_publish">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"$events/message_published\"">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
%% Default input SQL doesn't match any event topic
|
||||||
|
DefaultNoMatch = [
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx_2">>,
|
||||||
|
<<"event_type">> => <<"message_delivered">>,
|
||||||
|
<<"from_clientid">> => <<"c_emqx_1">>,
|
||||||
|
<<"from_username">> => <<"u_emqx_1">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx_2">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx_2">>,
|
||||||
|
<<"event_type">> => <<"message_acked">>,
|
||||||
|
<<"from_clientid">> => <<"c_emqx_1">>,
|
||||||
|
<<"from_username">> => <<"u_emqx_1">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx_2">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"message_dropped">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"reason">> => <<"no_subscribers">>,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"client_connected">>,
|
||||||
|
<<"peername">> => <<"127.0.0.1:52918">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"client_disconnected">>,
|
||||||
|
<<"reason">> => <<"normal">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"client_connack">>,
|
||||||
|
<<"reason_code">> => <<"sucess">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"action">> => <<"publish">>,
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"client_check_authz_complete">>,
|
||||||
|
<<"result">> => <<"allow">>,
|
||||||
|
<<"topic">> => <<"t/1">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"session_subscribed">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"session_unsubscribed">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx_2">>,
|
||||||
|
<<"event_type">> => <<"delivery_dropped">>,
|
||||||
|
<<"from_clientid">> => <<"c_emqx_1">>,
|
||||||
|
<<"from_username">> => <<"u_emqx_1">>,
|
||||||
|
<<"payload">> => <<"{\"msg\": \"hello\"}">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"reason">> => <<"queue_full">>,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx_2">>
|
||||||
|
},
|
||||||
|
<<"sql">> => <<"SELECT\n *\nFROM\n \"t/#\"">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
MultipleFrom = [
|
||||||
|
#{
|
||||||
|
expected => #{code => 200},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"session_unsubscribed">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> =>
|
||||||
|
<<"SELECT\n *\nFROM\n \"t/#\", \"$events/session_unsubscribed\" ">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 200},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"session_unsubscribed">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> =>
|
||||||
|
<<"SELECT\n *\nFROM\n \"$events/message_dropped\", \"$events/session_unsubscribed\" ">>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
#{
|
||||||
|
expected => #{code => 412},
|
||||||
|
input =>
|
||||||
|
#{
|
||||||
|
<<"context">> =>
|
||||||
|
#{
|
||||||
|
<<"clientid">> => <<"c_emqx">>,
|
||||||
|
<<"event_type">> => <<"session_unsubscribed">>,
|
||||||
|
<<"qos">> => 1,
|
||||||
|
<<"topic">> => <<"t/a">>,
|
||||||
|
<<"username">> => <<"u_emqx">>
|
||||||
|
},
|
||||||
|
<<"sql">> =>
|
||||||
|
<<"SELECT\n *\nFROM\n \"$events/message_dropped\", \"$events/client_connected\" ">>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Cases = Publish ++ DefaultNoMatch ++ MultipleFrom,
|
||||||
|
FailedCases = lists:filtermap(fun do_t_rule_test_smoke/1, Cases),
|
||||||
|
?assertEqual([], FailedCases),
|
||||||
|
ok.
|
||||||
|
|
||||||
|
do_t_rule_test_smoke(#{input := Input, expected := #{code := ExpectedCode}} = Case) ->
|
||||||
|
{_ErrOrOk, {{_, Code, _}, _, Body}} = sql_test_api(Input),
|
||||||
|
case Code =:= ExpectedCode of
|
||||||
|
true ->
|
||||||
|
false;
|
||||||
|
false ->
|
||||||
|
{true, #{
|
||||||
|
expected => ExpectedCode,
|
||||||
|
hint => maps:get(hint, Case, <<>>),
|
||||||
|
got => Code,
|
||||||
|
resp_body => Body
|
||||||
|
}}
|
||||||
|
end.
|
|
@ -216,7 +216,7 @@ t_ctx_delivery_dropped(_) ->
|
||||||
|
|
||||||
t_mongo_date_function_should_return_string_in_test_env(_) ->
|
t_mongo_date_function_should_return_string_in_test_env(_) ->
|
||||||
SQL =
|
SQL =
|
||||||
<<"SELECT mongo_date() as mongo_date FROM \"t/1\"">>,
|
<<"SELECT mongo_date() as mongo_date FROM \"$events/client_check_authz_complete\"">>,
|
||||||
Context =
|
Context =
|
||||||
#{
|
#{
|
||||||
action => <<"publish">>,
|
action => <<"publish">>,
|
||||||
|
|
Loading…
Reference in New Issue