github.com/rajeev159/opa@v0.45.0/ast/compare_test.go (about) 1 // Copyright 2016 The OPA Authors. All rights reserved. 2 // Use of this source code is governed by an Apache2 3 // license that can be found in the LICENSE file. 4 5 package ast 6 7 import ( 8 "testing" 9 ) 10 11 func TestCompare(t *testing.T) { 12 13 // Many of the comparison cases are covered by existing equality tests. Here 14 // we cover edge cases. 15 tests := []struct { 16 a string 17 b string 18 expected int 19 }{ 20 // Comparisons to Go nil. Everything is greater than nil and nil is equal to nil 21 {"null", "", 1}, 22 {"", "null", -1}, 23 {"", "", 0}, 24 25 // Booleans 26 {"false", "true", -1}, 27 {"true", "false", 1}, 28 29 // Numbers 30 {"0", "1", -1}, 31 {"1", "0", 1}, 32 {"0", "0", 0}, 33 {"0", "1.5", -1}, 34 {"1.5", "0", 1}, 35 {"123456789123456789123", "123456789123456789123", 0}, 36 {"123456789123456789123", "123456789123456789122", 1}, 37 {"123456789123456789122", "123456789123456789123", -1}, 38 {"123456789123456789123.5", "123456789123456789123.5", 0}, 39 {"123456789123456789123.5", "123456789123456789122.5", 1}, 40 {"123456789123456789122.5", "123456789123456789123.5", -1}, 41 {"630E-840354372", "0", 0}, 42 43 // Object comparisons are consistent 44 {`{1: 2, 3: 4}`, `{4: 3, 1: 2}`, -1}, 45 {`{1: 2, 3: 4}`, `{1: 2, 4: 3}`, -1}, 46 {`{1: 2, 3: 4}`, `{1: 2, 3: 5}`, -1}, 47 {`{1: 2, 3: 4}`, `{1: 2, 3: 4, 5: 6}`, -1}, 48 {`{1: 2, 3: 4, 5: 6}`, `{1: 2, 3: 4}`, 1}, 49 50 // Comprehensions 51 {`[null | true]`, `[false | null]`, -1}, 52 {`{null | true}`, `{false | null}`, -1}, 53 {`{"abc": null | true}`, `{"cba": false | null}`, -1}, 54 55 // Expressions 56 {`a = b`, `b = a`, -1}, 57 {`b = a`, `not a = b`, -1}, 58 {`a = b`, `x`, 1}, 59 {`a = b`, `a = b with input.foo as bar`, -1}, 60 {`a = b with input.foo as bar`, `a = b`, 1}, 61 {`a = b with input.foo as bar`, `a = b with input.foo.bar.baz as qux`, -1}, 62 {`a = b with input.foo as bar`, `a = b with input.foo as bar with input.baz as qux`, -1}, 63 64 // Body 65 {`a = b`, `a = b; b = a`, -1}, 66 {`a = b; b = a`, `a = b`, 1}, 67 } 68 for _, tc := range tests { 69 var a, b interface{} 70 if len(tc.a) > 0 { 71 a = MustParseStatement(tc.a) 72 } 73 if len(tc.b) > 0 { 74 b = MustParseStatement(tc.b) 75 } 76 result := Compare(a, b) 77 if tc.expected != result { 78 t.Errorf("Expected %v.Compare(%v) == %v but got %v", a, b, tc.expected, result) 79 } 80 } 81 } 82 83 func TestCompareModule(t *testing.T) { 84 a := MustParseModule(`package a.b.c`) 85 b := MustParseModule(`package a.b.d`) 86 result := Compare(a, b) 87 88 if result != -1 { 89 t.Errorf("Expected %v to be less than %v but got: %v", a, b, result) 90 } 91 92 a = MustParseModule(`package a.b.c 93 94 import input.x.y`) 95 b = MustParseModule(`package a.b.c 96 97 import input.x.z`) 98 result = Compare(a, b) 99 100 if result != -1 { 101 t.Errorf("Expected %v to be less than %v but got: %v", a, b, result) 102 } 103 104 var err error 105 106 a, err = ParseModuleWithOpts("test.rego", `package a 107 108 # METADATA 109 # scope: rule 110 # schemas: 111 # - input: schema.a 112 p := 7`, ParserOptions{ProcessAnnotation: true}) 113 if err != nil { 114 t.Fatal(err) 115 } 116 117 b, err = ParseModuleWithOpts("test.rego", `package a 118 119 # METADATA 120 # scope: rule 121 # schemas: 122 # - input: schema.b 123 p := 7`, ParserOptions{ProcessAnnotation: true}) 124 if err != nil { 125 t.Fatal(err) 126 } 127 128 result = Compare(a, b) 129 130 if result != -1 { 131 t.Errorf("Expected %v to be less than %v but got: %v", a, b, result) 132 } 133 } 134 135 func TestCompareAnnotations(t *testing.T) { 136 137 tests := []struct { 138 note string 139 a string 140 b string 141 exp int 142 }{ 143 { 144 note: "same", 145 a: ` 146 # METADATA 147 # scope: a`, 148 b: ` 149 # METADATA 150 # scope: a`, 151 exp: 0, 152 }, 153 { 154 note: "unknown scope", 155 a: ` 156 # METADATA 157 # scope: rule`, 158 b: ` 159 # METADATA 160 # scope: a`, 161 exp: 1, 162 }, 163 { 164 note: "unknown scope - less than", 165 a: ` 166 # METADATA 167 # scope: a`, 168 b: ` 169 # METADATA 170 # scope: rule`, 171 exp: -1, 172 }, 173 { 174 note: "unknown scope - greater than - lexigraphical", 175 a: ` 176 # METADATA 177 # scope: b`, 178 b: ` 179 # METADATA 180 # scope: a`, 181 exp: 1, 182 }, 183 { 184 note: "unknown scope - less than - lexigraphical", 185 a: ` 186 # METADATA 187 # scope: b`, 188 b: ` 189 # METADATA 190 # scope: c`, 191 exp: -1, 192 }, 193 { 194 note: "schema", 195 a: ` 196 # METADATA 197 # scope: rule 198 # schemas: 199 # - input: schema`, 200 b: ` 201 # METADATA 202 # scope: rule 203 # schemas: 204 # - input: schema`, 205 exp: 0, 206 }, 207 { 208 note: "schema - less than", 209 a: ` 210 # METADATA 211 # scope: rule 212 # schemas: 213 # - input.a: schema`, 214 b: ` 215 # METADATA 216 # scope: rule 217 # schemas: 218 # - input.b: schema`, 219 exp: -1, 220 }, 221 { 222 note: "schema - greater than", 223 a: ` 224 # METADATA 225 # scope: rule 226 # schemas: 227 # - input.b: schema`, 228 b: ` 229 # METADATA 230 # scope: rule 231 # schemas: 232 # - input.a: schema`, 233 exp: 1, 234 }, 235 { 236 note: "schema - less than (fewer)", 237 a: ` 238 # METADATA 239 # scope: rule 240 # schemas: 241 # - input.a: schema`, 242 b: ` 243 # METADATA 244 # scope: rule 245 # schemas: 246 # - input.a: schema 247 # - input.b: schema`, 248 exp: -1, 249 }, 250 { 251 note: "schema - greater than (more)", 252 a: ` 253 # METADATA 254 # scope: rule 255 # schemas: 256 # - input.a: schema 257 # - input.b: schema`, 258 b: ` 259 # METADATA 260 # scope: rule 261 # schemas: 262 # - input.a: schema`, 263 exp: 1, 264 }, 265 { 266 note: "schema - less than - lexigraphical", 267 a: ` 268 # METADATA 269 # scope: rule 270 # schemas: 271 # - input: schema.a`, 272 b: ` 273 # METADATA 274 # scope: rule 275 # schemas: 276 # - input: schema.b`, 277 exp: -1, 278 }, 279 { 280 note: "schema - greater than - lexigraphical", 281 a: ` 282 # METADATA 283 # scope: rule 284 # schemas: 285 # - input: schema.c`, 286 b: ` 287 # METADATA 288 # scope: rule 289 # schemas: 290 # - input: schema.b`, 291 exp: 1, 292 }, 293 { 294 note: "definition", 295 a: ` 296 # METADATA 297 # schemas: 298 # - input: {"type": "string"}`, 299 b: ` 300 # METADATA 301 # schemas: 302 # - input: {"type": "string"}`, 303 }, 304 { 305 note: "definition - less than schema", 306 a: ` 307 # METADATA 308 # schemas: 309 # - input: {"type": "string"}`, 310 b: ` 311 # METADATA 312 # schemas: 313 # - input: schema.a`, 314 exp: -1, 315 }, 316 { 317 note: "schema - greater than definition", 318 a: ` 319 # METADATA 320 # schemas: 321 # - input: schema.a`, 322 b: ` 323 # METADATA 324 # schemas: 325 # - input: {"type": "string"}`, 326 exp: 1, 327 }, 328 { 329 note: "title", 330 a: ` 331 # METADATA 332 # title: a`, 333 b: ` 334 # METADATA 335 # title: a`, 336 exp: 0, 337 }, 338 { 339 note: "title - less than", 340 a: ` 341 # METADATA 342 # title: a`, 343 b: ` 344 # METADATA 345 # title: b`, 346 exp: -1, 347 }, 348 { 349 note: "title - greater than", 350 a: ` 351 # METADATA 352 # title: b`, 353 b: ` 354 # METADATA 355 # title: a`, 356 exp: 1, 357 }, 358 { 359 note: "description", 360 a: ` 361 # METADATA 362 # description: a`, 363 b: ` 364 # METADATA 365 # description: a`, 366 exp: 0, 367 }, 368 { 369 note: "description - less than", 370 a: ` 371 # METADATA 372 # description: a`, 373 b: ` 374 # METADATA 375 # description: b`, 376 exp: -1, 377 }, 378 { 379 note: "description - greater than", 380 a: ` 381 # METADATA 382 # description: b`, 383 b: ` 384 # METADATA 385 # description: a`, 386 exp: 1, 387 }, 388 { 389 note: "authors", 390 a: ` 391 # METADATA 392 # authors: 393 # - John Doe 394 # - Jane Doe`, 395 b: ` 396 # METADATA 397 # authors: 398 # - John Doe 399 # - Jane Doe`, 400 exp: 0, 401 }, 402 { 403 note: "authors - less than", 404 a: ` 405 # METADATA 406 # authors: 407 # - Jane Doe 408 # - John Doe 409 `, 410 b: ` 411 # METADATA 412 # authors: 413 # - John Doe 414 # - Jane Doe`, 415 exp: -1, 416 }, 417 { 418 note: "authors - greater than", 419 a: ` 420 # METADATA 421 # authors: 422 # - John Doe 423 # - Jane Doe`, 424 b: ` 425 # METADATA 426 # authors: 427 # - Jane Doe 428 # - John Doe`, 429 exp: 1, 430 }, 431 { 432 note: "authors - less than (fewer)", 433 a: ` 434 # METADATA 435 # scope: rule 436 # authors: 437 # - John Doe`, 438 b: ` 439 # METADATA 440 # scope: rule 441 # authors: 442 # - John Doe 443 # - Jane Doe`, 444 exp: -1, 445 }, 446 { 447 note: "authors - greater than (more)", 448 a: ` 449 # METADATA 450 # scope: rule 451 # authors: 452 # - John Doe 453 # - Jane Doe`, 454 b: ` 455 # METADATA 456 # scope: rule 457 # authors: 458 # - John Doe`, 459 exp: 1, 460 }, 461 { 462 note: "authors - less than (email)", 463 a: ` 464 # METADATA 465 # authors: 466 # - John Doe <a@example.com>`, 467 b: ` 468 # METADATA 469 # authors: 470 # - John Doe <b@example.com>`, 471 exp: -1, 472 }, 473 { 474 note: "authors - greater than (email)", 475 a: ` 476 # METADATA 477 # authors: 478 # - John Doe <b@example.com>`, 479 b: ` 480 # METADATA 481 # authors: 482 # - John Doe <a@example.com>`, 483 exp: 1, 484 }, 485 { 486 note: "organizations", 487 a: ` 488 # METADATA 489 # organizations: 490 # - a 491 # - b`, 492 b: ` 493 # METADATA 494 # organizations: 495 # - a 496 # - b`, 497 exp: 0, 498 }, 499 { 500 note: "organizations - less than", 501 a: ` 502 # METADATA 503 # organizations: 504 # - a 505 # - b`, 506 b: ` 507 # METADATA 508 # organizations: 509 # - c 510 # - d`, 511 exp: -1, 512 }, 513 { 514 note: "organizations - greater than", 515 a: ` 516 # METADATA 517 # organizations: 518 # - c 519 # - d`, 520 b: ` 521 # METADATA 522 # organizations: 523 # - a 524 # - b`, 525 exp: 1, 526 }, 527 { 528 note: "organizations - less than (fewer)", 529 a: ` 530 # METADATA 531 # scope: rule 532 # organizations: 533 # - a`, 534 b: ` 535 # METADATA 536 # scope: rule 537 # organizations: 538 # - a 539 # - b`, 540 exp: -1, 541 }, 542 { 543 note: "organizations - greater than (more)", 544 a: ` 545 # METADATA 546 # scope: rule 547 # organizations: 548 # - a 549 # - b`, 550 b: ` 551 # METADATA 552 # scope: rule 553 # organizations: 554 # - a`, 555 exp: 1, 556 }, 557 { 558 note: "related_resources", 559 a: ` 560 # METADATA 561 # related_resources: 562 # - https://a.example.com 563 # - 564 # ref: https://b.example.com 565 # description: foo bar`, 566 b: ` 567 # METADATA 568 # related_resources: 569 # - https://a.example.com 570 # - 571 # ref: https://b.example.com 572 # description: foo bar`, 573 exp: 0, 574 }, 575 { 576 note: "related_resources - less than", 577 a: ` 578 # METADATA 579 # related_resources: 580 # - https://a.example.com 581 # - https://b.example.com`, 582 b: ` 583 # METADATA 584 # related_resources: 585 # - https://b.example.com 586 # - https://c.example.com`, 587 exp: -1, 588 }, 589 { 590 note: "related_resources - greater than", 591 a: ` 592 # METADATA 593 # related_resources: 594 # - https://b.example.com 595 # - https://c.example.com`, 596 b: ` 597 # METADATA 598 # related_resources: 599 # - https://a.example.com 600 # - https://b.example.com`, 601 exp: 1, 602 }, 603 { 604 note: "related_resources - less than (fewer)", 605 a: ` 606 # METADATA 607 # scope: rule 608 # organizations: 609 # - https://a.example.com`, 610 b: ` 611 # METADATA 612 # scope: rule 613 # organizations: 614 # - https://a.example.com 615 # - https://b.example.com`, 616 exp: -1, 617 }, 618 { 619 note: "related_resources - greater than (more)", 620 a: ` 621 # METADATA 622 # scope: rule 623 # organizations: 624 # - https://a.example.com 625 # - https://b.example.com`, 626 b: ` 627 # METADATA 628 # scope: rule 629 # organizations: 630 # - https://a.example.com`, 631 exp: 1, 632 }, 633 { 634 note: "related_resources - less than (description)", 635 a: ` 636 # METADATA 637 # related_resources: 638 # - 639 # ref: https://example.com 640 # description: a`, 641 b: ` 642 # METADATA 643 # related_resources: 644 # - 645 # ref: https://example.com 646 # description: b`, 647 exp: -1, 648 }, 649 { 650 note: "related_resources - greater than (description)", 651 a: ` 652 # METADATA 653 # related_resources: 654 # - 655 # ref: https://example.com 656 # description: b`, 657 b: ` 658 # METADATA 659 # related_resources: 660 # - 661 # ref: https://example.com 662 # description: a`, 663 exp: 1, 664 }, 665 { 666 note: "custom", 667 a: ` 668 # METADATA 669 # custom: 670 # a: 1 671 # b: true 672 # c: 673 # d: 674 # - 1 675 # - 2 676 # e: 677 # i: 1 678 # j: 2`, 679 b: ` 680 # METADATA 681 # custom: 682 # a: 1 683 # b: true 684 # c: 685 # d: 686 # - 1 687 # - 2 688 # e: 689 # i: 1 690 # j: 2`, 691 exp: 0, 692 }, 693 { 694 note: "custom - less than", 695 a: ` 696 # METADATA 697 # custom: 698 # a: 1`, 699 b: ` 700 # METADATA 701 # custom: 702 # b: 1`, 703 exp: -1, 704 }, 705 { 706 note: "custom - greater than", 707 a: ` 708 # METADATA 709 # custom: 710 # b: 1`, 711 b: ` 712 # METADATA 713 # custom: 714 # a: 1`, 715 exp: 1, 716 }, 717 { 718 note: "custom - less than (value)", 719 a: ` 720 # METADATA 721 # custom: 722 # a: 1`, 723 b: ` 724 # METADATA 725 # custom: 726 # a: 2`, 727 exp: -1, 728 }, 729 { 730 note: "custom - greater than (value)", 731 a: ` 732 # METADATA 733 # custom: 734 # a: 2`, 735 b: ` 736 # METADATA 737 # custom: 738 # a: 1`, 739 exp: 1, 740 }, 741 { 742 note: "custom - less than (fewer)", 743 a: ` 744 # METADATA 745 # custom: 746 # a: 1`, 747 b: ` 748 # METADATA 749 # custom: 750 # a: 1 751 # b: 2`, 752 exp: -1, 753 }, 754 { 755 note: "custom - greater than (more)", 756 a: ` 757 # METADATA 758 # custom: 759 # a: 1 760 # b: 2`, 761 b: ` 762 # METADATA 763 # custom: 764 # a: 1`, 765 exp: 1, 766 }, 767 } 768 769 for _, tc := range tests { 770 t.Run(tc.note, func(t *testing.T) { 771 stmts, _, err := ParseStatementsWithOpts("test.rego", tc.a, ParserOptions{ProcessAnnotation: true}) 772 if err != nil { 773 t.Fatal(err) 774 } 775 a := stmts[0].(*Annotations) 776 stmts, _, err = ParseStatementsWithOpts("test.rego", tc.b, ParserOptions{ProcessAnnotation: true}) 777 if err != nil { 778 t.Fatal(err) 779 } 780 b := stmts[0].(*Annotations) 781 result := a.Compare(b) 782 if result != tc.exp { 783 t.Fatalf("Expected %d but got %v for %v and %v", tc.exp, result, a, b) 784 } 785 }) 786 } 787 }