funcs_misc_test.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  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. "errors"
  17. "fmt"
  18. "reflect"
  19. "testing"
  20. "time"
  21. "github.com/stretchr/testify/assert"
  22. "github.com/stretchr/testify/require"
  23. "github.com/lf-edge/ekuiper/internal/conf"
  24. "github.com/lf-edge/ekuiper/internal/keyedstate"
  25. "github.com/lf-edge/ekuiper/internal/testx"
  26. kctx "github.com/lf-edge/ekuiper/internal/topo/context"
  27. "github.com/lf-edge/ekuiper/internal/topo/state"
  28. "github.com/lf-edge/ekuiper/pkg/api"
  29. "github.com/lf-edge/ekuiper/pkg/ast"
  30. )
  31. func init() {
  32. testx.InitEnv()
  33. }
  34. func TestToMap(t *testing.T) {
  35. f, ok := builtins["object_construct"]
  36. if !ok {
  37. t.Fatal("builtin not found")
  38. }
  39. contextLogger := conf.Log.WithField("rule", "testExec")
  40. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  41. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  42. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  43. tests := []struct {
  44. args []interface{}
  45. result interface{}
  46. }{
  47. { // 0
  48. args: []interface{}{
  49. "foo",
  50. "bar",
  51. },
  52. result: map[string]interface{}{
  53. "foo": "bar",
  54. },
  55. }, { // 1
  56. args: []interface{}{
  57. true,
  58. "bar",
  59. },
  60. result: fmt.Errorf("key true is not a string"),
  61. }, { // 2
  62. args: []interface{}{
  63. "key1",
  64. "bar",
  65. "key2",
  66. "foo",
  67. },
  68. result: map[string]interface{}{
  69. "key1": "bar",
  70. "key2": "foo",
  71. },
  72. },
  73. }
  74. for i, tt := range tests {
  75. result, _ := f.exec(fctx, tt.args)
  76. if !reflect.DeepEqual(result, tt.result) {
  77. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  78. }
  79. }
  80. }
  81. func TestCoalesceExec(t *testing.T) {
  82. f, ok := builtins["coalesce"]
  83. if !ok {
  84. t.Fatal("builtin not found")
  85. }
  86. contextLogger := conf.Log.WithField("rule", "testExec")
  87. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  88. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  89. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  90. tests := []struct {
  91. args []interface{}
  92. result interface{}
  93. }{
  94. { // 1
  95. args: []interface{}{
  96. "foo",
  97. "bar",
  98. "2",
  99. },
  100. result: "foo",
  101. },
  102. { // 2
  103. args: []interface{}{
  104. nil,
  105. "dd",
  106. "1",
  107. },
  108. result: "dd",
  109. },
  110. { // 3
  111. args: []interface{}{
  112. "bar",
  113. nil,
  114. "1",
  115. },
  116. result: "bar",
  117. },
  118. { // 4
  119. args: []interface{}{
  120. nil,
  121. nil,
  122. "2",
  123. },
  124. result: "2",
  125. },
  126. { // 4
  127. args: []interface{}{
  128. nil,
  129. nil,
  130. nil,
  131. },
  132. result: nil,
  133. },
  134. }
  135. for i, tt := range tests {
  136. result, _ := f.exec(fctx, tt.args)
  137. if !reflect.DeepEqual(result, tt.result) {
  138. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  139. }
  140. }
  141. }
  142. func TestToJson(t *testing.T) {
  143. f, ok := builtins["to_json"]
  144. if !ok {
  145. t.Fatal("builtin not found")
  146. }
  147. contextLogger := conf.Log.WithField("rule", "testExec")
  148. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  149. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  150. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  151. tests := []struct {
  152. args []interface{}
  153. result interface{}
  154. }{
  155. { // 0
  156. args: []interface{}{
  157. "foo",
  158. },
  159. result: `"foo"`,
  160. }, { // 1
  161. args: []interface{}{
  162. nil,
  163. },
  164. result: "null",
  165. }, { // 2
  166. args: []interface{}{
  167. map[string]interface{}{
  168. "key1": "bar",
  169. "key2": "foo",
  170. },
  171. },
  172. result: `{"key1":"bar","key2":"foo"}`,
  173. },
  174. }
  175. for i, tt := range tests {
  176. result, _ := f.exec(fctx, tt.args)
  177. if !reflect.DeepEqual(result, tt.result) {
  178. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  179. }
  180. }
  181. }
  182. func TestFromJson(t *testing.T) {
  183. f, ok := builtins["parse_json"]
  184. if !ok {
  185. t.Fatal("builtin not found")
  186. }
  187. contextLogger := conf.Log.WithField("rule", "testExec")
  188. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  189. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  190. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  191. tests := []struct {
  192. args []interface{}
  193. result interface{}
  194. }{
  195. { // 0
  196. args: []interface{}{
  197. `"foo"`,
  198. },
  199. result: "foo",
  200. }, { // 1
  201. args: []interface{}{
  202. "null",
  203. },
  204. result: nil,
  205. }, { // 2
  206. args: []interface{}{
  207. `{"key1":"bar","key2":"foo"}`,
  208. },
  209. result: map[string]interface{}{
  210. "key1": "bar",
  211. "key2": "foo",
  212. },
  213. }, { // 3
  214. args: []interface{}{
  215. "key1",
  216. },
  217. result: fmt.Errorf("fail to parse json: invalid character 'k' looking for beginning of value"),
  218. }, { // 4
  219. args: []interface{}{
  220. `[{"key1":"bar","key2":"foo"}]`,
  221. },
  222. result: []interface{}{
  223. map[string]interface{}{
  224. "key1": "bar",
  225. "key2": "foo",
  226. },
  227. },
  228. },
  229. }
  230. for i, tt := range tests {
  231. result, _ := f.exec(fctx, tt.args)
  232. if !reflect.DeepEqual(result, tt.result) {
  233. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  234. }
  235. }
  236. }
  237. func TestConvertTZ(t *testing.T) {
  238. f, ok := builtins["convert_tz"]
  239. if !ok {
  240. t.Fatal("builtin not found")
  241. }
  242. contextLogger := conf.Log.WithField("rule", "testExec")
  243. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  244. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  245. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  246. loc, _ := time.LoadLocation("Asia/Shanghai")
  247. tests := []struct {
  248. args []interface{}
  249. result interface{}
  250. }{
  251. { // 0
  252. args: []interface{}{
  253. time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
  254. "UTC",
  255. },
  256. result: time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
  257. }, { // 1
  258. args: []interface{}{
  259. time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
  260. "Asia/Shanghai",
  261. },
  262. result: time.Date(2022, time.April, 13, 14, 22, 32, 233000000, loc),
  263. }, { // 2
  264. args: []interface{}{
  265. time.Date(2022, time.April, 13, 6, 22, 32, 233000000, time.UTC),
  266. "Unknown",
  267. },
  268. result: errors.New("unknown time zone Unknown"),
  269. }, { // 3
  270. args: []interface{}{
  271. true,
  272. "UTC",
  273. },
  274. result: errors.New("unsupported type to convert to timestamp true"),
  275. },
  276. }
  277. for _, tt := range tests {
  278. result, _ := f.exec(fctx, tt.args)
  279. assert.Equal(t, tt.result, result)
  280. }
  281. vtests := []struct {
  282. args []ast.Expr
  283. wantErr bool
  284. }{
  285. {
  286. []ast.Expr{&ast.TimeLiteral{Val: 0}, &ast.StringLiteral{Val: "0"}},
  287. false,
  288. },
  289. {
  290. []ast.Expr{&ast.StringLiteral{Val: "0"}},
  291. true,
  292. },
  293. {
  294. []ast.Expr{&ast.NumberLiteral{Val: 0}, &ast.NumberLiteral{Val: 0}},
  295. true,
  296. },
  297. {
  298. []ast.Expr{&ast.NumberLiteral{Val: 0}, &ast.TimeLiteral{Val: 0}},
  299. true,
  300. },
  301. {
  302. []ast.Expr{&ast.NumberLiteral{Val: 0}, &ast.BooleanLiteral{Val: true}},
  303. true,
  304. },
  305. {
  306. []ast.Expr{&ast.StringLiteral{Val: "0"}, &ast.NumberLiteral{Val: 0}},
  307. true,
  308. },
  309. {
  310. []ast.Expr{&ast.BooleanLiteral{Val: true}, &ast.NumberLiteral{Val: 0}},
  311. true,
  312. },
  313. }
  314. for _, vtt := range vtests {
  315. err := f.val(fctx, vtt.args)
  316. if vtt.wantErr {
  317. assert.Error(t, err)
  318. } else {
  319. assert.NoError(t, err)
  320. }
  321. }
  322. }
  323. func TestDelay(t *testing.T) {
  324. f, ok := builtins["delay"]
  325. if !ok {
  326. t.Fatal("builtin not found")
  327. }
  328. contextLogger := conf.Log.WithField("rule", "testExec")
  329. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  330. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  331. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  332. err := f.val(fctx, []ast.Expr{&ast.StringLiteral{Val: "abc"}})
  333. if err == nil {
  334. t.Fatal("expect error")
  335. }
  336. err = f.val(fctx, []ast.Expr{&ast.StringLiteral{Val: "1s"}, &ast.StringLiteral{Val: "1s"}})
  337. if err == nil {
  338. t.Fatal("expect error")
  339. }
  340. err = f.val(fctx, []ast.Expr{&ast.IntegerLiteral{Val: 1000}, &ast.StringLiteral{Val: "1s"}})
  341. if err != nil {
  342. t.Fatal("expect no error")
  343. }
  344. tests := []struct {
  345. args []interface{}
  346. result interface{}
  347. }{
  348. { // 0
  349. args: []interface{}{
  350. 10,
  351. "bar",
  352. },
  353. result: "bar",
  354. }, { // 1
  355. args: []interface{}{
  356. "bar",
  357. "bar",
  358. },
  359. result: fmt.Errorf("cannot convert string(bar) to int"),
  360. },
  361. }
  362. for i, tt := range tests {
  363. result, _ := f.exec(fctx, tt.args)
  364. if !reflect.DeepEqual(result, tt.result) {
  365. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  366. }
  367. }
  368. }
  369. func TestKeyedStateValidation(t *testing.T) {
  370. f, ok := builtins["get_keyed_state"]
  371. if !ok {
  372. t.Fatal("builtin not found")
  373. }
  374. tests := []struct {
  375. args []ast.Expr
  376. err error
  377. }{
  378. {
  379. args: []ast.Expr{
  380. &ast.StringLiteral{Val: "foo"},
  381. },
  382. err: fmt.Errorf("Expect 3 arguments but found 1."),
  383. }, {
  384. args: []ast.Expr{
  385. &ast.StringLiteral{Val: "foo"},
  386. &ast.StringLiteral{Val: "bar"},
  387. },
  388. err: fmt.Errorf("Expect 3 arguments but found 2."),
  389. }, {
  390. args: []ast.Expr{
  391. &ast.StringLiteral{Val: "foo"},
  392. &ast.StringLiteral{Val: "bar"},
  393. &ast.StringLiteral{Val: "barz"},
  394. },
  395. err: fmt.Errorf("expect one of following value for the 2nd parameter: bigint, float, string, boolean, datetime"),
  396. }, {
  397. args: []ast.Expr{
  398. &ast.StringLiteral{Val: "foo"},
  399. &ast.StringLiteral{Val: "bigint"},
  400. &ast.StringLiteral{Val: "barz"},
  401. },
  402. err: nil,
  403. },
  404. }
  405. for i, tt := range tests {
  406. err := f.val(nil, tt.args)
  407. if !reflect.DeepEqual(err, tt.err) {
  408. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, err, tt.err)
  409. }
  410. }
  411. }
  412. func TestKeyedStateExec(t *testing.T) {
  413. keyedstate.InitKeyedStateKV()
  414. f, ok := builtins["get_keyed_state"]
  415. if !ok {
  416. t.Fatal("builtin not found")
  417. }
  418. contextLogger := conf.Log.WithField("rule", "testExec")
  419. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  420. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  421. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 1)
  422. tests := []struct {
  423. args []interface{}
  424. result interface{}
  425. }{
  426. { // 0
  427. args: []interface{}{
  428. "foo",
  429. },
  430. result: fmt.Errorf("the args must be two or three"),
  431. }, { // 1
  432. args: []interface{}{
  433. "foo",
  434. "bigint",
  435. "baz",
  436. "bar",
  437. },
  438. result: fmt.Errorf("the args must be two or three"),
  439. }, { // 2
  440. args: []interface{}{
  441. "foo",
  442. "float",
  443. 20.0,
  444. },
  445. result: 20.0,
  446. },
  447. }
  448. for i, tt := range tests {
  449. result, _ := f.exec(fctx, tt.args)
  450. if !reflect.DeepEqual(result, tt.result) {
  451. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  452. }
  453. }
  454. _ = keyedstate.ClearKeyedState()
  455. }
  456. func TestHexIntFunctions(t *testing.T) {
  457. contextLogger := conf.Log.WithField("rule", "testExec")
  458. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  459. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  460. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  461. tests := []struct {
  462. name string
  463. args []interface{}
  464. result interface{}
  465. }{
  466. {
  467. name: "hex2dec",
  468. args: []interface{}{
  469. "0x10",
  470. },
  471. result: int64(16),
  472. },
  473. {
  474. name: "dec2hex",
  475. args: []interface{}{
  476. 16,
  477. },
  478. result: "0x10",
  479. },
  480. }
  481. for i, tt := range tests {
  482. f, ok := builtins[tt.name]
  483. if !ok {
  484. t.Fatal(fmt.Sprintf("builtin %v not found", tt.name))
  485. }
  486. result, _ := f.exec(fctx, tt.args)
  487. if !reflect.DeepEqual(result, tt.result) {
  488. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  489. }
  490. }
  491. }
  492. func TestMiscFuncNil(t *testing.T) {
  493. contextLogger := conf.Log.WithField("rule", "testExec")
  494. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  495. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  496. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  497. oldBuiltins := builtins
  498. defer func() {
  499. builtins = oldBuiltins
  500. }()
  501. builtins = map[string]builtinFunc{}
  502. registerMiscFunc()
  503. for name, function := range builtins {
  504. switch name {
  505. case "compress", "decompress", "newuuid", "tstamp", "rule_id", "window_start", "window_end", "event_time",
  506. "json_path_query", "json_path_query_first", "coalesce", "meta", "json_path_exists":
  507. continue
  508. case "isnull":
  509. v, b := function.exec(fctx, []interface{}{nil})
  510. require.True(t, b)
  511. require.Equal(t, v, true)
  512. case "cardinality":
  513. v, b := function.check([]interface{}{nil})
  514. require.True(t, b)
  515. require.Equal(t, v, 0)
  516. case "to_json":
  517. v, b := function.exec(fctx, []interface{}{nil})
  518. require.True(t, b)
  519. require.Equal(t, v, "null")
  520. case "parse_json":
  521. v, b := function.exec(fctx, []interface{}{nil})
  522. require.True(t, b)
  523. require.Equal(t, v, nil)
  524. v, b = function.exec(fctx, []interface{}{"null"})
  525. require.True(t, b)
  526. require.Equal(t, v, nil)
  527. default:
  528. v, b := function.check([]interface{}{nil})
  529. require.True(t, b, fmt.Sprintf("%v failed", name))
  530. require.Nil(t, v, fmt.Sprintf("%v failed", name))
  531. }
  532. }
  533. }