| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- %%--------------------------------------------------------------------
- %% Copyright (c) 2024 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_variform_tests).
- -compile(export_all).
- -compile(nowarn_export_all).
- -include_lib("eunit/include/eunit.hrl").
- -define(SYNTAX_ERROR, {error, "syntax error before:" ++ _}).
- redner_test_() ->
- [
- {"direct var reference", fun() -> ?assertEqual({ok, <<"1">>}, render("a", #{a => 1})) end},
- {"concat strings", fun() ->
- ?assertEqual({ok, <<"a,b">>}, render("concat(['a',',','b'])", #{}))
- end},
- {"concat empty string", fun() ->
- ?assertEqual({ok, <<"">>}, render("concat([''])", #{}))
- end},
- {"tokens 1st", fun() ->
- ?assertEqual({ok, <<"a">>}, render("nth(1,tokens(var, ','))", #{var => <<"a,b">>}))
- end},
- {"unknown var return error", fun() ->
- ?assertMatch({error, #{reason := var_unbound}}, render("var", #{}))
- end},
- {"out of range nth index", fun() ->
- ?assertEqual({ok, <<>>}, render("nth(2, tokens(var, ','))", #{var => <<"a">>}))
- end},
- {"string for nth index", fun() ->
- ?assertEqual({ok, <<"a">>}, render("nth('1', tokens(var, ','))", #{var => <<"a">>}))
- end},
- {"not a index number for nth", fun() ->
- ?assertMatch(
- {error, #{reason := invalid_argument, func := nth, index := <<"notnum">>}},
- render("nth('notnum', tokens(var, ','))", #{var => <<"a">>})
- )
- end},
- {"substr", fun() ->
- ?assertMatch(
- {ok, <<"b">>},
- render("substr(var,1)", #{var => <<"ab">>})
- )
- end},
- {"result in integer", fun() ->
- ?assertMatch(
- {ok, <<"2">>},
- render("strlen(var)", #{var => <<"ab">>})
- )
- end},
- {"result in float", fun() ->
- ?assertMatch(
- {ok, <<"2.2">>},
- render("var", #{var => 2.2})
- )
- end},
- {"concat a number", fun() ->
- ?assertMatch(
- {ok, <<"2.2">>},
- render("concat(strlen(var),'.2')", #{var => <<"xy">>})
- )
- end},
- {"var is an array", fun() ->
- ?assertMatch(
- {ok, <<"y">>},
- render("nth(2,var)", #{var => [<<"x">>, <<"y">>]})
- )
- end}
- ].
- unknown_func_test_() ->
- [
- {"unknown function", fun() ->
- ?assertMatch(
- {error, #{reason := unknown_variform_function}},
- render("nonexistingatom__(a)", #{})
- )
- end},
- {"unknown module", fun() ->
- ?assertMatch(
- {error, #{reason := unknown_variform_module}},
- render("nonexistingatom__.nonexistingatom__(a)", #{})
- )
- end},
- {"unknown function in a known module", fun() ->
- ?assertMatch(
- {error, #{reason := unknown_variform_function}},
- render("emqx_variform_bif.nonexistingatom__(a)", #{})
- )
- end},
- {"invalid func reference", fun() ->
- ?assertMatch(
- {error, #{reason := invalid_function_reference, function := "a.b.c"}},
- render("a.b.c(var)", #{})
- )
- end}
- ].
- concat(L) -> iolist_to_binary(L).
- inject_allowed_module_test() ->
- try
- emqx_variform:inject_allowed_module(?MODULE),
- ?assertEqual({ok, <<"ab">>}, render(atom_to_list(?MODULE) ++ ".concat(['a','b'])", #{})),
- ?assertMatch(
- {error, #{
- reason := unknown_variform_function,
- module := ?MODULE,
- function := concat,
- arity := 2
- }},
- render(atom_to_list(?MODULE) ++ ".concat('a','b')", #{})
- ),
- ?assertMatch(
- {error, #{reason := unallowed_veriform_module, module := emqx}},
- render("emqx.concat('a','b')", #{})
- )
- after
- emqx_variform:erase_allowed_module(?MODULE)
- end.
- coalesce_test_() ->
- [
- {"first", fun() ->
- ?assertEqual({ok, <<"a">>}, render("coalesce(['a','b'])", #{}))
- end},
- {"second", fun() ->
- ?assertEqual({ok, <<"b">>}, render("coalesce(['', 'b'])", #{}))
- end},
- {"first var", fun() ->
- ?assertEqual({ok, <<"a">>}, render("coalesce([a,b])", #{a => <<"a">>, b => <<"b">>}))
- end},
- {"second var", fun() ->
- ?assertEqual({ok, <<"b">>}, render("coalesce([a,b])", #{b => <<"b">>}))
- end},
- {"empty", fun() -> ?assertEqual({ok, <<>>}, render("coalesce([a,b])", #{})) end},
- {"arg from other func", fun() ->
- ?assertEqual({ok, <<"b">>}, render("coalesce(tokens(a,','))", #{a => <<",,b,c">>}))
- end},
- {"var unbound", fun() -> ?assertEqual({ok, <<>>}, render("coalesce(a)", #{})) end},
- {"var unbound in call", fun() ->
- ?assertEqual({ok, <<>>}, render("coalesce(concat(a))", #{}))
- end},
- {"var unbound in calls", fun() ->
- ?assertEqual({ok, <<"c">>}, render("coalesce([any_to_str(a),any_to_str(b),'c'])", #{}))
- end},
- {"badarg", fun() ->
- ?assertMatch(
- {error, #{reason := coalesce_badarg}}, render("coalesce(a,b)", #{a => 1, b => 2})
- )
- end},
- {"badarg from return", fun() ->
- ?assertMatch(
- {error, #{reason := coalesce_badarg}}, render("coalesce(any_to_str(a))", #{a => 1})
- )
- end}
- ].
- syntax_error_test_() ->
- [
- {"empty expression", fun() -> ?assertMatch(?SYNTAX_ERROR, render("", #{})) end},
- {"const string single quote", fun() -> ?assertMatch(?SYNTAX_ERROR, render("'a'", #{})) end},
- {"const string double quote", fun() ->
- ?assertMatch(?SYNTAX_ERROR, render(<<"\"a\"">>, #{}))
- end},
- {"no arity", fun() -> ?assertMatch(?SYNTAX_ERROR, render("concat()", #{})) end}
- ].
- render(Expression, Bindings) ->
- emqx_variform:render(Expression, Bindings).
- hash_pick_test() ->
- lists:foreach(
- fun(_) ->
- {ok, Res} = render("nth(hash_to_range(rand_str(10),1,5),[1,2,3,4,5])", #{}),
- ?assert(Res >= <<"1">> andalso Res =< <<"5">>)
- end,
- lists:seq(1, 100)
- ).
- map_to_range_pick_test() ->
- lists:foreach(
- fun(_) ->
- {ok, Res} = render("nth(map_to_range(rand_str(10),1,5),[1,2,3,4,5])", #{}),
- ?assert(Res >= <<"1">> andalso Res =< <<"5">>)
- end,
- lists:seq(1, 100)
- ).
- -define(ASSERT_BADARG(FUNC, ARGS),
- ?_assertEqual(
- {error, #{reason => badarg, function => FUNC}},
- render(atom_to_list(FUNC) ++ ARGS, #{})
- )
- ).
- to_range_badarg_test_() ->
- [
- ?ASSERT_BADARG(hash_to_range, "(1,1,2)"),
- ?ASSERT_BADARG(hash_to_range, "('',1,2)"),
- ?ASSERT_BADARG(hash_to_range, "('a','1',2)"),
- ?ASSERT_BADARG(hash_to_range, "('a',2,1)"),
- ?ASSERT_BADARG(map_to_range, "('',1,2)"),
- ?ASSERT_BADARG(map_to_range, "('a','1',2)"),
- ?ASSERT_BADARG(map_to_range, "('a',2,1)")
- ].
|