boolean_value_test.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package ottl
  4. import (
  5. "context"
  6. "strings"
  7. "testing"
  8. "time"
  9. "github.com/stretchr/testify/assert"
  10. "github.com/stretchr/testify/require"
  11. "go.opentelemetry.io/collector/component/componenttest"
  12. "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl/ottltest"
  13. )
  14. // valueFor is a test helper to eliminate a lot of tedium in writing tests of Comparisons.
  15. func valueFor(x any) value {
  16. val := value{}
  17. switch v := x.(type) {
  18. case []byte:
  19. var b byteSlice = v
  20. val.Bytes = &b
  21. case string:
  22. switch {
  23. case v == "NAME":
  24. // if the string is NAME construct a path of "name".
  25. val.Literal = &mathExprLiteral{
  26. Path: &Path{
  27. Fields: []Field{
  28. {
  29. Name: "name",
  30. },
  31. },
  32. },
  33. }
  34. case strings.Contains(v, "ENUM"):
  35. // if the string contains ENUM construct an EnumSymbol from it.
  36. val.Enum = (*EnumSymbol)(ottltest.Strp(v))
  37. case v == "dur1" || v == "dur2":
  38. val.Literal = &mathExprLiteral{
  39. Path: &Path{
  40. Fields: []Field{
  41. {
  42. Name: v,
  43. },
  44. },
  45. },
  46. }
  47. case v == "time1" || v == "time2":
  48. val.Literal = &mathExprLiteral{
  49. Path: &Path{
  50. Fields: []Field{
  51. {
  52. Name: v,
  53. },
  54. },
  55. },
  56. }
  57. default:
  58. val.String = ottltest.Strp(v)
  59. }
  60. case float64:
  61. val.Literal = &mathExprLiteral{Float: ottltest.Floatp(v)}
  62. case *float64:
  63. val.Literal = &mathExprLiteral{Float: v}
  64. case int:
  65. val.Literal = &mathExprLiteral{Int: ottltest.Intp(int64(v))}
  66. case *int64:
  67. val.Literal = &mathExprLiteral{Int: v}
  68. case bool:
  69. val.Bool = booleanp(boolean(v))
  70. case nil:
  71. var n isNil = true
  72. val.IsNil = &n
  73. default:
  74. panic("test error!")
  75. }
  76. return val
  77. }
  78. // comparison is a test helper that constructs a comparison object using valueFor
  79. func comparisonHelper(left any, right any, op string) *comparison {
  80. return &comparison{
  81. Left: valueFor(left),
  82. Right: valueFor(right),
  83. Op: compareOpTable[op],
  84. }
  85. }
  86. func Test_newComparisonEvaluator(t *testing.T) {
  87. p, _ := NewParser(
  88. defaultFunctionsForTests(),
  89. testParsePath,
  90. componenttest.NewNopTelemetrySettings(),
  91. WithEnumParser[any](testParseEnum),
  92. )
  93. twelveNanoseconds, err := time.ParseDuration("12ns")
  94. require.NoError(t, err)
  95. oneMillisecond, err := time.ParseDuration("1ms")
  96. require.NoError(t, err)
  97. threeSeconds, err := time.ParseDuration("3s")
  98. require.NoError(t, err)
  99. twentyTwoMinutes, err := time.ParseDuration("22m")
  100. require.NoError(t, err)
  101. oneHundredThirtyFiveHours, err := time.ParseDuration("135h")
  102. require.NoError(t, err)
  103. JanFirst2023 := time.Date(2023, 1, 1, 0, 0, 0, 0, time.Local)
  104. var tests = []struct {
  105. name string
  106. l any
  107. r any
  108. op string
  109. item any
  110. want bool
  111. }{
  112. {name: "literals match", l: "hello", r: "hello", op: "==", want: true},
  113. {name: "literals don't match", l: "hello", r: "goodbye", op: "!=", want: true},
  114. {name: "path expression matches", l: "NAME", r: "bear", op: "==", item: "bear", want: true},
  115. {name: "path expression not matches", l: "NAME", r: "cat", op: "!=", item: "bear", want: true},
  116. {name: "compare Enum to int", l: "TEST_ENUM", r: 0, op: "==", want: true},
  117. {name: "compare int to Enum", l: 2, r: "TEST_ENUM_TWO", op: "==", want: true},
  118. {name: "2 > Enum 0", l: 2, r: "TEST_ENUM", op: ">", want: true},
  119. {name: "not 2 < Enum 0", l: 2, r: "TEST_ENUM", op: "<"},
  120. {name: "not 6 == 3.14", l: 6, r: 3.14, op: "=="},
  121. {name: "6 != 3.14", l: 6, r: 3.14, op: "!=", want: true},
  122. {name: "6 > 3.14", l: 6, r: 3.14, op: ">", want: true},
  123. {name: "6 >= 3.14", l: 6, r: 3.14, op: ">=", want: true},
  124. {name: "not 6 < 3.14", l: 6, r: 3.14, op: "<"},
  125. {name: "not 6 <= 3.14", l: 6, r: 3.14, op: "<="},
  126. {name: "'foo' > 'bar'", l: "foo", r: "bar", op: ">", want: true},
  127. {name: "'foo' > bear", l: "foo", r: "NAME", op: ">", item: "bear", want: true},
  128. {name: "true > false", l: true, r: false, op: ">", want: true},
  129. {name: "not true > 0", l: true, r: 0, op: ">"},
  130. {name: "not 'true' == true", l: "true", r: true, op: "=="},
  131. {name: "[]byte('a') < []byte('b')", l: []byte("a"), r: []byte("b"), op: "<", want: true},
  132. {name: "nil == nil", op: "==", want: true},
  133. {name: "nil == []byte(nil)", r: []byte(nil), op: "==", want: true},
  134. {name: "compare equal durations", l: "dur1", r: "dur2", op: "==", want: true, item: map[string]time.Duration{"dur1": oneMillisecond, "dur2": oneMillisecond}},
  135. {name: "compare unequal durations", l: "dur1", r: "dur2", op: "==", want: false, item: map[string]time.Duration{"dur1": oneMillisecond, "dur2": threeSeconds}},
  136. {name: "compare not equal durations", l: "dur1", r: "dur2", op: "!=", want: true, item: map[string]time.Duration{"dur1": oneMillisecond, "dur2": threeSeconds}},
  137. {name: "compare not equal durations", l: "dur1", r: "dur2", op: "!=", want: false, item: map[string]time.Duration{"dur1": threeSeconds, "dur2": threeSeconds}},
  138. {name: "compare less than durations", l: "dur1", r: "dur2", op: "<", want: true, item: map[string]time.Duration{"dur1": oneMillisecond, "dur2": twentyTwoMinutes}},
  139. {name: "compare not less than durations", l: "dur1", r: "dur2", op: "<", want: false, item: map[string]time.Duration{"dur1": twentyTwoMinutes, "dur2": twentyTwoMinutes}},
  140. {name: "compare less than equal to durations", l: "dur1", r: "dur2", op: "<=", want: true, item: map[string]time.Duration{"dur1": threeSeconds, "dur2": threeSeconds}},
  141. {name: "compare not less than equal to durations", l: "dur1", r: "dur2", op: "<=", want: false, item: map[string]time.Duration{"dur1": oneHundredThirtyFiveHours, "dur2": threeSeconds}},
  142. {name: "compare greater than durations", l: "dur1", r: "dur2", op: ">", want: true, item: map[string]time.Duration{"dur1": oneMillisecond, "dur2": twelveNanoseconds}},
  143. {name: "compare not greater than durations", l: "dur1", r: "dur2", op: ">", want: false, item: map[string]time.Duration{"dur1": twelveNanoseconds, "dur2": twentyTwoMinutes}},
  144. {name: "compare greater than equal to durations", l: "dur1", r: "dur2", op: ">=", want: true, item: map[string]time.Duration{"dur1": oneHundredThirtyFiveHours, "dur2": threeSeconds}},
  145. {name: "compare not greater than equal to durations", l: "dur1", r: "dur2", op: ">=", want: false, item: map[string]time.Duration{"dur1": oneMillisecond, "dur2": threeSeconds}},
  146. {name: "compare equal times", l: "time1", r: "time2", op: "==", want: true, item: map[string]time.Time{"time1": JanFirst2023, "time2": JanFirst2023}},
  147. {name: "compare unequal times", l: "time1", r: "time2", op: "==", want: false, item: map[string]time.Time{"time1": JanFirst2023, "time2": time.Date(2023, 1, 2, 0, 0, 0, 0, time.Local)}},
  148. {name: "compare for not equal times", l: "time1", r: "time2", op: "!=", want: true, item: map[string]time.Time{"time1": JanFirst2023, "time2": time.Date(2002, 11, 2, 01, 01, 01, 01, time.Local)}},
  149. {name: "compare for equal times using not equal", l: "time1", r: "time2", op: "!=", want: false, item: map[string]time.Time{"time1": time.Date(2002, 11, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2002, 11, 2, 01, 01, 01, 01, time.Local)}},
  150. {name: "compare less than times", l: "time1", r: "time2", op: "<", want: true, item: map[string]time.Time{"time1": JanFirst2023, "time2": time.Date(2023, 5, 2, 01, 01, 01, 01, time.Local)}},
  151. {name: "compare not less than times", l: "time1", r: "time2", op: "<", want: false, item: map[string]time.Time{"time1": time.Date(2023, 6, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2023, 5, 2, 01, 01, 01, 01, time.Local)}},
  152. {name: "compare less than equal to times", l: "time1", r: "time2", op: "<=", want: true, item: map[string]time.Time{"time1": time.Date(2003, 5, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2003, 5, 2, 01, 01, 01, 01, time.Local)}},
  153. {name: "compare not less than equal to times", l: "time1", r: "time2", op: "<=", want: false, item: map[string]time.Time{"time1": time.Date(2002, 5, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(1999, 5, 2, 01, 01, 01, 01, time.Local)}},
  154. {name: "compare not greater than equal to w/ times", l: "time1", r: "time2", op: ">=", want: false, item: map[string]time.Time{"time1": time.Date(2002, 5, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2003, 5, 2, 01, 01, 01, 01, time.Local)}},
  155. {name: "compare greater than equal to w/ times", l: "time1", r: "time2", op: ">=", want: true, item: map[string]time.Time{"time1": time.Date(2022, 5, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2003, 5, 2, 01, 01, 01, 01, time.Local)}},
  156. {name: "compare greater than w/ times", l: "time1", r: "time2", op: ">", want: true, item: map[string]time.Time{"time1": time.Date(2022, 5, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2003, 5, 2, 01, 01, 01, 01, time.Local)}},
  157. {name: "compare not greater than w/ times", l: "time1", r: "time2", op: ">", want: false, item: map[string]time.Time{"time1": time.Date(2002, 3, 2, 01, 01, 01, 01, time.Local), "time2": time.Date(2003, 5, 2, 01, 01, 01, 01, time.Local)}},
  158. }
  159. for _, tt := range tests {
  160. t.Run(tt.name, func(t *testing.T) {
  161. comp := comparisonHelper(tt.l, tt.r, tt.op)
  162. evaluator, err := p.newComparisonEvaluator(comp)
  163. assert.NoError(t, err)
  164. result, err := evaluator.Eval(context.Background(), tt.item)
  165. assert.NoError(t, err)
  166. assert.Equal(t, tt.want, result)
  167. })
  168. }
  169. }
  170. func Test_newConditionEvaluator_invalid(t *testing.T) {
  171. p, _ := NewParser(
  172. defaultFunctionsForTests(),
  173. testParsePath,
  174. componenttest.NewNopTelemetrySettings(),
  175. WithEnumParser[any](testParseEnum),
  176. )
  177. tests := []struct {
  178. name string
  179. comparison *comparison
  180. }{
  181. {
  182. name: "unknown Path",
  183. comparison: &comparison{
  184. Left: value{
  185. Enum: (*EnumSymbol)(ottltest.Strp("SYMBOL_NOT_FOUND")),
  186. },
  187. Op: EQ,
  188. Right: value{
  189. String: ottltest.Strp("trash"),
  190. },
  191. },
  192. },
  193. }
  194. for _, tt := range tests {
  195. t.Run(tt.name, func(t *testing.T) {
  196. _, err := p.newComparisonEvaluator(tt.comparison)
  197. assert.Error(t, err)
  198. })
  199. }
  200. }
  201. func True() (ExprFunc[any], error) {
  202. return func(ctx context.Context, tCtx any) (any, error) {
  203. return true, nil
  204. }, nil
  205. }
  206. func False() (ExprFunc[any], error) {
  207. return func(ctx context.Context, tCtx any) (any, error) {
  208. return false, nil
  209. }, nil
  210. }
  211. func Test_newBooleanExpressionEvaluator(t *testing.T) {
  212. functions := defaultFunctionsForTests()
  213. functions["True"] = createFactory("True", &struct{}{}, True)
  214. functions["False"] = createFactory("False", &struct{}{}, False)
  215. p, _ := NewParser(
  216. functions,
  217. testParsePath,
  218. componenttest.NewNopTelemetrySettings(),
  219. WithEnumParser[any](testParseEnum),
  220. )
  221. tests := []struct {
  222. name string
  223. want bool
  224. expr *booleanExpression
  225. }{
  226. {"a", false,
  227. &booleanExpression{
  228. Left: &term{
  229. Left: &booleanValue{
  230. ConstExpr: &constExpr{
  231. Boolean: booleanp(true),
  232. },
  233. },
  234. Right: []*opAndBooleanValue{
  235. {
  236. Operator: "and",
  237. Value: &booleanValue{
  238. ConstExpr: &constExpr{
  239. Boolean: booleanp(false),
  240. },
  241. },
  242. },
  243. },
  244. },
  245. },
  246. },
  247. {"b", true,
  248. &booleanExpression{
  249. Left: &term{
  250. Left: &booleanValue{
  251. ConstExpr: &constExpr{
  252. Boolean: booleanp(true),
  253. },
  254. },
  255. Right: []*opAndBooleanValue{
  256. {
  257. Operator: "and",
  258. Value: &booleanValue{
  259. ConstExpr: &constExpr{
  260. Boolean: booleanp(true),
  261. },
  262. },
  263. },
  264. },
  265. },
  266. },
  267. },
  268. {"c", false,
  269. &booleanExpression{
  270. Left: &term{
  271. Left: &booleanValue{
  272. ConstExpr: &constExpr{
  273. Boolean: booleanp(true),
  274. },
  275. },
  276. Right: []*opAndBooleanValue{
  277. {
  278. Operator: "and",
  279. Value: &booleanValue{
  280. ConstExpr: &constExpr{
  281. Boolean: booleanp(true),
  282. },
  283. },
  284. },
  285. {
  286. Operator: "and",
  287. Value: &booleanValue{
  288. ConstExpr: &constExpr{
  289. Boolean: booleanp(false),
  290. },
  291. },
  292. },
  293. },
  294. },
  295. },
  296. },
  297. {"d", true,
  298. &booleanExpression{
  299. Left: &term{
  300. Left: &booleanValue{
  301. ConstExpr: &constExpr{
  302. Boolean: booleanp(true),
  303. },
  304. },
  305. },
  306. Right: []*opOrTerm{
  307. {
  308. Operator: "or",
  309. Term: &term{
  310. Left: &booleanValue{
  311. ConstExpr: &constExpr{
  312. Boolean: booleanp(false),
  313. },
  314. },
  315. },
  316. },
  317. },
  318. },
  319. },
  320. {"e", true,
  321. &booleanExpression{
  322. Left: &term{
  323. Left: &booleanValue{
  324. ConstExpr: &constExpr{
  325. Boolean: booleanp(false),
  326. },
  327. },
  328. },
  329. Right: []*opOrTerm{
  330. {
  331. Operator: "or",
  332. Term: &term{
  333. Left: &booleanValue{
  334. ConstExpr: &constExpr{
  335. Boolean: booleanp(true),
  336. },
  337. },
  338. },
  339. },
  340. },
  341. },
  342. },
  343. {"f", false,
  344. &booleanExpression{
  345. Left: &term{
  346. Left: &booleanValue{
  347. ConstExpr: &constExpr{
  348. Boolean: booleanp(false),
  349. },
  350. },
  351. },
  352. Right: []*opOrTerm{
  353. {
  354. Operator: "or",
  355. Term: &term{
  356. Left: &booleanValue{
  357. ConstExpr: &constExpr{
  358. Boolean: booleanp(false),
  359. },
  360. },
  361. },
  362. },
  363. },
  364. },
  365. },
  366. {"g", true,
  367. &booleanExpression{
  368. Left: &term{
  369. Left: &booleanValue{
  370. ConstExpr: &constExpr{
  371. Boolean: booleanp(false),
  372. },
  373. },
  374. Right: []*opAndBooleanValue{
  375. {
  376. Operator: "and",
  377. Value: &booleanValue{
  378. ConstExpr: &constExpr{
  379. Boolean: booleanp(false),
  380. },
  381. },
  382. },
  383. },
  384. },
  385. Right: []*opOrTerm{
  386. {
  387. Operator: "or",
  388. Term: &term{
  389. Left: &booleanValue{
  390. ConstExpr: &constExpr{
  391. Boolean: booleanp(true),
  392. },
  393. },
  394. },
  395. },
  396. },
  397. },
  398. },
  399. {"h", true,
  400. &booleanExpression{
  401. Left: &term{
  402. Left: &booleanValue{
  403. ConstExpr: &constExpr{
  404. Boolean: booleanp(true),
  405. },
  406. },
  407. Right: []*opAndBooleanValue{
  408. {
  409. Operator: "and",
  410. Value: &booleanValue{
  411. SubExpr: &booleanExpression{
  412. Left: &term{
  413. Left: &booleanValue{
  414. ConstExpr: &constExpr{
  415. Boolean: booleanp(true),
  416. },
  417. },
  418. },
  419. Right: []*opOrTerm{
  420. {
  421. Operator: "or",
  422. Term: &term{
  423. Left: &booleanValue{
  424. ConstExpr: &constExpr{
  425. Boolean: booleanp(false),
  426. },
  427. },
  428. },
  429. },
  430. },
  431. },
  432. },
  433. },
  434. },
  435. },
  436. },
  437. },
  438. {"i", true,
  439. &booleanExpression{
  440. Left: &term{
  441. Left: &booleanValue{
  442. Negation: ottltest.Strp("not"),
  443. ConstExpr: &constExpr{
  444. Boolean: booleanp(false),
  445. },
  446. },
  447. },
  448. },
  449. },
  450. {"j", false,
  451. &booleanExpression{
  452. Left: &term{
  453. Left: &booleanValue{
  454. Negation: ottltest.Strp("not"),
  455. ConstExpr: &constExpr{
  456. Boolean: booleanp(true),
  457. },
  458. },
  459. },
  460. },
  461. },
  462. {"k", true,
  463. &booleanExpression{
  464. Left: &term{
  465. Left: &booleanValue{
  466. Negation: ottltest.Strp("not"),
  467. Comparison: &comparison{
  468. Left: value{
  469. String: ottltest.Strp("test"),
  470. },
  471. Op: EQ,
  472. Right: value{
  473. String: ottltest.Strp("not test"),
  474. },
  475. },
  476. },
  477. },
  478. },
  479. },
  480. {"l", false,
  481. &booleanExpression{
  482. Left: &term{
  483. Left: &booleanValue{
  484. ConstExpr: &constExpr{
  485. Boolean: booleanp(true),
  486. },
  487. },
  488. Right: []*opAndBooleanValue{
  489. {
  490. Operator: "and",
  491. Value: &booleanValue{
  492. Negation: ottltest.Strp("not"),
  493. SubExpr: &booleanExpression{
  494. Left: &term{
  495. Left: &booleanValue{
  496. ConstExpr: &constExpr{
  497. Boolean: booleanp(true),
  498. },
  499. },
  500. },
  501. Right: []*opOrTerm{
  502. {
  503. Operator: "or",
  504. Term: &term{
  505. Left: &booleanValue{
  506. ConstExpr: &constExpr{
  507. Boolean: booleanp(false),
  508. },
  509. },
  510. },
  511. },
  512. },
  513. },
  514. },
  515. },
  516. },
  517. },
  518. },
  519. },
  520. {"m", false,
  521. &booleanExpression{
  522. Left: &term{
  523. Left: &booleanValue{
  524. Negation: ottltest.Strp("not"),
  525. ConstExpr: &constExpr{
  526. Boolean: booleanp(true),
  527. },
  528. },
  529. Right: []*opAndBooleanValue{
  530. {
  531. Operator: "and",
  532. Value: &booleanValue{
  533. Negation: ottltest.Strp("not"),
  534. ConstExpr: &constExpr{
  535. Boolean: booleanp(false),
  536. },
  537. },
  538. },
  539. },
  540. },
  541. Right: []*opOrTerm{
  542. {
  543. Operator: "or",
  544. Term: &term{
  545. Left: &booleanValue{
  546. Negation: ottltest.Strp("not"),
  547. ConstExpr: &constExpr{
  548. Boolean: booleanp(true),
  549. },
  550. },
  551. },
  552. },
  553. },
  554. },
  555. },
  556. {"n", true,
  557. &booleanExpression{
  558. Left: &term{
  559. Left: &booleanValue{
  560. ConstExpr: &constExpr{
  561. Converter: &converter{
  562. Function: "True",
  563. },
  564. },
  565. },
  566. },
  567. },
  568. },
  569. {"o", false,
  570. &booleanExpression{
  571. Left: &term{
  572. Left: &booleanValue{
  573. ConstExpr: &constExpr{
  574. Converter: &converter{
  575. Function: "False",
  576. },
  577. },
  578. },
  579. },
  580. },
  581. },
  582. }
  583. for _, tt := range tests {
  584. t.Run(tt.name, func(t *testing.T) {
  585. evaluator, err := p.newBoolExpr(tt.expr)
  586. assert.NoError(t, err)
  587. result, err := evaluator.Eval(context.Background(), nil)
  588. assert.NoError(t, err)
  589. assert.Equal(t, tt.want, result)
  590. })
  591. }
  592. }
  593. func Test_newBooleanExpressionEvaluator_invalid(t *testing.T) {
  594. functions := map[string]Factory[any]{"Hello": createFactory("Hello", &struct{}{}, hello)}
  595. p, _ := NewParser(
  596. functions,
  597. testParsePath,
  598. componenttest.NewNopTelemetrySettings(),
  599. WithEnumParser[any](testParseEnum),
  600. )
  601. tests := []struct {
  602. name string
  603. expr *booleanExpression
  604. }{
  605. {
  606. name: "Converter doesn't return bool",
  607. expr: &booleanExpression{
  608. Left: &term{
  609. Left: &booleanValue{
  610. ConstExpr: &constExpr{
  611. Converter: &converter{
  612. Function: "Hello",
  613. },
  614. },
  615. },
  616. },
  617. },
  618. },
  619. }
  620. for _, tt := range tests {
  621. t.Run(tt.name, func(t *testing.T) {
  622. evaluator, err := p.newBoolExpr(tt.expr)
  623. assert.NoError(t, err)
  624. _, err = evaluator.Eval(context.Background(), nil)
  625. assert.Error(t, err)
  626. })
  627. }
  628. }