allocationfilter_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967
  1. package kubecost
  2. import (
  3. "reflect"
  4. "testing"
  5. )
  6. func Test_AllocationFilterCondition_Matches(t *testing.T) {
  7. cases := []struct {
  8. name string
  9. a *Allocation
  10. filter AllocationFilter
  11. expected bool
  12. }{
  13. {
  14. name: "ClusterID Equals -> true",
  15. a: &Allocation{
  16. Properties: &AllocationProperties{
  17. Cluster: "cluster-one",
  18. },
  19. },
  20. filter: AllocationFilterCondition{
  21. Field: FilterClusterID,
  22. Op: FilterEquals,
  23. Value: "cluster-one",
  24. },
  25. expected: true,
  26. },
  27. {
  28. name: "ClusterID StartsWith -> true",
  29. a: &Allocation{
  30. Properties: &AllocationProperties{
  31. Cluster: "cluster-one",
  32. },
  33. },
  34. filter: AllocationFilterCondition{
  35. Field: FilterClusterID,
  36. Op: FilterStartsWith,
  37. Value: "cluster",
  38. },
  39. expected: true,
  40. },
  41. {
  42. name: "ClusterID StartsWith -> false",
  43. a: &Allocation{
  44. Properties: &AllocationProperties{
  45. Cluster: "k8s-one",
  46. },
  47. },
  48. filter: AllocationFilterCondition{
  49. Field: FilterClusterID,
  50. Op: FilterStartsWith,
  51. Value: "cluster",
  52. },
  53. expected: false,
  54. },
  55. {
  56. name: "Node Equals -> true",
  57. a: &Allocation{
  58. Properties: &AllocationProperties{
  59. Node: "node123",
  60. },
  61. },
  62. filter: AllocationFilterCondition{
  63. Field: FilterNode,
  64. Op: FilterEquals,
  65. Value: "node123",
  66. },
  67. expected: true,
  68. },
  69. {
  70. name: "Namespace NotEquals -> false",
  71. a: &Allocation{
  72. Properties: &AllocationProperties{
  73. Namespace: "kube-system",
  74. },
  75. },
  76. filter: AllocationFilterCondition{
  77. Field: FilterNamespace,
  78. Op: FilterNotEquals,
  79. Value: "kube-system",
  80. },
  81. expected: false,
  82. },
  83. {
  84. name: "Namespace NotEquals Unallocated -> true",
  85. a: &Allocation{
  86. Properties: &AllocationProperties{
  87. Namespace: "kube-system",
  88. },
  89. },
  90. filter: AllocationFilterCondition{
  91. Field: FilterNamespace,
  92. Op: FilterNotEquals,
  93. Value: UnallocatedSuffix,
  94. },
  95. expected: true,
  96. },
  97. {
  98. name: "Namespace NotEquals Unallocated -> false",
  99. a: &Allocation{
  100. Properties: &AllocationProperties{
  101. Namespace: "",
  102. },
  103. },
  104. filter: AllocationFilterCondition{
  105. Field: FilterNamespace,
  106. Op: FilterNotEquals,
  107. Value: UnallocatedSuffix,
  108. },
  109. expected: false,
  110. },
  111. {
  112. name: "Namespace Equals Unallocated -> true",
  113. a: &Allocation{
  114. Properties: &AllocationProperties{
  115. Namespace: "",
  116. },
  117. },
  118. filter: AllocationFilterCondition{
  119. Field: FilterNamespace,
  120. Op: FilterEquals,
  121. Value: UnallocatedSuffix,
  122. },
  123. expected: true,
  124. },
  125. {
  126. name: "ControllerKind Equals -> true",
  127. a: &Allocation{
  128. Properties: &AllocationProperties{
  129. ControllerKind: "deployment", // We generally store controller kinds as all lowercase
  130. },
  131. },
  132. filter: AllocationFilterCondition{
  133. Field: FilterControllerKind,
  134. Op: FilterEquals,
  135. Value: "deployment",
  136. },
  137. expected: true,
  138. },
  139. {
  140. name: "ControllerName Equals -> true",
  141. a: &Allocation{
  142. Properties: &AllocationProperties{
  143. Controller: "kc-cost-analyzer",
  144. },
  145. },
  146. filter: AllocationFilterCondition{
  147. Field: FilterControllerName,
  148. Op: FilterEquals,
  149. Value: "kc-cost-analyzer",
  150. },
  151. expected: true,
  152. },
  153. {
  154. name: "Pod (with UID) Equals -> true",
  155. a: &Allocation{
  156. Properties: &AllocationProperties{
  157. Pod: "pod-123 UID-ABC",
  158. },
  159. },
  160. filter: AllocationFilterCondition{
  161. Field: FilterPod,
  162. Op: FilterEquals,
  163. Value: "pod-123 UID-ABC",
  164. },
  165. expected: true,
  166. },
  167. {
  168. name: "Container Equals -> true",
  169. a: &Allocation{
  170. Properties: &AllocationProperties{
  171. Container: "cost-model",
  172. },
  173. },
  174. filter: AllocationFilterCondition{
  175. Field: FilterContainer,
  176. Op: FilterEquals,
  177. Value: "cost-model",
  178. },
  179. expected: true,
  180. },
  181. {
  182. name: `label[app]="foo" -> true`,
  183. a: &Allocation{
  184. Properties: &AllocationProperties{
  185. Labels: map[string]string{
  186. "app": "foo",
  187. },
  188. },
  189. },
  190. filter: AllocationFilterCondition{
  191. Field: FilterLabel,
  192. Op: FilterEquals,
  193. Key: "app",
  194. Value: "foo",
  195. },
  196. expected: true,
  197. },
  198. {
  199. name: `label[app]="foo" -> different value -> false`,
  200. a: &Allocation{
  201. Properties: &AllocationProperties{
  202. Labels: map[string]string{
  203. "app": "bar",
  204. },
  205. },
  206. },
  207. filter: AllocationFilterCondition{
  208. Field: FilterLabel,
  209. Op: FilterEquals,
  210. Key: "app",
  211. Value: "foo",
  212. },
  213. expected: false,
  214. },
  215. {
  216. name: `label[app]="foo" -> label missing -> false`,
  217. a: &Allocation{
  218. Properties: &AllocationProperties{
  219. Labels: map[string]string{
  220. "someotherlabel": "someothervalue",
  221. },
  222. },
  223. },
  224. filter: AllocationFilterCondition{
  225. Field: FilterLabel,
  226. Op: FilterEquals,
  227. Key: "app",
  228. Value: "foo",
  229. },
  230. expected: false,
  231. },
  232. {
  233. name: `label[app]=Unallocated -> label missing -> true`,
  234. a: &Allocation{
  235. Properties: &AllocationProperties{
  236. Labels: map[string]string{
  237. "someotherlabel": "someothervalue",
  238. },
  239. },
  240. },
  241. filter: AllocationFilterCondition{
  242. Field: FilterLabel,
  243. Op: FilterEquals,
  244. Key: "app",
  245. Value: UnallocatedSuffix,
  246. },
  247. expected: true,
  248. },
  249. {
  250. name: `label[app]=Unallocated -> label present -> false`,
  251. a: &Allocation{
  252. Properties: &AllocationProperties{
  253. Labels: map[string]string{
  254. "app": "test",
  255. },
  256. },
  257. },
  258. filter: AllocationFilterCondition{
  259. Field: FilterLabel,
  260. Op: FilterEquals,
  261. Key: "app",
  262. Value: UnallocatedSuffix,
  263. },
  264. expected: false,
  265. },
  266. {
  267. name: `label[app]!=Unallocated -> label missing -> false`,
  268. a: &Allocation{
  269. Properties: &AllocationProperties{
  270. Labels: map[string]string{
  271. "someotherlabel": "someothervalue",
  272. },
  273. },
  274. },
  275. filter: AllocationFilterCondition{
  276. Field: FilterLabel,
  277. Op: FilterNotEquals,
  278. Key: "app",
  279. Value: UnallocatedSuffix,
  280. },
  281. expected: false,
  282. },
  283. {
  284. name: `label[app]!=Unallocated -> label present -> true`,
  285. a: &Allocation{
  286. Properties: &AllocationProperties{
  287. Labels: map[string]string{
  288. "app": "test",
  289. },
  290. },
  291. },
  292. filter: AllocationFilterCondition{
  293. Field: FilterLabel,
  294. Op: FilterNotEquals,
  295. Key: "app",
  296. Value: UnallocatedSuffix,
  297. },
  298. expected: true,
  299. },
  300. {
  301. name: `label[app]!="foo" -> label missing -> true`,
  302. a: &Allocation{
  303. Properties: &AllocationProperties{
  304. Labels: map[string]string{
  305. "someotherlabel": "someothervalue",
  306. },
  307. },
  308. },
  309. filter: AllocationFilterCondition{
  310. Field: FilterLabel,
  311. Op: FilterNotEquals,
  312. Key: "app",
  313. Value: "foo",
  314. },
  315. expected: true,
  316. },
  317. {
  318. name: `annotation[prom_modified_name]="testing123" -> true`,
  319. a: &Allocation{
  320. Properties: &AllocationProperties{
  321. Annotations: map[string]string{
  322. "prom_modified_name": "testing123",
  323. },
  324. },
  325. },
  326. filter: AllocationFilterCondition{
  327. Field: FilterAnnotation,
  328. Op: FilterEquals,
  329. Key: "prom_modified_name",
  330. Value: "testing123",
  331. },
  332. expected: true,
  333. },
  334. {
  335. name: `annotation[app]="foo" -> different value -> false`,
  336. a: &Allocation{
  337. Properties: &AllocationProperties{
  338. Annotations: map[string]string{
  339. "app": "bar",
  340. },
  341. },
  342. },
  343. filter: AllocationFilterCondition{
  344. Field: FilterAnnotation,
  345. Op: FilterEquals,
  346. Key: "app",
  347. Value: "foo",
  348. },
  349. expected: false,
  350. },
  351. {
  352. name: `annotation[app]="foo" -> annotation missing -> false`,
  353. a: &Allocation{
  354. Properties: &AllocationProperties{
  355. Annotations: map[string]string{
  356. "someotherannotation": "someothervalue",
  357. },
  358. },
  359. },
  360. filter: AllocationFilterCondition{
  361. Field: FilterAnnotation,
  362. Op: FilterEquals,
  363. Key: "app",
  364. Value: "foo",
  365. },
  366. expected: false,
  367. },
  368. {
  369. name: `annotation[app]!="foo" -> annotation missing -> true`,
  370. a: &Allocation{
  371. Properties: &AllocationProperties{
  372. Annotations: map[string]string{
  373. "someotherannotation": "someothervalue",
  374. },
  375. },
  376. },
  377. filter: AllocationFilterCondition{
  378. Field: FilterAnnotation,
  379. Op: FilterNotEquals,
  380. Key: "app",
  381. Value: "foo",
  382. },
  383. expected: true,
  384. },
  385. {
  386. name: `namespace unallocated -> true`,
  387. a: &Allocation{
  388. Properties: &AllocationProperties{
  389. Namespace: "",
  390. },
  391. },
  392. filter: AllocationFilterCondition{
  393. Field: FilterNamespace,
  394. Op: FilterEquals,
  395. Value: UnallocatedSuffix,
  396. },
  397. expected: true,
  398. },
  399. {
  400. name: `services contains -> true`,
  401. a: &Allocation{
  402. Properties: &AllocationProperties{
  403. Services: []string{"serv1", "serv2"},
  404. },
  405. },
  406. filter: AllocationFilterCondition{
  407. Field: FilterServices,
  408. Op: FilterContains,
  409. Value: "serv2",
  410. },
  411. expected: true,
  412. },
  413. {
  414. name: `services contains -> false`,
  415. a: &Allocation{
  416. Properties: &AllocationProperties{
  417. Services: []string{"serv1", "serv2"},
  418. },
  419. },
  420. filter: AllocationFilterCondition{
  421. Field: FilterServices,
  422. Op: FilterContains,
  423. Value: "serv3",
  424. },
  425. expected: false,
  426. },
  427. {
  428. name: `services notcontains -> true`,
  429. a: &Allocation{
  430. Properties: &AllocationProperties{
  431. Services: []string{"serv1", "serv2"},
  432. },
  433. },
  434. filter: AllocationFilterCondition{
  435. Field: FilterServices,
  436. Op: FilterNotContains,
  437. Value: "serv3",
  438. },
  439. expected: true,
  440. },
  441. {
  442. name: `services notcontains -> false`,
  443. a: &Allocation{
  444. Properties: &AllocationProperties{
  445. Services: []string{"serv1", "serv2"},
  446. },
  447. },
  448. filter: AllocationFilterCondition{
  449. Field: FilterServices,
  450. Op: FilterNotContains,
  451. Value: "serv2",
  452. },
  453. expected: false,
  454. },
  455. {
  456. name: `services notcontains unallocated -> true`,
  457. a: &Allocation{
  458. Properties: &AllocationProperties{
  459. Services: []string{"serv1", "serv2"},
  460. },
  461. },
  462. filter: AllocationFilterCondition{
  463. Field: FilterServices,
  464. Op: FilterNotContains,
  465. Value: UnallocatedSuffix,
  466. },
  467. expected: true,
  468. },
  469. {
  470. name: `services notcontains unallocated -> false`,
  471. a: &Allocation{
  472. Properties: &AllocationProperties{
  473. Services: []string{},
  474. },
  475. },
  476. filter: AllocationFilterCondition{
  477. Field: FilterServices,
  478. Op: FilterNotContains,
  479. Value: UnallocatedSuffix,
  480. },
  481. expected: false,
  482. },
  483. {
  484. name: `services containsprefix -> true`,
  485. a: &Allocation{
  486. Properties: &AllocationProperties{
  487. Services: []string{"serv1", "serv2"},
  488. },
  489. },
  490. filter: AllocationFilterCondition{
  491. Field: FilterServices,
  492. Op: FilterContainsPrefix,
  493. Value: "serv",
  494. },
  495. expected: true,
  496. },
  497. {
  498. name: `services containsprefix -> false`,
  499. a: &Allocation{
  500. Properties: &AllocationProperties{
  501. Services: []string{"foo", "bar"},
  502. },
  503. },
  504. filter: AllocationFilterCondition{
  505. Field: FilterServices,
  506. Op: FilterContainsPrefix,
  507. Value: "serv",
  508. },
  509. expected: false,
  510. },
  511. {
  512. name: `services contains unallocated -> false`,
  513. a: &Allocation{
  514. Properties: &AllocationProperties{
  515. Services: []string{"serv1", "serv2"},
  516. },
  517. },
  518. filter: AllocationFilterCondition{
  519. Field: FilterServices,
  520. Op: FilterContains,
  521. Value: UnallocatedSuffix,
  522. },
  523. expected: false,
  524. },
  525. {
  526. name: `services contains unallocated -> true`,
  527. a: &Allocation{
  528. Properties: &AllocationProperties{
  529. Services: []string{},
  530. },
  531. },
  532. filter: AllocationFilterCondition{
  533. Field: FilterServices,
  534. Op: FilterContains,
  535. Value: UnallocatedSuffix,
  536. },
  537. expected: true,
  538. },
  539. }
  540. for _, c := range cases {
  541. result := c.filter.Matches(c.a)
  542. if result != c.expected {
  543. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  544. }
  545. }
  546. }
  547. func Test_AllocationFilterAnd_Matches(t *testing.T) {
  548. cases := []struct {
  549. name string
  550. a *Allocation
  551. filter AllocationFilter
  552. expected bool
  553. }{
  554. {
  555. name: `label[app]="foo" and namespace="kubecost" -> both true`,
  556. a: &Allocation{
  557. Properties: &AllocationProperties{
  558. Namespace: "kubecost",
  559. Labels: map[string]string{
  560. "app": "foo",
  561. },
  562. },
  563. },
  564. filter: AllocationFilterAnd{[]AllocationFilter{
  565. AllocationFilterCondition{
  566. Field: FilterLabel,
  567. Op: FilterEquals,
  568. Key: "app",
  569. Value: "foo",
  570. },
  571. AllocationFilterCondition{
  572. Field: FilterNamespace,
  573. Op: FilterEquals,
  574. Value: "kubecost",
  575. },
  576. }},
  577. expected: true,
  578. },
  579. {
  580. name: `label[app]="foo" and namespace="kubecost" -> first true`,
  581. a: &Allocation{
  582. Properties: &AllocationProperties{
  583. Namespace: "kube-system",
  584. Labels: map[string]string{
  585. "app": "foo",
  586. },
  587. },
  588. },
  589. filter: AllocationFilterAnd{[]AllocationFilter{
  590. AllocationFilterCondition{
  591. Field: FilterLabel,
  592. Op: FilterEquals,
  593. Key: "app",
  594. Value: "foo",
  595. },
  596. AllocationFilterCondition{
  597. Field: FilterNamespace,
  598. Op: FilterEquals,
  599. Value: "kubecost",
  600. },
  601. }},
  602. expected: false,
  603. },
  604. {
  605. name: `label[app]="foo" and namespace="kubecost" -> second true`,
  606. a: &Allocation{
  607. Properties: &AllocationProperties{
  608. Namespace: "kubecost",
  609. Labels: map[string]string{
  610. "app": "bar",
  611. },
  612. },
  613. },
  614. filter: AllocationFilterAnd{[]AllocationFilter{
  615. AllocationFilterCondition{
  616. Field: FilterLabel,
  617. Op: FilterEquals,
  618. Key: "app",
  619. Value: "foo",
  620. },
  621. AllocationFilterCondition{
  622. Field: FilterNamespace,
  623. Op: FilterEquals,
  624. Value: "kubecost",
  625. },
  626. }},
  627. expected: false,
  628. },
  629. {
  630. name: `label[app]="foo" and namespace="kubecost" -> both false`,
  631. a: &Allocation{
  632. Properties: &AllocationProperties{
  633. Namespace: "kube-system",
  634. Labels: map[string]string{
  635. "app": "bar",
  636. },
  637. },
  638. },
  639. filter: AllocationFilterAnd{[]AllocationFilter{
  640. AllocationFilterCondition{
  641. Field: FilterLabel,
  642. Op: FilterEquals,
  643. Key: "app",
  644. Value: "foo",
  645. },
  646. AllocationFilterCondition{
  647. Field: FilterNamespace,
  648. Op: FilterEquals,
  649. Value: "kubecost",
  650. },
  651. }},
  652. expected: false,
  653. },
  654. }
  655. for _, c := range cases {
  656. result := c.filter.Matches(c.a)
  657. if result != c.expected {
  658. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  659. }
  660. }
  661. }
  662. func Test_AllocationFilterOr_Matches(t *testing.T) {
  663. cases := []struct {
  664. name string
  665. a *Allocation
  666. filter AllocationFilter
  667. expected bool
  668. }{
  669. {
  670. name: `label[app]="foo" or namespace="kubecost" -> both true`,
  671. a: &Allocation{
  672. Properties: &AllocationProperties{
  673. Namespace: "kubecost",
  674. Labels: map[string]string{
  675. "app": "foo",
  676. },
  677. },
  678. },
  679. filter: AllocationFilterOr{[]AllocationFilter{
  680. AllocationFilterCondition{
  681. Field: FilterLabel,
  682. Op: FilterEquals,
  683. Key: "app",
  684. Value: "foo",
  685. },
  686. AllocationFilterCondition{
  687. Field: FilterNamespace,
  688. Op: FilterEquals,
  689. Value: "kubecost",
  690. },
  691. }},
  692. expected: true,
  693. },
  694. {
  695. name: `label[app]="foo" or namespace="kubecost" -> first true`,
  696. a: &Allocation{
  697. Properties: &AllocationProperties{
  698. Namespace: "kube-system",
  699. Labels: map[string]string{
  700. "app": "foo",
  701. },
  702. },
  703. },
  704. filter: AllocationFilterOr{[]AllocationFilter{
  705. AllocationFilterCondition{
  706. Field: FilterLabel,
  707. Op: FilterEquals,
  708. Key: "app",
  709. Value: "foo",
  710. },
  711. AllocationFilterCondition{
  712. Field: FilterNamespace,
  713. Op: FilterEquals,
  714. Value: "kubecost",
  715. },
  716. }},
  717. expected: true,
  718. },
  719. {
  720. name: `label[app]="foo" or namespace="kubecost" -> second true`,
  721. a: &Allocation{
  722. Properties: &AllocationProperties{
  723. Namespace: "kubecost",
  724. Labels: map[string]string{
  725. "app": "bar",
  726. },
  727. },
  728. },
  729. filter: AllocationFilterOr{[]AllocationFilter{
  730. AllocationFilterCondition{
  731. Field: FilterLabel,
  732. Op: FilterEquals,
  733. Key: "app",
  734. Value: "foo",
  735. },
  736. AllocationFilterCondition{
  737. Field: FilterNamespace,
  738. Op: FilterEquals,
  739. Value: "kubecost",
  740. },
  741. }},
  742. expected: true,
  743. },
  744. {
  745. name: `label[app]="foo" or namespace="kubecost" -> both false`,
  746. a: &Allocation{
  747. Properties: &AllocationProperties{
  748. Namespace: "kube-system",
  749. Labels: map[string]string{
  750. "app": "bar",
  751. },
  752. },
  753. },
  754. filter: AllocationFilterOr{[]AllocationFilter{
  755. AllocationFilterCondition{
  756. Field: FilterLabel,
  757. Op: FilterEquals,
  758. Key: "app",
  759. Value: "foo",
  760. },
  761. AllocationFilterCondition{
  762. Field: FilterNamespace,
  763. Op: FilterEquals,
  764. Value: "kubecost",
  765. },
  766. }},
  767. expected: false,
  768. },
  769. }
  770. for _, c := range cases {
  771. result := c.filter.Matches(c.a)
  772. if result != c.expected {
  773. t.Errorf("%s: expected %t, got %t", c.name, c.expected, result)
  774. }
  775. }
  776. }
  777. func Test_AllocationFilter_Flattened(t *testing.T) {
  778. cases := []struct {
  779. name string
  780. input AllocationFilter
  781. expected AllocationFilter
  782. }{
  783. {
  784. name: "AllocationFilterCondition",
  785. input: AllocationFilterCondition{
  786. Field: FilterNamespace,
  787. Op: FilterEquals,
  788. },
  789. expected: AllocationFilterCondition{
  790. Field: FilterNamespace,
  791. Op: FilterEquals,
  792. },
  793. },
  794. {
  795. name: "empty AllocationFilterAnd (nil)",
  796. input: AllocationFilterAnd{},
  797. expected: nil,
  798. },
  799. {
  800. name: "empty AllocationFilterAnd (len 0)",
  801. input: AllocationFilterAnd{Filters: []AllocationFilter{}},
  802. expected: nil,
  803. },
  804. {
  805. name: "empty AllocationFilterOr (nil)",
  806. input: AllocationFilterOr{},
  807. expected: nil,
  808. },
  809. {
  810. name: "empty AllocationFilterOr (len 0)",
  811. input: AllocationFilterOr{Filters: []AllocationFilter{}},
  812. expected: nil,
  813. },
  814. {
  815. name: "single-element AllocationFilterAnd",
  816. input: AllocationFilterAnd{Filters: []AllocationFilter{
  817. AllocationFilterCondition{
  818. Field: FilterNamespace,
  819. Op: FilterEquals,
  820. },
  821. }},
  822. expected: AllocationFilterCondition{
  823. Field: FilterNamespace,
  824. Op: FilterEquals,
  825. },
  826. },
  827. {
  828. name: "single-element AllocationFilterOr",
  829. input: AllocationFilterOr{Filters: []AllocationFilter{
  830. AllocationFilterCondition{
  831. Field: FilterNamespace,
  832. Op: FilterEquals,
  833. },
  834. }},
  835. expected: AllocationFilterCondition{
  836. Field: FilterNamespace,
  837. Op: FilterEquals,
  838. },
  839. },
  840. {
  841. name: "multi-element AllocationFilterAnd",
  842. input: AllocationFilterAnd{Filters: []AllocationFilter{
  843. AllocationFilterCondition{
  844. Field: FilterNamespace,
  845. Op: FilterEquals,
  846. },
  847. AllocationFilterCondition{
  848. Field: FilterClusterID,
  849. Op: FilterNotEquals,
  850. },
  851. AllocationFilterCondition{
  852. Field: FilterServices,
  853. Op: FilterContains,
  854. },
  855. }},
  856. expected: AllocationFilterAnd{Filters: []AllocationFilter{
  857. AllocationFilterCondition{
  858. Field: FilterNamespace,
  859. Op: FilterEquals,
  860. },
  861. AllocationFilterCondition{
  862. Field: FilterClusterID,
  863. Op: FilterNotEquals,
  864. },
  865. AllocationFilterCondition{
  866. Field: FilterServices,
  867. Op: FilterContains,
  868. },
  869. }},
  870. },
  871. {
  872. name: "multi-element AllocationFilterOr",
  873. input: AllocationFilterOr{Filters: []AllocationFilter{
  874. AllocationFilterCondition{
  875. Field: FilterNamespace,
  876. Op: FilterEquals,
  877. },
  878. AllocationFilterCondition{
  879. Field: FilterClusterID,
  880. Op: FilterNotEquals,
  881. },
  882. AllocationFilterCondition{
  883. Field: FilterServices,
  884. Op: FilterContains,
  885. },
  886. }},
  887. expected: AllocationFilterOr{Filters: []AllocationFilter{
  888. AllocationFilterCondition{
  889. Field: FilterNamespace,
  890. Op: FilterEquals,
  891. },
  892. AllocationFilterCondition{
  893. Field: FilterClusterID,
  894. Op: FilterNotEquals,
  895. },
  896. AllocationFilterCondition{
  897. Field: FilterServices,
  898. Op: FilterContains,
  899. },
  900. }},
  901. },
  902. }
  903. for _, c := range cases {
  904. t.Run(c.name, func(t *testing.T) {
  905. result := c.input.Flattened()
  906. if !reflect.DeepEqual(result, c.expected) {
  907. t.Errorf("Expected: '%s'. Got '%s'.", c.expected, result)
  908. }
  909. })
  910. }
  911. }