funcs_misc_test.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425
  1. // Copyright 2022-2023 EMQ Technologies Co., Ltd.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package function
  15. import (
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "github.com/stretchr/testify/require"
  20. "github.com/lf-edge/ekuiper/internal/conf"
  21. "github.com/lf-edge/ekuiper/internal/keyedstate"
  22. "github.com/lf-edge/ekuiper/internal/testx"
  23. kctx "github.com/lf-edge/ekuiper/internal/topo/context"
  24. "github.com/lf-edge/ekuiper/internal/topo/state"
  25. "github.com/lf-edge/ekuiper/pkg/api"
  26. "github.com/lf-edge/ekuiper/pkg/ast"
  27. )
  28. func init() {
  29. testx.InitEnv()
  30. }
  31. func TestToMap(t *testing.T) {
  32. f, ok := builtins["object_construct"]
  33. if !ok {
  34. t.Fatal("builtin not found")
  35. }
  36. contextLogger := conf.Log.WithField("rule", "testExec")
  37. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  38. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  39. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  40. tests := []struct {
  41. args []interface{}
  42. result interface{}
  43. }{
  44. { // 0
  45. args: []interface{}{
  46. "foo",
  47. "bar",
  48. },
  49. result: map[string]interface{}{
  50. "foo": "bar",
  51. },
  52. }, { // 1
  53. args: []interface{}{
  54. true,
  55. "bar",
  56. },
  57. result: fmt.Errorf("key true is not a string"),
  58. }, { // 2
  59. args: []interface{}{
  60. "key1",
  61. "bar",
  62. "key2",
  63. "foo",
  64. },
  65. result: map[string]interface{}{
  66. "key1": "bar",
  67. "key2": "foo",
  68. },
  69. },
  70. }
  71. for i, tt := range tests {
  72. result, _ := f.exec(fctx, tt.args)
  73. if !reflect.DeepEqual(result, tt.result) {
  74. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  75. }
  76. }
  77. }
  78. func TestCoalesceExec(t *testing.T) {
  79. f, ok := builtins["coalesce"]
  80. if !ok {
  81. t.Fatal("builtin not found")
  82. }
  83. contextLogger := conf.Log.WithField("rule", "testExec")
  84. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  85. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  86. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  87. tests := []struct {
  88. args []interface{}
  89. result interface{}
  90. }{
  91. { // 1
  92. args: []interface{}{
  93. "foo",
  94. "bar",
  95. "2",
  96. },
  97. result: "foo",
  98. },
  99. { // 2
  100. args: []interface{}{
  101. nil,
  102. "dd",
  103. "1",
  104. },
  105. result: "dd",
  106. },
  107. { // 3
  108. args: []interface{}{
  109. "bar",
  110. nil,
  111. "1",
  112. },
  113. result: "bar",
  114. },
  115. { // 4
  116. args: []interface{}{
  117. nil,
  118. nil,
  119. "2",
  120. },
  121. result: "2",
  122. },
  123. { // 4
  124. args: []interface{}{
  125. nil,
  126. nil,
  127. nil,
  128. },
  129. result: nil,
  130. },
  131. }
  132. for i, tt := range tests {
  133. result, _ := f.exec(fctx, tt.args)
  134. if !reflect.DeepEqual(result, tt.result) {
  135. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  136. }
  137. }
  138. }
  139. func TestToJson(t *testing.T) {
  140. f, ok := builtins["to_json"]
  141. if !ok {
  142. t.Fatal("builtin not found")
  143. }
  144. contextLogger := conf.Log.WithField("rule", "testExec")
  145. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  146. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  147. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  148. tests := []struct {
  149. args []interface{}
  150. result interface{}
  151. }{
  152. { // 0
  153. args: []interface{}{
  154. "foo",
  155. },
  156. result: `"foo"`,
  157. }, { // 1
  158. args: []interface{}{
  159. nil,
  160. },
  161. result: "null",
  162. }, { // 2
  163. args: []interface{}{
  164. map[string]interface{}{
  165. "key1": "bar",
  166. "key2": "foo",
  167. },
  168. },
  169. result: `{"key1":"bar","key2":"foo"}`,
  170. },
  171. }
  172. for i, tt := range tests {
  173. result, _ := f.exec(fctx, tt.args)
  174. if !reflect.DeepEqual(result, tt.result) {
  175. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  176. }
  177. }
  178. }
  179. func TestFromJson(t *testing.T) {
  180. f, ok := builtins["parse_json"]
  181. if !ok {
  182. t.Fatal("builtin not found")
  183. }
  184. contextLogger := conf.Log.WithField("rule", "testExec")
  185. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  186. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  187. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  188. tests := []struct {
  189. args []interface{}
  190. result interface{}
  191. }{
  192. { // 0
  193. args: []interface{}{
  194. `"foo"`,
  195. },
  196. result: "foo",
  197. }, { // 1
  198. args: []interface{}{
  199. "null",
  200. },
  201. result: nil,
  202. }, { // 2
  203. args: []interface{}{
  204. `{"key1":"bar","key2":"foo"}`,
  205. },
  206. result: map[string]interface{}{
  207. "key1": "bar",
  208. "key2": "foo",
  209. },
  210. }, { // 3
  211. args: []interface{}{
  212. "key1",
  213. },
  214. result: fmt.Errorf("fail to parse json: invalid character 'k' looking for beginning of value"),
  215. }, { // 4
  216. args: []interface{}{
  217. `[{"key1":"bar","key2":"foo"}]`,
  218. },
  219. result: []interface{}{
  220. map[string]interface{}{
  221. "key1": "bar",
  222. "key2": "foo",
  223. },
  224. },
  225. },
  226. }
  227. for i, tt := range tests {
  228. result, _ := f.exec(fctx, tt.args)
  229. if !reflect.DeepEqual(result, tt.result) {
  230. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  231. }
  232. }
  233. }
  234. func TestDelay(t *testing.T) {
  235. f, ok := builtins["delay"]
  236. if !ok {
  237. t.Fatal("builtin not found")
  238. }
  239. contextLogger := conf.Log.WithField("rule", "testExec")
  240. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  241. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  242. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  243. err := f.val(fctx, []ast.Expr{&ast.StringLiteral{Val: "abc"}})
  244. if err == nil {
  245. t.Fatal("expect error")
  246. }
  247. err = f.val(fctx, []ast.Expr{&ast.StringLiteral{Val: "1s"}, &ast.StringLiteral{Val: "1s"}})
  248. if err == nil {
  249. t.Fatal("expect error")
  250. }
  251. err = f.val(fctx, []ast.Expr{&ast.IntegerLiteral{Val: 1000}, &ast.StringLiteral{Val: "1s"}})
  252. if err != nil {
  253. t.Fatal("expect no error")
  254. }
  255. tests := []struct {
  256. args []interface{}
  257. result interface{}
  258. }{
  259. { // 0
  260. args: []interface{}{
  261. 10,
  262. "bar",
  263. },
  264. result: "bar",
  265. }, { // 1
  266. args: []interface{}{
  267. "bar",
  268. "bar",
  269. },
  270. result: fmt.Errorf("cannot convert string(bar) to int"),
  271. },
  272. }
  273. for i, tt := range tests {
  274. result, _ := f.exec(fctx, tt.args)
  275. if !reflect.DeepEqual(result, tt.result) {
  276. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  277. }
  278. }
  279. }
  280. func TestKeyedStateValidation(t *testing.T) {
  281. f, ok := builtins["get_keyed_state"]
  282. if !ok {
  283. t.Fatal("builtin not found")
  284. }
  285. tests := []struct {
  286. args []ast.Expr
  287. err error
  288. }{
  289. {
  290. args: []ast.Expr{
  291. &ast.StringLiteral{Val: "foo"},
  292. },
  293. err: fmt.Errorf("Expect 3 arguments but found 1."),
  294. }, {
  295. args: []ast.Expr{
  296. &ast.StringLiteral{Val: "foo"},
  297. &ast.StringLiteral{Val: "bar"},
  298. },
  299. err: fmt.Errorf("Expect 3 arguments but found 2."),
  300. }, {
  301. args: []ast.Expr{
  302. &ast.StringLiteral{Val: "foo"},
  303. &ast.StringLiteral{Val: "bar"},
  304. &ast.StringLiteral{Val: "barz"},
  305. },
  306. err: fmt.Errorf("expect one of following value for the 2nd parameter: bigint, float, string, boolean, datetime"),
  307. }, {
  308. args: []ast.Expr{
  309. &ast.StringLiteral{Val: "foo"},
  310. &ast.StringLiteral{Val: "bigint"},
  311. &ast.StringLiteral{Val: "barz"},
  312. },
  313. err: nil,
  314. },
  315. }
  316. for i, tt := range tests {
  317. err := f.val(nil, tt.args)
  318. if !reflect.DeepEqual(err, tt.err) {
  319. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, err, tt.err)
  320. }
  321. }
  322. }
  323. func TestKeyedStateExec(t *testing.T) {
  324. keyedstate.InitKeyedStateKV()
  325. f, ok := builtins["get_keyed_state"]
  326. if !ok {
  327. t.Fatal("builtin not found")
  328. }
  329. contextLogger := conf.Log.WithField("rule", "testExec")
  330. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  331. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  332. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 1)
  333. tests := []struct {
  334. args []interface{}
  335. result interface{}
  336. }{
  337. { // 0
  338. args: []interface{}{
  339. "foo",
  340. },
  341. result: fmt.Errorf("the args must be two or three"),
  342. }, { // 1
  343. args: []interface{}{
  344. "foo",
  345. "bigint",
  346. "baz",
  347. "bar",
  348. },
  349. result: fmt.Errorf("the args must be two or three"),
  350. }, { // 2
  351. args: []interface{}{
  352. "foo",
  353. "float",
  354. 20.0,
  355. },
  356. result: 20.0,
  357. },
  358. }
  359. for i, tt := range tests {
  360. result, _ := f.exec(fctx, tt.args)
  361. if !reflect.DeepEqual(result, tt.result) {
  362. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  363. }
  364. }
  365. _ = keyedstate.ClearKeyedState()
  366. }
  367. func TestMiscFuncNil(t *testing.T) {
  368. contextLogger := conf.Log.WithField("rule", "testExec")
  369. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  370. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  371. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  372. oldBuiltins := builtins
  373. defer func() {
  374. builtins = oldBuiltins
  375. }()
  376. builtins = map[string]builtinFunc{}
  377. registerMiscFunc()
  378. for name, function := range builtins {
  379. switch name {
  380. case "compress", "decompress", "newuuid", "tstamp", "rule_id", "window_start", "window_end",
  381. "json_path_query", "json_path_query_first", "coalesce", "meta", "json_path_exists":
  382. continue
  383. case "isnull":
  384. v, b := function.exec(fctx, []interface{}{nil})
  385. require.True(t, b)
  386. require.Equal(t, v, true)
  387. case "cardinality":
  388. v, b := function.check([]interface{}{nil})
  389. require.True(t, b)
  390. require.Equal(t, v, 0)
  391. case "to_json":
  392. v, b := function.exec(fctx, []interface{}{nil})
  393. require.True(t, b)
  394. require.Equal(t, v, "null")
  395. case "parse_json":
  396. v, b := function.exec(fctx, []interface{}{nil})
  397. require.True(t, b)
  398. require.Equal(t, v, nil)
  399. v, b = function.exec(fctx, []interface{}{"null"})
  400. require.True(t, b)
  401. require.Equal(t, v, nil)
  402. default:
  403. v, b := function.check([]interface{}{nil})
  404. require.True(t, b, fmt.Sprintf("%v failed", name))
  405. require.Nil(t, v, fmt.Sprintf("%v failed", name))
  406. }
  407. }
  408. }