From bdea05f022f76b4c2f4699860b43bcf323410822 Mon Sep 17 00:00:00 2001 From: JimMoen Date: Wed, 6 Apr 2022 10:35:54 +0800 Subject: [PATCH] test(exhook): other cluster_name only handle 'cilent.*' hooks --- apps/emqx_exhook/test/emqx_exhook_SUITE.erl | 65 ++++++-- .../emqx_exhook/test/emqx_exhook_demo_svr.erl | 53 +++--- .../test/props/prop_exhook_hooks.erl | 155 +++++++++++------- 3 files changed, 179 insertions(+), 94 deletions(-) diff --git a/apps/emqx_exhook/test/emqx_exhook_SUITE.erl b/apps/emqx_exhook/test/emqx_exhook_SUITE.erl index 5aed2f2b8..101c55749 100644 --- a/apps/emqx_exhook/test/emqx_exhook_SUITE.erl +++ b/apps/emqx_exhook/test/emqx_exhook_SUITE.erl @@ -19,9 +19,14 @@ -compile(export_all). -compile(nowarn_export_all). +-include("emqx_exhook.hrl"). + -include_lib("eunit/include/eunit.hrl"). -include_lib("common_test/include/ct.hrl"). +-define(OTHER_CLUSTER_NAME_ATOM, test_emqx_cluster). +-define(OTHER_CLUSTER_NAME_STRING, "test_emqx_cluster"). + %%-------------------------------------------------------------------- %% Setups %%-------------------------------------------------------------------- @@ -99,29 +104,63 @@ t_cli_stats(_) -> unmeck_print(). t_priority(_) -> - restart_exhook_with_envs([{emqx_exhook, hook_priority, 1}]), + restart_apps_with_envs([ {emqx, fun set_special_cfgs/1} + , {emqx_exhook, [{hook_priority, 1}]}]), emqx_exhook:disable(default), ok = emqx_exhook:enable(default), [Callback | _] = emqx_hooks:lookup('client.connected'), - 1 = emqx_hooks:callback_priority(Callback). + ?assertEqual(1, emqx_hooks:callback_priority(Callback)). + +t_cluster_name(_) -> + SetEnvFun = + fun(emqx) -> + set_special_cfgs(emqx), + application:set_env(ekka, cluster_name, ?OTHER_CLUSTER_NAME_ATOM); + (emqx_exhook) -> + application:set_env(emqx_exhook, hook_priority, 1) + end, + + emqx_ct_helpers:stop_apps([emqx, emqx_exhook]), + emqx_ct_helpers:start_apps([emqx, emqx_exhook], SetEnvFun), + + ?assertEqual(?OTHER_CLUSTER_NAME_STRING, emqx_sys:cluster_name()), + + emqx_exhook:disable(default), + ok = emqx_exhook:enable(default), + %% See emqx_exhook_demo_svr:on_provider_loaded/2 + ?assertEqual([], emqx_hooks:lookup('session.created')), + ?assertEqual([], emqx_hooks:lookup('message_publish')), + + [Callback | _] = emqx_hooks:lookup('client.connected'), + ?assertEqual(1, emqx_hooks:callback_priority(Callback)). %%-------------------------------------------------------------------- %% Utils %%-------------------------------------------------------------------- %% TODO: make it more general and move to `emqx_ct_helpers` -restart_exhook_with_envs(Envs) -> - emqx_ct_helpers:stop_apps([emqx_exhook]), - SetPriorityFun - = fun(emqx) -> - set_special_cfgs(emqx); - (emqx_exhook) -> - lists:foreach(fun({App, Key, Val}) -> - application:set_env(App, Key, Val) - end, Envs) - end, - emqx_ct_helpers:start_apps([emqx_exhook], SetPriorityFun). +restart_app_with_envs(App, Fun) + when is_function(Fun) -> + emqx_ct_helpers:stop_apps([App]), + emqx_ct_helpers:start_apps([App], Fun); + +restart_app_with_envs(App, Envs) + when is_list(Envs) -> + emqx_ct_helpers:stop_apps([App]), + HandlerFun = + fun(App) -> + lists:foreach(fun({Key, Val}) -> + application:set_env(App, Key, Val) + end, Envs) + end, + emqx_ct_helpers:start_apps([App], HandlerFun). + +restart_apps_with_envs([]) -> + ok; +restart_apps_with_envs([{App, Envs} | Rest]) -> + restart_app_with_envs(App, Envs), + restart_apps_with_envs(Rest). meck_print() -> meck:new(emqx_ctl, [passthrough, no_history, no_link]), diff --git a/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl b/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl index c2db04dd4..0e041d689 100644 --- a/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl +++ b/apps/emqx_exhook/test/emqx_exhook_demo_svr.erl @@ -51,6 +51,8 @@ -define(PORT, 9000). -define(NAME, ?MODULE). +-define(DEFAULT_CLUSTER_NAME, <<"emqxcl">>). +-define(OTHER_CLUSTER_NAME_BIN, <<"test_emqx_cluster">>). %%-------------------------------------------------------------------- %% Server APIs @@ -112,30 +114,37 @@ reply(Q1, Q2) -> -spec on_provider_loaded(emqx_exhook_pb:provider_loaded_request(), grpc:metadata()) -> {ok, emqx_exhook_pb:loaded_response(), grpc:metadata()} | {error, grpc_cowboy_h:error_response()}. - -on_provider_loaded(Req, Md) -> +on_provider_loaded(#{meta := #{cluster_name := Name}} = Req, Md) -> ?MODULE:in({?FUNCTION_NAME, Req}), %io:format("fun: ~p, req: ~0p~n", [?FUNCTION_NAME, Req]), - {ok, #{hooks => [ - #{name => <<"client.connect">>}, - #{name => <<"client.connack">>}, - #{name => <<"client.connected">>}, - #{name => <<"client.disconnected">>}, - #{name => <<"client.authenticate">>}, - #{name => <<"client.check_acl">>}, - #{name => <<"client.subscribe">>}, - #{name => <<"client.unsubscribe">>}, - #{name => <<"session.created">>}, - #{name => <<"session.subscribed">>}, - #{name => <<"session.unsubscribed">>}, - #{name => <<"session.resumed">>}, - #{name => <<"session.discarded">>}, - #{name => <<"session.takeovered">>}, - #{name => <<"session.terminated">>}, - #{name => <<"message.publish">>}, - #{name => <<"message.delivered">>}, - #{name => <<"message.acked">>}, - #{name => <<"message.dropped">>}]}, Md}. + HooksClient = + [#{name => <<"client.connect">>}, + #{name => <<"client.connack">>}, + #{name => <<"client.connected">>}, + #{name => <<"client.disconnected">>}, + #{name => <<"client.authenticate">>}, + #{name => <<"client.check_acl">>}, + #{name => <<"client.subscribe">>}, + #{name => <<"client.unsubscribe">>}], + HooksSession = + [#{name => <<"session.created">>}, + #{name => <<"session.subscribed">>}, + #{name => <<"session.unsubscribed">>}, + #{name => <<"session.resumed">>}, + #{name => <<"session.discarded">>}, + #{name => <<"session.takeovered">>}, + #{name => <<"session.terminated">>}], + HooksMessage = + [#{name => <<"message.publish">>}, + #{name => <<"message.delivered">>}, + #{name => <<"message.acked">>}, + #{name => <<"message.dropped">>}], + case Name of + ?DEFAULT_CLUSTER_NAME -> + {ok, #{hooks => HooksClient ++ HooksSession ++ HooksMessage}, Md}; + ?OTHER_CLUSTER_NAME_BIN -> + {ok, #{hooks => HooksClient}, Md} + end. -spec on_provider_unloaded(emqx_exhook_pb:provider_unloaded_request(), grpc:metadata()) -> {ok, emqx_exhook_pb:empty_success(), grpc:metadata()} | {error, grpc_cowboy_h:error_response()}. diff --git a/apps/emqx_exhook/test/props/prop_exhook_hooks.erl b/apps/emqx_exhook/test/props/prop_exhook_hooks.erl index 24f45c8b0..2aec580ff 100644 --- a/apps/emqx_exhook/test/props/prop_exhook_hooks.erl +++ b/apps/emqx_exhook/test/props/prop_exhook_hooks.erl @@ -36,42 +36,46 @@ fun() -> do_teardown(State) end end, ?FORALL(Vars, Types, Exprs))). +-define(DEFAULT_CLUSTER_NAME, <<"emqxcl">>). + %%-------------------------------------------------------------------- %% Properties %%-------------------------------------------------------------------- prop_client_connect() -> - ?ALL({ConnInfo, ConnProps}, - {conninfo(), conn_properties()}, - begin - ok = emqx_hooks:run('client.connect', [ConnInfo, ConnProps]), - {'on_client_connect', Resp} = emqx_exhook_demo_svr:take(), - Expected = - #{props => properties(ConnProps), - conninfo => from_conninfo(ConnInfo) - }, - ?assertEqual(Expected, Resp), - true - end). + ?ALL({ConnInfo, ConnProps, Meta}, + {conninfo(), conn_properties(), request_meta()}, + begin + ok = emqx_hooks:run('client.connect', [ConnInfo, ConnProps]), + {'on_client_connect', Resp} = emqx_exhook_demo_svr:take(), + Expected = + #{props => properties(ConnProps), + conninfo => from_conninfo(ConnInfo), + meta => Meta + }, + ?assertEqual(Expected, Resp), + true + end). prop_client_connack() -> - ?ALL({ConnInfo, Rc, AckProps}, - {conninfo(), connack_return_code(), ack_properties()}, + ?ALL({ConnInfo, Rc, AckProps, Meta}, + {conninfo(), connack_return_code(), ack_properties(), request_meta()}, begin ok = emqx_hooks:run('client.connack', [ConnInfo, Rc, AckProps]), {'on_client_connack', Resp} = emqx_exhook_demo_svr:take(), Expected = #{props => properties(AckProps), result_code => atom_to_binary(Rc, utf8), - conninfo => from_conninfo(ConnInfo) + conninfo => from_conninfo(ConnInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_client_authenticate() -> - ?ALL({ClientInfo0, AuthResult}, - {clientinfo(), authresult()}, + ?ALL({ClientInfo0, AuthResult, Meta}, + {clientinfo(), authresult(), request_meta()}, begin ClientInfo = inject_magic_into(username, ClientInfo0), OutAuthResult = emqx_hooks:run_fold('client.authenticate', [ClientInfo], AuthResult), @@ -103,16 +107,16 @@ prop_client_authenticate() -> {'on_client_authenticate', Resp} = emqx_exhook_demo_svr:take(), Expected = #{result => authresult_to_bool(AuthResult), - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_client_check_acl() -> - ?ALL({ClientInfo0, PubSub, Topic, Result}, - {clientinfo(), oneof([publish, subscribe]), - topic(), oneof([allow, deny])}, + ?ALL({ClientInfo0, PubSub, Topic, Result, Meta}, + {clientinfo(), oneof([publish, subscribe]), topic(), oneof([allow, deny]), request_meta()}, begin ClientInfo = inject_magic_into(username, ClientInfo0), OutResult = emqx_hooks:run_fold( @@ -132,162 +136,179 @@ prop_client_check_acl() -> #{result => aclresult_to_bool(Result), type => pubsub_to_enum(PubSub), topic => Topic, - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_client_connected() -> - ?ALL({ClientInfo, ConnInfo}, - {clientinfo(), conninfo()}, + ?ALL({ClientInfo, ConnInfo, Meta}, + {clientinfo(), conninfo(), request_meta()}, begin ok = emqx_hooks:run('client.connected', [ClientInfo, ConnInfo]), {'on_client_connected', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) + #{clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_client_disconnected() -> - ?ALL({ClientInfo, Reason, ConnInfo}, - {clientinfo(), shutdown_reason(), conninfo()}, + ?ALL({ClientInfo, Reason, ConnInfo, Meta}, + {clientinfo(), shutdown_reason(), conninfo(), request_meta()}, begin ok = emqx_hooks:run('client.disconnected', [ClientInfo, Reason, ConnInfo]), {'on_client_disconnected', Resp} = emqx_exhook_demo_svr:take(), Expected = #{reason => stringfy(Reason), - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_client_subscribe() -> - ?ALL({ClientInfo, SubProps, TopicTab}, - {clientinfo(), sub_properties(), topictab()}, + ?ALL({ClientInfo, SubProps, TopicTab, Meta}, + {clientinfo(), sub_properties(), topictab(), request_meta()}, begin ok = emqx_hooks:run('client.subscribe', [ClientInfo, SubProps, TopicTab]), {'on_client_subscribe', Resp} = emqx_exhook_demo_svr:take(), Expected = #{props => properties(SubProps), topic_filters => topicfilters(TopicTab), - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_client_unsubscribe() -> - ?ALL({ClientInfo, UnSubProps, TopicTab}, - {clientinfo(), unsub_properties(), topictab()}, + ?ALL({ClientInfo, UnSubProps, TopicTab, Meta}, + {clientinfo(), unsub_properties(), topictab(), request_meta()}, begin ok = emqx_hooks:run('client.unsubscribe', [ClientInfo, UnSubProps, TopicTab]), {'on_client_unsubscribe', Resp} = emqx_exhook_demo_svr:take(), Expected = #{props => properties(UnSubProps), topic_filters => topicfilters(TopicTab), - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_created() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL({ClientInfo, SessInfo, Meta}, + {clientinfo(), sessioninfo(), request_meta()}, begin ok = emqx_hooks:run('session.created', [ClientInfo, SessInfo]), {'on_session_created', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) + #{clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_subscribed() -> - ?ALL({ClientInfo, Topic, SubOpts}, - {clientinfo(), topic(), subopts()}, + ?ALL({ClientInfo, Topic, SubOpts, Meta}, + {clientinfo(), topic(), subopts(), request_meta()}, begin ok = emqx_hooks:run('session.subscribed', [ClientInfo, Topic, SubOpts]), {'on_session_subscribed', Resp} = emqx_exhook_demo_svr:take(), Expected = #{topic => Topic, subopts => subopts(SubOpts), - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_unsubscribed() -> - ?ALL({ClientInfo, Topic, SubOpts}, - {clientinfo(), topic(), subopts()}, + ?ALL({ClientInfo, Topic, SubOpts, Meta}, + {clientinfo(), topic(), subopts(), request_meta()}, begin ok = emqx_hooks:run('session.unsubscribed', [ClientInfo, Topic, SubOpts]), {'on_session_unsubscribed', Resp} = emqx_exhook_demo_svr:take(), Expected = #{topic => Topic, - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_resumed() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL({ClientInfo, SessInfo, Meta}, + {clientinfo(), sessioninfo(), request_meta()}, begin ok = emqx_hooks:run('session.resumed', [ClientInfo, SessInfo]), {'on_session_resumed', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) + #{clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_discared() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL({ClientInfo, SessInfo, Meta}, + {clientinfo(), sessioninfo(), request_meta()}, begin ok = emqx_hooks:run('session.discarded', [ClientInfo, SessInfo]), {'on_session_discarded', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) + #{clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_takeovered() -> - ?ALL({ClientInfo, SessInfo}, {clientinfo(), sessioninfo()}, + ?ALL({ClientInfo, SessInfo, Meta}, + {clientinfo(), sessioninfo(), request_meta()}, begin ok = emqx_hooks:run('session.takeovered', [ClientInfo, SessInfo]), {'on_session_takeovered', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{clientinfo => from_clientinfo(ClientInfo) + #{clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_session_terminated() -> - ?ALL({ClientInfo, Reason, SessInfo}, - {clientinfo(), shutdown_reason(), sessioninfo()}, + ?ALL({ClientInfo, Reason, SessInfo, Meta}, + {clientinfo(), shutdown_reason(), sessioninfo(), request_meta()}, begin ok = emqx_hooks:run('session.terminated', [ClientInfo, Reason, SessInfo]), {'on_session_terminated', Resp} = emqx_exhook_demo_svr:take(), Expected = #{reason => stringfy(Reason), - clientinfo => from_clientinfo(ClientInfo) + clientinfo => from_clientinfo(ClientInfo), + meta => Meta }, ?assertEqual(Expected, Resp), true end). prop_message_publish() -> - ?ALL(Msg0, message(), + ?ALL({Msg0, Meta}, + {message(), request_meta()}, begin Msg = emqx_message:from_map( inject_magic_into(from, emqx_message:to_map(Msg0))), @@ -317,7 +338,8 @@ prop_message_publish() -> {'on_message_publish', Resp} = emqx_exhook_demo_svr:take(), Expected = - #{message => from_message(Msg) + #{message => from_message(Msg), + meta => Meta }, ?assertEqual(Expected, Resp) end, @@ -325,7 +347,8 @@ prop_message_publish() -> end). prop_message_dropped() -> - ?ALL({Msg, By, Reason}, {message(), hardcoded, shutdown_reason()}, + ?ALL({Msg, By, Reason, Meta}, + {message(), hardcoded, shutdown_reason(), request_meta()}, begin ok = emqx_hooks:run('message.dropped', [Msg, By, Reason]), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of @@ -334,7 +357,8 @@ prop_message_dropped() -> {'on_message_dropped', Resp} = emqx_exhook_demo_svr:take(), Expected = #{reason => stringfy(Reason), - message => from_message(Msg) + message => from_message(Msg), + meta => Meta }, ?assertEqual(Expected, Resp) end, @@ -342,7 +366,8 @@ prop_message_dropped() -> end). prop_message_delivered() -> - ?ALL({ClientInfo, Msg}, {clientinfo(), message()}, + ?ALL({ClientInfo, Msg, Meta}, + {clientinfo(), message(), request_meta()}, begin ok = emqx_hooks:run('message.delivered', [ClientInfo, Msg]), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of @@ -351,7 +376,8 @@ prop_message_delivered() -> {'on_message_delivered', Resp} = emqx_exhook_demo_svr:take(), Expected = #{clientinfo => from_clientinfo(ClientInfo), - message => from_message(Msg) + message => from_message(Msg), + meta => Meta }, ?assertEqual(Expected, Resp) end, @@ -359,7 +385,8 @@ prop_message_delivered() -> end). prop_message_acked() -> - ?ALL({ClientInfo, Msg}, {clientinfo(), message()}, + ?ALL({ClientInfo, Msg, Meta}, + {clientinfo(), message(), request_meta()}, begin ok = emqx_hooks:run('message.acked', [ClientInfo, Msg]), case emqx_topic:match(emqx_message:topic(Msg), <<"$SYS/#">>) of @@ -368,7 +395,8 @@ prop_message_acked() -> {'on_message_acked', Resp} = emqx_exhook_demo_svr:take(), Expected = #{clientinfo => from_clientinfo(ClientInfo), - message => from_message(Msg) + message => from_message(Msg), + meta => Meta }, ?assertEqual(Expected, Resp) end, @@ -411,6 +439,8 @@ stringfy(Term) when is_integer(Term) -> integer_to_binary(Term); stringfy(Term) when is_atom(Term) -> atom_to_binary(Term, utf8); +stringfy(Term) when is_list(Term) -> + list_to_binary(Term); stringfy(Term) -> unicode:characters_to_binary((io_lib:format("~0p", [Term]))). @@ -513,6 +543,13 @@ sub_properties() -> unsub_properties() -> #{}. +request_meta() -> + #{ node => nodestr() + , version => stringfy(emqx_sys:version()) + , sysdescr => stringfy(emqx_sys:sysdescr()) + , cluster_name => ?DEFAULT_CLUSTER_NAME + }. + shutdown_reason() -> oneof([utf8(), {shutdown, emqx_ct_proper_types:limited_atom()}]).