rule_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. package processors
  2. import (
  3. "encoding/json"
  4. "github.com/emqx/kuiper/xstream/api"
  5. "testing"
  6. )
  7. func TestSingleSQL(t *testing.T) {
  8. //Reset
  9. streamList := []string{"demo", "demoError", "demo1"}
  10. handleStream(false, streamList, t)
  11. //Data setup
  12. var tests = []ruleTest{
  13. {
  14. name: `TestSingleSQLRule1`,
  15. sql: `SELECT * FROM demo`,
  16. r: [][]map[string]interface{}{
  17. {{
  18. "color": "red",
  19. "size": float64(3),
  20. "ts": float64(1541152486013),
  21. }},
  22. {{
  23. "color": "blue",
  24. "size": float64(6),
  25. "ts": float64(1541152486822),
  26. }},
  27. {{
  28. "color": "blue",
  29. "size": float64(2),
  30. "ts": float64(1541152487632),
  31. }},
  32. {{
  33. "color": "yellow",
  34. "size": float64(4),
  35. "ts": float64(1541152488442),
  36. }},
  37. {{
  38. "color": "red",
  39. "size": float64(1),
  40. "ts": float64(1541152489252),
  41. }},
  42. },
  43. m: map[string]interface{}{
  44. "op_preprocessor_demo_0_exceptions_total": int64(0),
  45. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  46. "op_preprocessor_demo_0_records_in_total": int64(5),
  47. "op_preprocessor_demo_0_records_out_total": int64(5),
  48. "op_project_0_exceptions_total": int64(0),
  49. "op_project_0_process_latency_ms": int64(0),
  50. "op_project_0_records_in_total": int64(5),
  51. "op_project_0_records_out_total": int64(5),
  52. "sink_mockSink_0_exceptions_total": int64(0),
  53. "sink_mockSink_0_records_in_total": int64(5),
  54. "sink_mockSink_0_records_out_total": int64(5),
  55. "source_demo_0_exceptions_total": int64(0),
  56. "source_demo_0_records_in_total": int64(5),
  57. "source_demo_0_records_out_total": int64(5),
  58. },
  59. }, {
  60. name: `TestSingleSQLRule2`,
  61. sql: `SELECT color, ts FROM demo where size > 3`,
  62. r: [][]map[string]interface{}{
  63. {{
  64. "color": "blue",
  65. "ts": float64(1541152486822),
  66. }},
  67. {{
  68. "color": "yellow",
  69. "ts": float64(1541152488442),
  70. }},
  71. },
  72. m: map[string]interface{}{
  73. "op_preprocessor_demo_0_exceptions_total": int64(0),
  74. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  75. "op_preprocessor_demo_0_records_in_total": int64(5),
  76. "op_preprocessor_demo_0_records_out_total": int64(5),
  77. "op_project_0_exceptions_total": int64(0),
  78. "op_project_0_process_latency_ms": int64(0),
  79. "op_project_0_records_in_total": int64(2),
  80. "op_project_0_records_out_total": int64(2),
  81. "sink_mockSink_0_exceptions_total": int64(0),
  82. "sink_mockSink_0_records_in_total": int64(2),
  83. "sink_mockSink_0_records_out_total": int64(2),
  84. "source_demo_0_exceptions_total": int64(0),
  85. "source_demo_0_records_in_total": int64(5),
  86. "source_demo_0_records_out_total": int64(5),
  87. "op_filter_0_exceptions_total": int64(0),
  88. "op_filter_0_process_latency_ms": int64(0),
  89. "op_filter_0_records_in_total": int64(5),
  90. "op_filter_0_records_out_total": int64(2),
  91. },
  92. }, {
  93. name: `TestSingleSQLRule3`,
  94. sql: `SELECT size as Int8, ts FROM demo where size > 3`,
  95. r: [][]map[string]interface{}{
  96. {{
  97. "Int8": float64(6),
  98. "ts": float64(1541152486822),
  99. }},
  100. {{
  101. "Int8": float64(4),
  102. "ts": float64(1541152488442),
  103. }},
  104. },
  105. m: map[string]interface{}{
  106. "op_preprocessor_demo_0_exceptions_total": int64(0),
  107. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  108. "op_preprocessor_demo_0_records_in_total": int64(5),
  109. "op_preprocessor_demo_0_records_out_total": int64(5),
  110. "op_project_0_exceptions_total": int64(0),
  111. "op_project_0_process_latency_ms": int64(0),
  112. "op_project_0_records_in_total": int64(2),
  113. "op_project_0_records_out_total": int64(2),
  114. "sink_mockSink_0_exceptions_total": int64(0),
  115. "sink_mockSink_0_records_in_total": int64(2),
  116. "sink_mockSink_0_records_out_total": int64(2),
  117. "source_demo_0_exceptions_total": int64(0),
  118. "source_demo_0_records_in_total": int64(5),
  119. "source_demo_0_records_out_total": int64(5),
  120. "op_filter_0_exceptions_total": int64(0),
  121. "op_filter_0_process_latency_ms": int64(0),
  122. "op_filter_0_records_in_total": int64(5),
  123. "op_filter_0_records_out_total": int64(2),
  124. },
  125. }, {
  126. name: `TestSingleSQLRule4`,
  127. sql: `SELECT size as Int8, ts FROM demoError where size > 3`,
  128. r: [][]map[string]interface{}{
  129. {{
  130. "error": "error in preprocessor: invalid data type for color, expect string but found int(3)",
  131. }},
  132. {{
  133. "Int8": float64(6),
  134. "ts": float64(1541152486822),
  135. }},
  136. {{
  137. "error": "error in preprocessor: invalid data type for color, expect string but found int(7)",
  138. }},
  139. {{
  140. "error": "error in preprocessor: invalid data type for size, expect bigint but found string(blue)",
  141. }},
  142. },
  143. m: map[string]interface{}{
  144. "op_preprocessor_demoError_0_exceptions_total": int64(3),
  145. "op_preprocessor_demoError_0_process_latency_ms": int64(0),
  146. "op_preprocessor_demoError_0_records_in_total": int64(5),
  147. "op_preprocessor_demoError_0_records_out_total": int64(2),
  148. "op_project_0_exceptions_total": int64(3),
  149. "op_project_0_process_latency_ms": int64(0),
  150. "op_project_0_records_in_total": int64(4),
  151. "op_project_0_records_out_total": int64(1),
  152. "sink_mockSink_0_exceptions_total": int64(0),
  153. "sink_mockSink_0_records_in_total": int64(4),
  154. "sink_mockSink_0_records_out_total": int64(4),
  155. "source_demoError_0_exceptions_total": int64(0),
  156. "source_demoError_0_records_in_total": int64(5),
  157. "source_demoError_0_records_out_total": int64(5),
  158. "op_filter_0_exceptions_total": int64(3),
  159. "op_filter_0_process_latency_ms": int64(0),
  160. "op_filter_0_records_in_total": int64(5),
  161. "op_filter_0_records_out_total": int64(1),
  162. },
  163. }, {
  164. name: `TestSingleSQLRule4`,
  165. sql: `SELECT size as Int8, ts FROM demoError where size > 3`,
  166. r: [][]map[string]interface{}{
  167. {{
  168. "error": "error in preprocessor: invalid data type for color, expect string but found int(3)",
  169. }},
  170. {{
  171. "Int8": float64(6),
  172. "ts": float64(1541152486822),
  173. }},
  174. {{
  175. "error": "error in preprocessor: invalid data type for color, expect string but found int(7)",
  176. }},
  177. {{
  178. "error": "error in preprocessor: invalid data type for size, expect bigint but found string(blue)",
  179. }},
  180. },
  181. m: map[string]interface{}{
  182. "op_preprocessor_demoError_0_exceptions_total": int64(3),
  183. "op_preprocessor_demoError_0_process_latency_ms": int64(0),
  184. "op_preprocessor_demoError_0_records_in_total": int64(5),
  185. "op_preprocessor_demoError_0_records_out_total": int64(2),
  186. "op_project_0_exceptions_total": int64(3),
  187. "op_project_0_process_latency_ms": int64(0),
  188. "op_project_0_records_in_total": int64(4),
  189. "op_project_0_records_out_total": int64(1),
  190. "sink_mockSink_0_exceptions_total": int64(0),
  191. "sink_mockSink_0_records_in_total": int64(4),
  192. "sink_mockSink_0_records_out_total": int64(4),
  193. "source_demoError_0_exceptions_total": int64(0),
  194. "source_demoError_0_records_in_total": int64(5),
  195. "source_demoError_0_records_out_total": int64(5),
  196. "op_filter_0_exceptions_total": int64(3),
  197. "op_filter_0_process_latency_ms": int64(0),
  198. "op_filter_0_records_in_total": int64(5),
  199. "op_filter_0_records_out_total": int64(1),
  200. },
  201. }, {
  202. name: `TestSingleSQLRule5`,
  203. sql: `SELECT meta(topic) as m, ts FROM demo`,
  204. r: [][]map[string]interface{}{
  205. {{
  206. "m": "mock",
  207. "ts": float64(1541152486013),
  208. }},
  209. {{
  210. "m": "mock",
  211. "ts": float64(1541152486822),
  212. }},
  213. {{
  214. "m": "mock",
  215. "ts": float64(1541152487632),
  216. }},
  217. {{
  218. "m": "mock",
  219. "ts": float64(1541152488442),
  220. }},
  221. {{
  222. "m": "mock",
  223. "ts": float64(1541152489252),
  224. }},
  225. },
  226. m: map[string]interface{}{
  227. "op_preprocessor_demo_0_exceptions_total": int64(0),
  228. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  229. "op_preprocessor_demo_0_records_in_total": int64(5),
  230. "op_preprocessor_demo_0_records_out_total": int64(5),
  231. "op_project_0_exceptions_total": int64(0),
  232. "op_project_0_process_latency_ms": int64(0),
  233. "op_project_0_records_in_total": int64(5),
  234. "op_project_0_records_out_total": int64(5),
  235. "sink_mockSink_0_exceptions_total": int64(0),
  236. "sink_mockSink_0_records_in_total": int64(5),
  237. "sink_mockSink_0_records_out_total": int64(5),
  238. "source_demo_0_exceptions_total": int64(0),
  239. "source_demo_0_records_in_total": int64(5),
  240. "source_demo_0_records_out_total": int64(5),
  241. },
  242. }, {
  243. name: `TestSingleSQLRule6`,
  244. sql: `SELECT color, ts FROM demo where size > 3 and meta(topic)="mock"`,
  245. r: [][]map[string]interface{}{
  246. {{
  247. "color": "blue",
  248. "ts": float64(1541152486822),
  249. }},
  250. {{
  251. "color": "yellow",
  252. "ts": float64(1541152488442),
  253. }},
  254. },
  255. m: map[string]interface{}{
  256. "op_preprocessor_demo_0_exceptions_total": int64(0),
  257. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  258. "op_preprocessor_demo_0_records_in_total": int64(5),
  259. "op_preprocessor_demo_0_records_out_total": int64(5),
  260. "op_project_0_exceptions_total": int64(0),
  261. "op_project_0_process_latency_ms": int64(0),
  262. "op_project_0_records_in_total": int64(2),
  263. "op_project_0_records_out_total": int64(2),
  264. "sink_mockSink_0_exceptions_total": int64(0),
  265. "sink_mockSink_0_records_in_total": int64(2),
  266. "sink_mockSink_0_records_out_total": int64(2),
  267. "source_demo_0_exceptions_total": int64(0),
  268. "source_demo_0_records_in_total": int64(5),
  269. "source_demo_0_records_out_total": int64(5),
  270. "op_filter_0_exceptions_total": int64(0),
  271. "op_filter_0_process_latency_ms": int64(0),
  272. "op_filter_0_records_in_total": int64(5),
  273. "op_filter_0_records_out_total": int64(2),
  274. },
  275. }, {
  276. name: `TestSingleSQLRule7`,
  277. sql: "SELECT `from` FROM demo1",
  278. r: [][]map[string]interface{}{
  279. {{
  280. "from": "device1",
  281. }},
  282. {{
  283. "from": "device2",
  284. }},
  285. {{
  286. "from": "device3",
  287. }},
  288. {{
  289. "from": "device1",
  290. }},
  291. {{
  292. "from": "device3",
  293. }},
  294. },
  295. m: map[string]interface{}{
  296. "op_preprocessor_demo1_0_exceptions_total": int64(0),
  297. "op_preprocessor_demo1_0_process_latency_ms": int64(0),
  298. "op_preprocessor_demo1_0_records_in_total": int64(5),
  299. "op_preprocessor_demo1_0_records_out_total": int64(5),
  300. "op_project_0_exceptions_total": int64(0),
  301. "op_project_0_process_latency_ms": int64(0),
  302. "op_project_0_records_in_total": int64(5),
  303. "op_project_0_records_out_total": int64(5),
  304. "sink_mockSink_0_exceptions_total": int64(0),
  305. "sink_mockSink_0_records_in_total": int64(5),
  306. "sink_mockSink_0_records_out_total": int64(5),
  307. "source_demo1_0_exceptions_total": int64(0),
  308. "source_demo1_0_records_in_total": int64(5),
  309. "source_demo1_0_records_out_total": int64(5),
  310. },
  311. }, {
  312. name: `TestSingleSQLRule8`,
  313. sql: "SELECT * FROM demo1 where `from`=\"device1\"",
  314. r: [][]map[string]interface{}{
  315. {{
  316. "temp": float64(25.5),
  317. "hum": float64(65),
  318. "from": "device1",
  319. "ts": float64(1541152486013),
  320. }},
  321. {{
  322. "temp": float64(27.4),
  323. "hum": float64(80),
  324. "from": "device1",
  325. "ts": float64(1541152488442),
  326. }},
  327. },
  328. m: map[string]interface{}{
  329. "op_preprocessor_demo1_0_exceptions_total": int64(0),
  330. "op_preprocessor_demo1_0_process_latency_ms": int64(0),
  331. "op_preprocessor_demo1_0_records_in_total": int64(5),
  332. "op_preprocessor_demo1_0_records_out_total": int64(5),
  333. "op_project_0_exceptions_total": int64(0),
  334. "op_project_0_process_latency_ms": int64(0),
  335. "op_project_0_records_in_total": int64(2),
  336. "op_project_0_records_out_total": int64(2),
  337. "op_filter_0_exceptions_total": int64(0),
  338. "op_filter_0_process_latency_ms": int64(0),
  339. "op_filter_0_records_in_total": int64(5),
  340. "op_filter_0_records_out_total": int64(2),
  341. "sink_mockSink_0_exceptions_total": int64(0),
  342. "sink_mockSink_0_records_in_total": int64(2),
  343. "sink_mockSink_0_records_out_total": int64(2),
  344. "source_demo1_0_exceptions_total": int64(0),
  345. "source_demo1_0_records_in_total": int64(5),
  346. "source_demo1_0_records_out_total": int64(5),
  347. },
  348. },
  349. }
  350. handleStream(true, streamList, t)
  351. options := []*api.RuleOption{
  352. {
  353. BufferLength: 100,
  354. }, {
  355. BufferLength: 100,
  356. Qos: api.AtLeastOnce,
  357. CheckpointInterval: 5000,
  358. }, {
  359. BufferLength: 100,
  360. Qos: api.ExactlyOnce,
  361. CheckpointInterval: 5000,
  362. },
  363. }
  364. for j, opt := range options {
  365. doRuleTest(t, tests, j, opt)
  366. }
  367. }
  368. func TestSingleSQLError(t *testing.T) {
  369. //Reset
  370. streamList := []string{"ldemo"}
  371. handleStream(false, streamList, t)
  372. //Data setup
  373. var tests = []ruleTest{
  374. {
  375. name: `TestSingleSQLErrorRule1`,
  376. sql: `SELECT color, ts FROM ldemo where size >= 3`,
  377. r: [][]map[string]interface{}{
  378. {{
  379. "color": "red",
  380. "ts": float64(1541152486013),
  381. }},
  382. {{
  383. "error": "run Where error: invalid operation string(string) >= int64(3)",
  384. }},
  385. {{
  386. "ts": float64(1541152487632),
  387. }},
  388. },
  389. m: map[string]interface{}{
  390. "op_preprocessor_ldemo_0_exceptions_total": int64(0),
  391. "op_preprocessor_ldemo_0_process_latency_ms": int64(0),
  392. "op_preprocessor_ldemo_0_records_in_total": int64(5),
  393. "op_preprocessor_ldemo_0_records_out_total": int64(5),
  394. "op_project_0_exceptions_total": int64(1),
  395. "op_project_0_process_latency_ms": int64(0),
  396. "op_project_0_records_in_total": int64(3),
  397. "op_project_0_records_out_total": int64(2),
  398. "sink_mockSink_0_exceptions_total": int64(0),
  399. "sink_mockSink_0_records_in_total": int64(3),
  400. "sink_mockSink_0_records_out_total": int64(3),
  401. "source_ldemo_0_exceptions_total": int64(0),
  402. "source_ldemo_0_records_in_total": int64(5),
  403. "source_ldemo_0_records_out_total": int64(5),
  404. "op_filter_0_exceptions_total": int64(1),
  405. "op_filter_0_process_latency_ms": int64(0),
  406. "op_filter_0_records_in_total": int64(5),
  407. "op_filter_0_records_out_total": int64(2),
  408. },
  409. }, {
  410. name: `TestSingleSQLErrorRule2`,
  411. sql: `SELECT size * 5 FROM ldemo`,
  412. r: [][]map[string]interface{}{
  413. {{
  414. "rengine_field_0": float64(15),
  415. }},
  416. {{
  417. "error": "run Select error: invalid operation string(string) * int64(5)",
  418. }},
  419. {{
  420. "rengine_field_0": float64(15),
  421. }},
  422. {{
  423. "rengine_field_0": float64(10),
  424. }},
  425. {{}},
  426. },
  427. m: map[string]interface{}{
  428. "op_preprocessor_ldemo_0_exceptions_total": int64(0),
  429. "op_preprocessor_ldemo_0_process_latency_ms": int64(0),
  430. "op_preprocessor_ldemo_0_records_in_total": int64(5),
  431. "op_preprocessor_ldemo_0_records_out_total": int64(5),
  432. "op_project_0_exceptions_total": int64(1),
  433. "op_project_0_process_latency_ms": int64(0),
  434. "op_project_0_records_in_total": int64(5),
  435. "op_project_0_records_out_total": int64(4),
  436. "sink_mockSink_0_exceptions_total": int64(0),
  437. "sink_mockSink_0_records_in_total": int64(5),
  438. "sink_mockSink_0_records_out_total": int64(5),
  439. "source_ldemo_0_exceptions_total": int64(0),
  440. "source_ldemo_0_records_in_total": int64(5),
  441. "source_ldemo_0_records_out_total": int64(5),
  442. },
  443. },
  444. }
  445. handleStream(true, streamList, t)
  446. doRuleTest(t, tests, 0, &api.RuleOption{
  447. BufferLength: 100,
  448. })
  449. }
  450. func TestSingleSQLTemplate(t *testing.T) {
  451. //Reset
  452. streamList := []string{"demo"}
  453. handleStream(false, streamList, t)
  454. //Data setup
  455. var tests = []ruleTest{
  456. {
  457. name: `TestSingleSQLTemplateRule1`,
  458. sql: `SELECT * FROM demo`,
  459. r: []map[string]interface{}{
  460. {
  461. "c": "red",
  462. "wrapper": "w1",
  463. },
  464. {
  465. "c": "blue",
  466. "wrapper": "w1",
  467. },
  468. {
  469. "c": "blue",
  470. "wrapper": "w1",
  471. },
  472. {
  473. "c": "yellow",
  474. "wrapper": "w1",
  475. },
  476. {
  477. "c": "red",
  478. "wrapper": "w1",
  479. },
  480. },
  481. m: map[string]interface{}{
  482. "op_preprocessor_demo_0_exceptions_total": int64(0),
  483. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  484. "op_preprocessor_demo_0_records_in_total": int64(5),
  485. "op_preprocessor_demo_0_records_out_total": int64(5),
  486. "op_project_0_exceptions_total": int64(0),
  487. "op_project_0_process_latency_ms": int64(0),
  488. "op_project_0_records_in_total": int64(5),
  489. "op_project_0_records_out_total": int64(5),
  490. "sink_mockSink_0_exceptions_total": int64(0),
  491. "sink_mockSink_0_records_in_total": int64(5),
  492. "sink_mockSink_0_records_out_total": int64(5),
  493. "source_demo_0_exceptions_total": int64(0),
  494. "source_demo_0_records_in_total": int64(5),
  495. "source_demo_0_records_out_total": int64(5),
  496. },
  497. },
  498. }
  499. handleStream(true, streamList, t)
  500. doRuleTestBySinkProps(t, tests, 0, &api.RuleOption{
  501. BufferLength: 100,
  502. }, map[string]interface{}{
  503. "dataTemplate": `{"wrapper":"w1", "c":"{{.color}}"}`,
  504. "sendSingle": true,
  505. }, func(result [][]byte) interface{} {
  506. var maps []map[string]interface{}
  507. for _, v := range result {
  508. var mapRes map[string]interface{}
  509. err := json.Unmarshal(v, &mapRes)
  510. if err != nil {
  511. t.Errorf("Failed to parse the input into map")
  512. continue
  513. }
  514. maps = append(maps, mapRes)
  515. }
  516. return maps
  517. })
  518. }
  519. func TestNoneSingleSQLTemplate(t *testing.T) {
  520. //Reset
  521. streamList := []string{"demo"}
  522. handleStream(false, streamList, t)
  523. //Data setup
  524. var tests = []ruleTest{
  525. {
  526. name: `TestNoneSingleSQLTemplateRule1`,
  527. sql: `SELECT * FROM demo`,
  528. r: [][]byte{
  529. []byte("<div>results</div><ul><li>red - 3</li></ul>"),
  530. []byte("<div>results</div><ul><li>blue - 6</li></ul>"),
  531. []byte("<div>results</div><ul><li>blue - 2</li></ul>"),
  532. []byte("<div>results</div><ul><li>yellow - 4</li></ul>"),
  533. []byte("<div>results</div><ul><li>red - 1</li></ul>"),
  534. },
  535. m: map[string]interface{}{
  536. "op_preprocessor_demo_0_exceptions_total": int64(0),
  537. "op_preprocessor_demo_0_process_latency_ms": int64(0),
  538. "op_preprocessor_demo_0_records_in_total": int64(5),
  539. "op_preprocessor_demo_0_records_out_total": int64(5),
  540. "op_project_0_exceptions_total": int64(0),
  541. "op_project_0_process_latency_ms": int64(0),
  542. "op_project_0_records_in_total": int64(5),
  543. "op_project_0_records_out_total": int64(5),
  544. "sink_mockSink_0_exceptions_total": int64(0),
  545. "sink_mockSink_0_records_in_total": int64(5),
  546. "sink_mockSink_0_records_out_total": int64(5),
  547. "source_demo_0_exceptions_total": int64(0),
  548. "source_demo_0_records_in_total": int64(5),
  549. "source_demo_0_records_out_total": int64(5),
  550. },
  551. },
  552. }
  553. handleStream(true, streamList, t)
  554. doRuleTestBySinkProps(t, tests, 0, &api.RuleOption{
  555. BufferLength: 100,
  556. }, map[string]interface{}{
  557. "dataTemplate": `<div>results</div><ul>{{range .}}<li>{{.color}} - {{.size}}</li>{{end}}</ul>`,
  558. }, func(result [][]byte) interface{} {
  559. return result
  560. })
  561. }