funcs_obj_test.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. // Copyright 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. "sort"
  19. "testing"
  20. "github.com/stretchr/testify/require"
  21. "github.com/lf-edge/ekuiper/internal/conf"
  22. kctx "github.com/lf-edge/ekuiper/internal/topo/context"
  23. "github.com/lf-edge/ekuiper/internal/topo/state"
  24. "github.com/lf-edge/ekuiper/pkg/api"
  25. )
  26. func TestObjectFunctions(t *testing.T) {
  27. contextLogger := conf.Log.WithField("rule", "testExec")
  28. ctx := kctx.WithValue(kctx.Background(), kctx.LoggerKey, contextLogger)
  29. tempStore, _ := state.CreateStore("mockRule0", api.AtMostOnce)
  30. fctx := kctx.NewDefaultFuncContext(ctx.WithMeta("mockRule0", "test", tempStore), 2)
  31. tests := []struct {
  32. name string
  33. args []interface{}
  34. result interface{}
  35. }{
  36. {
  37. name: "keys",
  38. args: []interface{}{
  39. map[string]interface{}{
  40. "a": 1,
  41. "b": 2,
  42. },
  43. },
  44. result: []string{"a", "b"},
  45. },
  46. {
  47. name: "keys",
  48. args: []interface{}{1, 2},
  49. result: fmt.Errorf("the argument should be map[string]interface{}"),
  50. },
  51. {
  52. name: "values",
  53. args: []interface{}{
  54. map[string]interface{}{
  55. "a": "c",
  56. "b": "d",
  57. },
  58. },
  59. result: []interface{}{"c", "d"},
  60. },
  61. {
  62. name: "values",
  63. args: []interface{}{1, 2},
  64. result: fmt.Errorf("the argument should be map[string]interface{}"),
  65. },
  66. {
  67. name: "object",
  68. args: []interface{}{
  69. []interface{}{"a", "b"},
  70. []interface{}{1, 2},
  71. },
  72. result: map[string]interface{}{
  73. "a": 1,
  74. "b": 2,
  75. },
  76. },
  77. {
  78. name: "object",
  79. args: []interface{}{
  80. 1,
  81. []interface{}{1, 2},
  82. },
  83. result: fmt.Errorf("first argument should be []string"),
  84. },
  85. {
  86. name: "object",
  87. args: []interface{}{
  88. []interface{}{1, 2},
  89. []interface{}{1, 2},
  90. },
  91. result: fmt.Errorf("first argument should be []string"),
  92. },
  93. {
  94. name: "object",
  95. args: []interface{}{
  96. []interface{}{1, 2},
  97. 1,
  98. },
  99. result: fmt.Errorf("second argument should be []interface{}"),
  100. },
  101. {
  102. name: "object",
  103. args: []interface{}{
  104. []interface{}{"a", "b"},
  105. []interface{}{1, 2, 3},
  106. },
  107. result: fmt.Errorf("the length of the arguments should be same"),
  108. },
  109. {
  110. name: "zip",
  111. args: []interface{}{
  112. []interface{}{
  113. []interface{}{"a", 1},
  114. []interface{}{"b", 2},
  115. },
  116. },
  117. result: map[string]interface{}{
  118. "a": 1,
  119. "b": 2,
  120. },
  121. },
  122. {
  123. name: "zip",
  124. args: []interface{}{
  125. 1,
  126. },
  127. result: fmt.Errorf("each argument should be [][2]interface{}"),
  128. },
  129. {
  130. name: "zip",
  131. args: []interface{}{
  132. []interface{}{
  133. 1, 2,
  134. },
  135. },
  136. result: fmt.Errorf("each argument should be [][2]interface{}"),
  137. },
  138. {
  139. name: "zip",
  140. args: []interface{}{
  141. []interface{}{
  142. []interface{}{"a", 1, 3},
  143. []interface{}{"b", 2, 4},
  144. },
  145. },
  146. result: fmt.Errorf("each argument should be [][2]interface{}"),
  147. },
  148. {
  149. name: "zip",
  150. args: []interface{}{
  151. []interface{}{
  152. []interface{}{1, 3},
  153. []interface{}{2, 4},
  154. },
  155. },
  156. result: fmt.Errorf("the first element in the list item should be string"),
  157. },
  158. {
  159. name: "items",
  160. args: []interface{}{
  161. map[string]interface{}{
  162. "a": 1,
  163. "b": 2,
  164. },
  165. },
  166. result: []interface{}{
  167. []interface{}{"a", 1},
  168. []interface{}{"b", 2},
  169. },
  170. },
  171. {
  172. name: "items",
  173. args: []interface{}{
  174. 1,
  175. },
  176. result: fmt.Errorf("first argument should be map[string]interface{}"),
  177. },
  178. {
  179. name: "element_at",
  180. args: []interface{}{
  181. map[string]interface{}{
  182. "a": 1,
  183. "b": 2,
  184. },
  185. "a",
  186. },
  187. result: 1,
  188. },
  189. {
  190. name: "element_at",
  191. args: []interface{}{
  192. "1",
  193. "a",
  194. },
  195. result: fmt.Errorf("first argument should be []interface{} or map[string]interface{}"),
  196. },
  197. {
  198. name: "element_at",
  199. args: []interface{}{
  200. map[string]interface{}{
  201. "a": 1,
  202. "b": 2,
  203. },
  204. 2,
  205. },
  206. result: fmt.Errorf("second argument should be string"),
  207. },
  208. {
  209. name: "object_concat",
  210. args: []interface{}{
  211. map[string]interface{}{
  212. "a": 1,
  213. "b": 2,
  214. },
  215. map[string]interface{}{
  216. "b": 3,
  217. "c": 4,
  218. },
  219. map[string]interface{}{
  220. "a": 2,
  221. "d": 1,
  222. },
  223. },
  224. result: map[string]interface{}{
  225. "a": 2,
  226. "b": 3,
  227. "c": 4,
  228. "d": 1,
  229. },
  230. },
  231. {
  232. name: "object_concat",
  233. args: []interface{}{
  234. map[string]interface{}{
  235. "a": 1,
  236. "b": 2,
  237. },
  238. map[string]interface{}{
  239. "b": 3,
  240. "c": 4,
  241. },
  242. []interface{}{
  243. 1,
  244. 2,
  245. },
  246. },
  247. result: fmt.Errorf("the argument should be map[string]interface{}, got %v", []interface{}{1, 2}),
  248. },
  249. }
  250. for i, tt := range tests {
  251. f, ok := builtins[tt.name]
  252. if !ok {
  253. t.Fatal(fmt.Sprintf("builtin %v not found", tt.name))
  254. }
  255. result, _ := f.exec(fctx, tt.args)
  256. switch r := result.(type) {
  257. case []string:
  258. sort.Strings(r)
  259. if !reflect.DeepEqual(r, tt.result) {
  260. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, r, tt.result)
  261. }
  262. case []interface{}:
  263. rr := make([]interface{}, len(r))
  264. copy(rr, r)
  265. rr[0] = r[1]
  266. rr[1] = r[0]
  267. if !reflect.DeepEqual(r, tt.result) && !reflect.DeepEqual(rr, tt.result) {
  268. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, r, tt.result)
  269. }
  270. default:
  271. if !reflect.DeepEqual(result, tt.result) {
  272. t.Errorf("%d result mismatch,\ngot:\t%v \nwant:\t%v", i, result, tt.result)
  273. }
  274. }
  275. }
  276. }
  277. func TestObjectFunctionsNil(t *testing.T) {
  278. oldBuiltins := builtins
  279. defer func() {
  280. builtins = oldBuiltins
  281. }()
  282. builtins = map[string]builtinFunc{}
  283. registerObjectFunc()
  284. for name, function := range builtins {
  285. r, b := function.check([]interface{}{nil})
  286. require.True(t, b, fmt.Sprintf("%v failed", name))
  287. require.Nil(t, r, fmt.Sprintf("%v failed", name))
  288. }
  289. }