k8s.io/kube-openapi@v0.0.0-20240228011516-70dd3763d340/pkg/aggregator/aggregator_test.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package aggregator 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "os" 23 "path/filepath" 24 "reflect" 25 "testing" 26 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 30 "k8s.io/kube-openapi/pkg/handler" 31 "k8s.io/kube-openapi/pkg/validation/spec" 32 "sigs.k8s.io/yaml" 33 ) 34 35 type DebugSpec struct { 36 *spec.Swagger 37 } 38 39 func (d DebugSpec) String() string { 40 bytes, err := d.Swagger.MarshalJSON() 41 if err != nil { 42 return fmt.Sprintf("DebugSpec.String failed: %s", err) 43 } 44 return string(bytes) 45 } 46 func TestFilterSpecs(t *testing.T) { 47 var spec1, spec1_filtered *spec.Swagger 48 yaml.Unmarshal([]byte(` 49 swagger: "2.0" 50 paths: 51 /test: 52 post: 53 tags: 54 - "test" 55 summary: "Test API" 56 operationId: "addTest" 57 parameters: 58 - in: "body" 59 name: "body" 60 description: "test object" 61 required: true 62 schema: 63 $ref: "#/definitions/Test" 64 responses: 65 405: 66 description: "Invalid input" 67 $ref: "#/definitions/InvalidInput" 68 /othertest: 69 post: 70 tags: 71 - "test2" 72 summary: "Test2 API" 73 operationId: "addTest2" 74 consumes: 75 - "application/json" 76 produces: 77 - "application/xml" 78 parameters: 79 - in: "body" 80 name: "body" 81 description: "test2 object" 82 required: true 83 schema: 84 $ref: "#/definitions/Test2" 85 definitions: 86 Test: 87 type: "object" 88 properties: 89 id: 90 type: "integer" 91 format: "int64" 92 status: 93 type: "string" 94 description: "Status" 95 InvalidInput: 96 type: "string" 97 format: "string" 98 Test2: 99 type: "object" 100 properties: 101 other: 102 $ref: "#/definitions/Other" 103 Other: 104 type: "string" 105 `), &spec1) 106 107 yaml.Unmarshal([]byte(` 108 swagger: "2.0" 109 paths: 110 /test: 111 post: 112 tags: 113 - "test" 114 summary: "Test API" 115 operationId: "addTest" 116 parameters: 117 - in: "body" 118 name: "body" 119 description: "test object" 120 required: true 121 schema: 122 $ref: "#/definitions/Test" 123 responses: 124 405: 125 description: "Invalid input" 126 $ref: "#/definitions/InvalidInput" 127 definitions: 128 Test: 129 type: "object" 130 properties: 131 id: 132 type: "integer" 133 format: "int64" 134 status: 135 type: "string" 136 description: "Status" 137 InvalidInput: 138 type: "string" 139 format: "string" 140 `), &spec1_filtered) 141 142 ast := assert.New(t) 143 orig_spec1, _ := cloneSpec(spec1) 144 new_spec1 := FilterSpecByPathsWithoutSideEffects(spec1, []string{"/test"}) 145 ast.Equal(DebugSpec{spec1_filtered}, DebugSpec{new_spec1}) 146 ast.Equal(DebugSpec{orig_spec1}, DebugSpec{spec1}, "unexpected mutation of input") 147 } 148 149 func TestFilterSpecsWithUnusedDefinitions(t *testing.T) { 150 var spec1, spec1Filtered *spec.Swagger 151 yaml.Unmarshal([]byte(` 152 swagger: "2.0" 153 paths: 154 /test: 155 post: 156 tags: 157 - "test" 158 summary: "Test API" 159 operationId: "addTest" 160 parameters: 161 - in: "body" 162 name: "body" 163 description: "test object" 164 required: true 165 schema: 166 $ref: "#/definitions/Test" 167 responses: 168 405: 169 description: "Invalid input" 170 $ref: "#/definitions/InvalidInput" 171 /othertest: 172 post: 173 tags: 174 - "test2" 175 summary: "Test2 API" 176 operationId: "addTest2" 177 consumes: 178 - "application/json" 179 produces: 180 - "application/xml" 181 parameters: 182 - in: "body" 183 name: "body" 184 description: "test2 object" 185 required: true 186 schema: 187 $ref: "#/definitions/Test2" 188 definitions: 189 Test: 190 type: "object" 191 properties: 192 id: 193 type: "integer" 194 format: "int64" 195 status: 196 type: "string" 197 description: "Status" 198 InvalidInput: 199 type: "string" 200 format: "string" 201 Test2: 202 type: "object" 203 properties: 204 other: 205 $ref: "#/definitions/Other" 206 Other: 207 type: "string" 208 Unused: 209 type: "object" 210 `), &spec1) 211 212 yaml.Unmarshal([]byte(` 213 swagger: "2.0" 214 paths: 215 /test: 216 post: 217 tags: 218 - "test" 219 summary: "Test API" 220 operationId: "addTest" 221 parameters: 222 - in: "body" 223 name: "body" 224 description: "test object" 225 required: true 226 schema: 227 $ref: "#/definitions/Test" 228 responses: 229 405: 230 description: "Invalid input" 231 $ref: "#/definitions/InvalidInput" 232 definitions: 233 Test: 234 type: "object" 235 properties: 236 id: 237 type: "integer" 238 format: "int64" 239 status: 240 type: "string" 241 description: "Status" 242 InvalidInput: 243 type: "string" 244 format: "string" 245 Unused: 246 type: "object" 247 `), &spec1Filtered) 248 249 ast := assert.New(t) 250 orig_spec1, _ := cloneSpec(spec1) 251 new_spec1 := FilterSpecByPathsWithoutSideEffects(spec1, []string{"/test"}) 252 ast.Equal(DebugSpec{spec1Filtered}, DebugSpec{new_spec1}) 253 ast.Equal(DebugSpec{orig_spec1}, DebugSpec{spec1}, "unexpected mutation of input") 254 } 255 256 func TestMergeSpecsSimple(t *testing.T) { 257 var spec1, spec2, expected *spec.Swagger 258 require.NoError(t, yaml.Unmarshal([]byte(` 259 swagger: "2.0" 260 paths: 261 /test: 262 post: 263 tags: 264 - "test" 265 summary: "Test API" 266 operationId: "addTest" 267 parameters: 268 - in: "body" 269 name: "body" 270 description: "test object" 271 required: true 272 schema: 273 $ref: "#/definitions/Test" 274 - $ref: "#/parameters/a" 275 responses: 276 405: 277 description: "Invalid input" 278 $ref: "#/definitions/InvalidInput" 279 definitions: 280 Test: 281 type: "object" 282 properties: 283 id: 284 type: "integer" 285 format: "int64" 286 status: 287 type: "string" 288 description: "Status" 289 InvalidInput: 290 type: "string" 291 format: "string" 292 parameters: 293 a: 294 in: query 295 name: a 296 schema: 297 $ref: "#/definitions/Test" 298 `), &spec1)) 299 300 require.NoError(t, yaml.Unmarshal([]byte(` 301 swagger: "2.0" 302 paths: 303 /othertest: 304 post: 305 tags: 306 - "test2" 307 summary: "Test2 API" 308 operationId: "addTest2" 309 consumes: 310 - "application/json" 311 produces: 312 - "application/xml" 313 parameters: 314 - in: "body" 315 name: "body" 316 description: "test2 object" 317 required: true 318 schema: 319 $ref: "#/definitions/Test2" 320 - $ref: "#/parameters/b" 321 definitions: 322 Test2: 323 type: "object" 324 properties: 325 other: 326 $ref: "#/definitions/Other" 327 Other: 328 type: "string" 329 parameters: 330 b: 331 in: query 332 name: b 333 schema: 334 $ref: "#/definitions/Test2" 335 `), &spec2)) 336 337 require.NoError(t, yaml.Unmarshal([]byte(` 338 swagger: "2.0" 339 paths: 340 /test: 341 post: 342 tags: 343 - "test" 344 summary: "Test API" 345 operationId: "addTest" 346 parameters: 347 - in: "body" 348 name: "body" 349 description: "test object" 350 required: true 351 schema: 352 $ref: "#/definitions/Test" 353 - $ref: "#/parameters/a" 354 responses: 355 405: 356 description: "Invalid input" 357 $ref: "#/definitions/InvalidInput" 358 /othertest: 359 post: 360 tags: 361 - "test2" 362 summary: "Test2 API" 363 operationId: "addTest2" 364 consumes: 365 - "application/json" 366 produces: 367 - "application/xml" 368 parameters: 369 - in: "body" 370 name: "body" 371 description: "test2 object" 372 required: true 373 schema: 374 $ref: "#/definitions/Test2" 375 - $ref: "#/parameters/b" 376 definitions: 377 Test: 378 type: "object" 379 properties: 380 id: 381 type: "integer" 382 format: "int64" 383 status: 384 type: "string" 385 description: "Status" 386 InvalidInput: 387 type: "string" 388 format: "string" 389 Test2: 390 type: "object" 391 properties: 392 other: 393 $ref: "#/definitions/Other" 394 Other: 395 type: "string" 396 parameters: 397 a: 398 in: query 399 name: a 400 schema: 401 $ref: "#/definitions/Test" 402 b: 403 in: query 404 name: b 405 schema: 406 $ref: "#/definitions/Test2" 407 `), &expected)) 408 409 ast := assert.New(t) 410 orig_spec2, _ := cloneSpec(spec2) 411 if !ast.NoError(MergeSpecs(spec1, spec2)) { 412 return 413 } 414 ast.Equal(DebugSpec{expected}.String(), DebugSpec{spec1}.String()) 415 ast.Equal(DebugSpec{orig_spec2}.String(), DebugSpec{spec2}.String(), "unexpected mutation of input") 416 } 417 418 func TestMergeSpecsEmptyDefinitions(t *testing.T) { 419 var spec1, spec2, expected *spec.Swagger 420 require.NoError(t, yaml.Unmarshal([]byte(` 421 swagger: "2.0" 422 paths: 423 /test: 424 post: 425 tags: 426 - "test" 427 summary: "Test API" 428 operationId: "addTest" 429 parameters: 430 - in: "body" 431 name: "body" 432 description: "test object" 433 required: true 434 responses: 435 405: 436 description: "Invalid input" 437 `), &spec1)) 438 439 require.NoError(t, yaml.Unmarshal([]byte(` 440 swagger: "2.0" 441 paths: 442 /othertest: 443 post: 444 tags: 445 - "test2" 446 summary: "Test2 API" 447 operationId: "addTest2" 448 consumes: 449 - "application/json" 450 produces: 451 - "application/xml" 452 parameters: 453 - in: "body" 454 name: "body" 455 description: "test2 object" 456 required: true 457 schema: 458 $ref: "#/definitions/Test2" 459 definitions: 460 Test2: 461 type: "object" 462 properties: 463 other: 464 $ref: "#/definitions/Other" 465 Other: 466 type: "string" 467 `), &spec2)) 468 469 require.NoError(t, yaml.Unmarshal([]byte(` 470 swagger: "2.0" 471 paths: 472 /test: 473 post: 474 tags: 475 - "test" 476 summary: "Test API" 477 operationId: "addTest" 478 parameters: 479 - in: "body" 480 name: "body" 481 description: "test object" 482 required: true 483 responses: 484 405: 485 description: "Invalid input" 486 /othertest: 487 post: 488 tags: 489 - "test2" 490 summary: "Test2 API" 491 operationId: "addTest2" 492 consumes: 493 - "application/json" 494 produces: 495 - "application/xml" 496 parameters: 497 - in: "body" 498 name: "body" 499 description: "test2 object" 500 required: true 501 schema: 502 $ref: "#/definitions/Test2" 503 definitions: 504 Test2: 505 type: "object" 506 properties: 507 other: 508 $ref: "#/definitions/Other" 509 Other: 510 type: "string" 511 `), &expected)) 512 513 ast := assert.New(t) 514 orig_spec2, _ := cloneSpec(spec2) 515 if !ast.NoError(MergeSpecs(spec1, spec2)) { 516 return 517 } 518 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 519 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 520 } 521 522 func TestMergeSpecsEmptyPaths(t *testing.T) { 523 var spec1, spec2, expected *spec.Swagger 524 yaml.Unmarshal([]byte(` 525 swagger: "2.0" 526 definitions: 527 Test: 528 type: "object" 529 properties: 530 id: 531 type: "integer" 532 format: "int64" 533 status: 534 type: "string" 535 description: "Status" 536 InvalidInput: 537 type: "string" 538 format: "string" 539 `), &spec1) 540 541 yaml.Unmarshal([]byte(` 542 swagger: "2.0" 543 paths: 544 /othertest: 545 post: 546 tags: 547 - "test2" 548 summary: "Test2 API" 549 operationId: "addTest2" 550 consumes: 551 - "application/json" 552 produces: 553 - "application/xml" 554 parameters: 555 - in: "body" 556 name: "body" 557 description: "test2 object" 558 required: true 559 schema: 560 $ref: "#/definitions/Test2" 561 definitions: 562 Test2: 563 type: "object" 564 properties: 565 other: 566 $ref: "#/definitions/Other" 567 Other: 568 type: "string" 569 `), &spec2) 570 571 yaml.Unmarshal([]byte(` 572 swagger: "2.0" 573 paths: 574 /othertest: 575 post: 576 tags: 577 - "test2" 578 summary: "Test2 API" 579 operationId: "addTest2" 580 consumes: 581 - "application/json" 582 produces: 583 - "application/xml" 584 parameters: 585 - in: "body" 586 name: "body" 587 description: "test2 object" 588 required: true 589 schema: 590 $ref: "#/definitions/Test2" 591 definitions: 592 Test: 593 type: "object" 594 properties: 595 id: 596 type: "integer" 597 format: "int64" 598 status: 599 type: "string" 600 description: "Status" 601 InvalidInput: 602 type: "string" 603 format: "string" 604 Test2: 605 type: "object" 606 properties: 607 other: 608 $ref: "#/definitions/Other" 609 Other: 610 type: "string" 611 `), &expected) 612 613 ast := assert.New(t) 614 orig_spec2, _ := cloneSpec(spec2) 615 if !ast.NoError(MergeSpecs(spec1, spec2)) { 616 return 617 } 618 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 619 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 620 } 621 622 func TestMergeSpecsReuseModel(t *testing.T) { 623 var spec1, spec2, expected *spec.Swagger 624 require.NoError(t, yaml.Unmarshal([]byte(` 625 swagger: "2.0" 626 paths: 627 /test: 628 post: 629 tags: 630 - "test" 631 summary: "Test API" 632 operationId: "addTest" 633 parameters: 634 - in: "body" 635 name: "body" 636 description: "test object" 637 required: true 638 schema: 639 $ref: "#/definitions/Test" 640 - $ref: "#/parameters/a" 641 responses: 642 405: 643 description: "Invalid input" 644 $ref: "#/definitions/InvalidInput" 645 definitions: 646 Test: 647 type: "object" 648 properties: 649 id: 650 type: "integer" 651 format: "int64" 652 status: 653 type: "string" 654 description: "Status" 655 InvalidInput: 656 type: "string" 657 format: "string" 658 parameters: 659 a: 660 in: query 661 name: a 662 schema: 663 $ref: "#/definitions/Test" 664 `), &spec1)) 665 666 require.NoError(t, yaml.Unmarshal([]byte(` 667 swagger: "2.0" 668 paths: 669 /othertest: 670 post: 671 tags: 672 - "test2" 673 summary: "Test2 API" 674 operationId: "addTest2" 675 consumes: 676 - "application/json" 677 produces: 678 - "application/xml" 679 parameters: 680 - in: "body" 681 name: "body" 682 description: "test2 object" 683 required: true 684 schema: 685 $ref: "#/definitions/Test" 686 - $ref: "#/parameters/a" 687 definitions: 688 Test: 689 type: "object" 690 properties: 691 id: 692 type: "integer" 693 format: "int64" 694 status: 695 type: "string" 696 description: "Status" 697 InvalidInput: 698 type: "string" 699 format: "string" 700 parameters: 701 a: 702 in: query 703 name: a 704 schema: 705 $ref: "#/definitions/Test" 706 `), &spec2)) 707 708 require.NoError(t, yaml.Unmarshal([]byte(` 709 swagger: "2.0" 710 paths: 711 /test: 712 post: 713 tags: 714 - "test" 715 summary: "Test API" 716 operationId: "addTest" 717 parameters: 718 - in: "body" 719 name: "body" 720 description: "test object" 721 required: true 722 schema: 723 $ref: "#/definitions/Test" 724 - $ref: "#/parameters/a" 725 responses: 726 405: 727 description: "Invalid input" 728 $ref: "#/definitions/InvalidInput" 729 /othertest: 730 post: 731 tags: 732 - "test2" 733 summary: "Test2 API" 734 operationId: "addTest2" 735 consumes: 736 - "application/json" 737 produces: 738 - "application/xml" 739 parameters: 740 - in: "body" 741 name: "body" 742 description: "test2 object" 743 required: true 744 schema: 745 $ref: "#/definitions/Test" 746 - $ref: "#/parameters/a" 747 definitions: 748 Test: 749 type: "object" 750 properties: 751 id: 752 type: "integer" 753 format: "int64" 754 status: 755 type: "string" 756 description: "Status" 757 InvalidInput: 758 type: "string" 759 format: "string" 760 parameters: 761 a: 762 in: query 763 name: a 764 schema: 765 $ref: "#/definitions/Test" 766 `), &expected)) 767 768 ast := assert.New(t) 769 orig_spec2, _ := cloneSpec(spec2) 770 if !ast.NoError(MergeSpecs(spec1, spec2)) { 771 return 772 } 773 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 774 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 775 } 776 777 func TestMergeSpecsRenameModel(t *testing.T) { 778 var spec1, spec2, expected *spec.Swagger 779 require.NoError(t, yaml.Unmarshal([]byte(` 780 swagger: "2.0" 781 paths: 782 /test: 783 post: 784 tags: 785 - "test" 786 summary: "Test API" 787 operationId: "addTest" 788 parameters: 789 - in: "body" 790 name: "body" 791 description: "test object" 792 required: true 793 schema: 794 $ref: "#/definitions/Test" 795 - $ref: "#/parameters/a" 796 responses: 797 405: 798 description: "Invalid input" 799 $ref: "#/definitions/InvalidInput" 800 definitions: 801 Test: 802 type: "object" 803 properties: 804 id: 805 type: "integer" 806 format: "int64" 807 status: 808 type: "string" 809 description: "Status" 810 InvalidInput: 811 type: "string" 812 format: "string" 813 parameters: 814 a: 815 in: query 816 name: a 817 schema: 818 $ref: "#/definitions/Test" 819 `), &spec1)) 820 821 require.NoError(t, yaml.Unmarshal([]byte(` 822 swagger: "2.0" 823 paths: 824 /othertest: 825 post: 826 tags: 827 - "test2" 828 summary: "Test2 API" 829 operationId: "addTest2" 830 consumes: 831 - "application/json" 832 produces: 833 - "application/xml" 834 parameters: 835 - in: "body" 836 name: "body" 837 description: "test2 object" 838 required: true 839 schema: 840 $ref: "#/definitions/Test" 841 - $ref: "#/parameters/a" 842 definitions: 843 Test: 844 description: "This Test has a description" 845 type: "object" 846 properties: 847 id: 848 type: "integer" 849 format: "int64" 850 InvalidInput: 851 type: "string" 852 format: "string" 853 parameters: 854 a: 855 in: query 856 name: a 857 schema: 858 $ref: "#/definitions/Test" 859 `), &spec2)) 860 861 require.NoError(t, yaml.Unmarshal([]byte(` 862 swagger: "2.0" 863 paths: 864 /test: 865 post: 866 tags: 867 - "test" 868 summary: "Test API" 869 operationId: "addTest" 870 parameters: 871 - in: "body" 872 name: "body" 873 description: "test object" 874 required: true 875 schema: 876 $ref: "#/definitions/Test" 877 - $ref: "#/parameters/a" 878 responses: 879 405: 880 description: "Invalid input" 881 $ref: "#/definitions/InvalidInput" 882 /othertest: 883 post: 884 tags: 885 - "test2" 886 summary: "Test2 API" 887 operationId: "addTest2" 888 consumes: 889 - "application/json" 890 produces: 891 - "application/xml" 892 parameters: 893 - in: "body" 894 name: "body" 895 description: "test2 object" 896 required: true 897 schema: 898 $ref: "#/definitions/Test_v2" 899 - $ref: "#/parameters/a_v2" 900 definitions: 901 Test: 902 type: "object" 903 properties: 904 id: 905 type: "integer" 906 format: "int64" 907 status: 908 type: "string" 909 description: "Status" 910 Test_v2: 911 description: "This Test has a description" 912 type: "object" 913 properties: 914 id: 915 type: "integer" 916 format: "int64" 917 InvalidInput: 918 type: "string" 919 format: "string" 920 parameters: 921 a: 922 in: query 923 name: a 924 schema: 925 $ref: "#/definitions/Test" 926 a_v2: 927 in: query 928 name: a 929 schema: 930 $ref: "#/definitions/Test_v2" 931 `), &expected)) 932 933 ast := assert.New(t) 934 orig_spec2, _ := cloneSpec(spec2) 935 if !ast.NoError(MergeSpecs(spec1, spec2)) { 936 return 937 } 938 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}, DebugSpec{spec1}.String()) 939 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 940 } 941 942 func TestMergeSpecsRenameModelWithExistingV2InDestination(t *testing.T) { 943 var spec1, spec2, expected *spec.Swagger 944 require.NoError(t, yaml.Unmarshal([]byte(` 945 swagger: "2.0" 946 paths: 947 /test: 948 post: 949 parameters: 950 - name: "body" 951 schema: 952 $ref: "#/definitions/Test" 953 - $ref: "#/parameters/a" 954 /testv2: 955 post: 956 parameters: 957 - name: "body" 958 schema: 959 $ref: "#/definitions/Test_v2" 960 - $ref: "#/parameters/a_v2" 961 definitions: 962 Test: 963 type: "object" 964 Test_v2: 965 description: "This is an existing Test_v2 in destination schema" 966 type: "object" 967 parameters: 968 a: 969 in: query 970 name: a 971 schema: 972 $ref: "#/definitions/Test" 973 a_v2: 974 in: query 975 name: a 976 schema: 977 $ref: "#/definitions/Test_v2" 978 `), &spec1)) 979 980 require.NoError(t, yaml.Unmarshal([]byte(` 981 swagger: "2.0" 982 paths: 983 /othertest: 984 post: 985 parameters: 986 - name: "body" 987 schema: 988 $ref: "#/definitions/Test" 989 - $ref: "#/parameters/a" 990 definitions: 991 Test: 992 description: "This Test has a description" 993 type: "object" 994 parameters: 995 a: 996 in: query 997 name: a 998 schema: 999 $ref: "#/definitions/Test" 1000 `), &spec2)) 1001 1002 require.NoError(t, yaml.Unmarshal([]byte(` 1003 swagger: "2.0" 1004 paths: 1005 /test: 1006 post: 1007 parameters: 1008 - name: "body" 1009 schema: 1010 $ref: "#/definitions/Test" 1011 - $ref: "#/parameters/a" 1012 /testv2: 1013 post: 1014 parameters: 1015 - name: "body" 1016 schema: 1017 $ref: "#/definitions/Test_v2" 1018 - $ref: "#/parameters/a_v2" 1019 /othertest: 1020 post: 1021 parameters: 1022 - name: "body" 1023 schema: 1024 $ref: "#/definitions/Test_v3" 1025 - $ref: "#/parameters/a_v3" 1026 definitions: 1027 Test: 1028 type: "object" 1029 Test_v2: 1030 description: "This is an existing Test_v2 in destination schema" 1031 type: "object" 1032 Test_v3: 1033 description: "This Test has a description" 1034 type: "object" 1035 parameters: 1036 a: 1037 in: query 1038 name: a 1039 schema: 1040 $ref: "#/definitions/Test" 1041 a_v2: 1042 in: query 1043 name: a 1044 schema: 1045 $ref: "#/definitions/Test_v2" 1046 a_v3: 1047 in: query 1048 name: a 1049 schema: 1050 $ref: "#/definitions/Test_v3" 1051 `), &expected)) 1052 1053 ast := assert.New(t) 1054 orig_spec2, _ := cloneSpec(spec2) 1055 if !ast.NoError(MergeSpecs(spec1, spec2)) { 1056 return 1057 } 1058 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 1059 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 1060 } 1061 1062 func TestMergeSpecsMultipleRenamesOfModelsAndLateConflict(t *testing.T) { 1063 var spec1, spec2, expected *spec.Swagger 1064 require.NoError(t, yaml.Unmarshal([]byte(` 1065 swagger: "2.0" 1066 paths: 1067 /test: 1068 post: 1069 parameters: 1070 - name: "body" 1071 schema: 1072 $ref: "#/definitions/Test" 1073 - $ref: "#/parameters/a" 1074 /test3: 1075 post: 1076 parameters: 1077 - name: "body" 1078 schema: 1079 $ref: "#/definitions/Test_v3" 1080 - $ref: "#/parameters/a_v3" 1081 definitions: 1082 Test: 1083 description: "I used to be Test in destination" 1084 type: "object" 1085 Test_v3: 1086 description: "I used to be Test_v3 in destination" 1087 type: "object" 1088 parameters: 1089 a: 1090 in: query 1091 name: a 1092 schema: 1093 $ref: "#/definitions/Test" 1094 a_v3: 1095 in: query 1096 name: a 1097 schema: 1098 $ref: "#/definitions/Test_v3" 1099 `), &spec1)) 1100 1101 require.NoError(t, yaml.Unmarshal([]byte(` 1102 swagger: "2.0" 1103 paths: 1104 /othertest: 1105 post: 1106 parameters: 1107 - name: "body" 1108 schema: 1109 $ref: "#/definitions/Test" 1110 - $ref: "#/parameters/a" 1111 /othertest2: 1112 post: 1113 parameters: 1114 - name: "body" 1115 schema: 1116 $ref: "#/definitions/Test_v2" 1117 - $ref: "#/parameters/a_v2" 1118 definitions: 1119 Test: 1120 description: "I used to be Test in source" 1121 type: "object" 1122 Test_v2: 1123 description: "I used to be Test_v2 in source" 1124 type: "object" 1125 parameters: 1126 a: 1127 in: query 1128 name: a 1129 schema: 1130 $ref: "#/definitions/Test" 1131 a_v2: 1132 in: query 1133 name: a 1134 schema: 1135 $ref: "#/definitions/Test_v2" 1136 `), &spec2)) 1137 1138 require.NoError(t, yaml.Unmarshal([]byte(` 1139 swagger: "2.0" 1140 paths: 1141 /test: 1142 post: 1143 parameters: 1144 - name: "body" 1145 schema: 1146 $ref: "#/definitions/Test" 1147 - $ref: "#/parameters/a" 1148 /test3: 1149 post: 1150 parameters: 1151 - name: "body" 1152 schema: 1153 $ref: "#/definitions/Test_v3" 1154 - $ref: "#/parameters/a_v3" 1155 /othertest2: 1156 post: 1157 parameters: 1158 - name: "body" 1159 schema: 1160 $ref: "#/definitions/Test_v2" 1161 - $ref: "#/parameters/a_v2" 1162 /othertest: 1163 post: 1164 parameters: 1165 - name: "body" 1166 schema: 1167 $ref: "#/definitions/Test_v4" 1168 - $ref: "#/parameters/a_v4" 1169 definitions: 1170 Test: 1171 description: "I used to be Test in destination" 1172 type: "object" 1173 Test_v2: 1174 description: "I used to be Test_v2 in source" 1175 type: "object" 1176 Test_v3: 1177 description: "I used to be Test_v3 in destination" 1178 type: "object" 1179 Test_v4: 1180 description: "I used to be Test in source" 1181 type: "object" 1182 parameters: 1183 a: 1184 in: query 1185 name: a 1186 schema: 1187 $ref: "#/definitions/Test" 1188 a_v2: 1189 in: query 1190 name: a 1191 schema: 1192 $ref: "#/definitions/Test_v2" 1193 a_v3: 1194 in: query 1195 name: a 1196 schema: 1197 $ref: "#/definitions/Test_v3" 1198 a_v4: 1199 in: query 1200 name: a 1201 schema: 1202 $ref: "#/definitions/Test_v4" 1203 `), &expected)) 1204 1205 ast := assert.New(t) 1206 orig_spec2, _ := cloneSpec(spec2) 1207 if !ast.NoError(MergeSpecs(spec1, spec2)) { 1208 return 1209 } 1210 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 1211 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 1212 } 1213 1214 func TestMergeSpecsRenameModelWithExistingV2InSource(t *testing.T) { 1215 var spec1, spec2, expected *spec.Swagger 1216 require.NoError(t, yaml.Unmarshal([]byte(` 1217 swagger: "2.0" 1218 paths: 1219 /test: 1220 post: 1221 parameters: 1222 - name: "body" 1223 schema: 1224 $ref: "#/definitions/Test" 1225 - $ref: "#/parameters/a" 1226 definitions: 1227 Test: 1228 type: "object" 1229 parameters: 1230 a: 1231 in: query 1232 name: a 1233 schema: 1234 $ref: "#/definitions/Test" 1235 `), &spec1)) 1236 1237 require.NoError(t, yaml.Unmarshal([]byte(` 1238 swagger: "2.0" 1239 paths: 1240 /othertest: 1241 post: 1242 parameters: 1243 - name: "body" 1244 schema: 1245 $ref: "#/definitions/Test" 1246 - $ref: "#/parameters/a" 1247 /testv2: 1248 post: 1249 parameters: 1250 - name: "body" 1251 schema: 1252 $ref: "#/definitions/Test_v2" 1253 - $ref: "#/parameters/a_v2" 1254 definitions: 1255 Test: 1256 description: "This Test has a description" 1257 type: "object" 1258 Test_v2: 1259 description: "This is an existing Test_v2 in source schema" 1260 type: "object" 1261 parameters: 1262 a: 1263 in: query 1264 name: a 1265 schema: 1266 $ref: "#/definitions/Test" 1267 a_v2: 1268 in: query 1269 name: a 1270 schema: 1271 $ref: "#/definitions/Test_v2" 1272 `), &spec2)) 1273 1274 require.NoError(t, yaml.Unmarshal([]byte(` 1275 swagger: "2.0" 1276 paths: 1277 /test: 1278 post: 1279 parameters: 1280 - name: "body" 1281 schema: 1282 $ref: "#/definitions/Test" 1283 - $ref: "#/parameters/a" 1284 /testv2: 1285 post: 1286 parameters: 1287 - name: "body" 1288 schema: 1289 $ref: "#/definitions/Test_v2" 1290 - $ref: "#/parameters/a_v2" 1291 /othertest: 1292 post: 1293 parameters: 1294 - name: "body" 1295 schema: 1296 $ref: "#/definitions/Test_v3" 1297 - $ref: "#/parameters/a_v3" 1298 definitions: 1299 Test: 1300 type: "object" 1301 Test_v2: 1302 description: "This is an existing Test_v2 in source schema" 1303 type: "object" 1304 Test_v3: 1305 description: "This Test has a description" 1306 type: "object" 1307 parameters: 1308 a: 1309 in: query 1310 name: a 1311 schema: 1312 $ref: "#/definitions/Test" 1313 a_v2: 1314 in: query 1315 name: a 1316 schema: 1317 $ref: "#/definitions/Test_v2" 1318 a_v3: 1319 in: query 1320 name: a 1321 schema: 1322 $ref: "#/definitions/Test_v3" 1323 `), &expected)) 1324 1325 ast := assert.New(t) 1326 orig_spec2, _ := cloneSpec(spec2) 1327 if !ast.NoError(MergeSpecs(spec1, spec2)) { 1328 return 1329 } 1330 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 1331 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 1332 } 1333 1334 // This tests if there are three specs, where the first two use the same object definition, 1335 // while the third one uses its own. 1336 // We expect the merged schema to contain two versions of the object, not three 1337 func TestTwoMergeSpecsFirstTwoSchemasHaveSameDefinition(t *testing.T) { 1338 var spec1, spec2, spec3, expected *spec.Swagger 1339 yaml.Unmarshal([]byte(` 1340 swagger: "2.0" 1341 paths: 1342 /test: 1343 post: 1344 parameters: 1345 - name: "body" 1346 schema: 1347 $ref: "#/definitions/Test" 1348 definitions: 1349 Test: 1350 description: "spec1 and spec2 use the same object definition, while spec3 doesn't" 1351 type: "object" 1352 `), &spec1) 1353 1354 yaml.Unmarshal([]byte(` 1355 swagger: "2.0" 1356 paths: 1357 /test2: 1358 post: 1359 parameters: 1360 - name: "body" 1361 schema: 1362 $ref: "#/definitions/Test" 1363 definitions: 1364 Test: 1365 description: "spec1 and spec2 use the same object definition, while spec3 doesn't" 1366 type: "object" 1367 `), &spec2) 1368 1369 yaml.Unmarshal([]byte(` 1370 swagger: "2.0" 1371 paths: 1372 /test3: 1373 post: 1374 parameters: 1375 - name: "body" 1376 schema: 1377 $ref: "#/definitions/Test" 1378 definitions: 1379 Test: 1380 description: "spec3 has its own definition (the description doesn't match)" 1381 type: "object" 1382 `), &spec3) 1383 1384 yaml.Unmarshal([]byte(` 1385 swagger: "2.0" 1386 paths: 1387 /test: 1388 post: 1389 parameters: 1390 - name: "body" 1391 schema: 1392 $ref: "#/definitions/Test" 1393 /test2: 1394 post: 1395 parameters: 1396 - name: "body" 1397 schema: 1398 $ref: "#/definitions/Test" 1399 /test3: 1400 post: 1401 parameters: 1402 - name: "body" 1403 schema: 1404 $ref: "#/definitions/Test_v2" 1405 definitions: 1406 Test: 1407 description: "spec1 and spec2 use the same object definition, while spec3 doesn't" 1408 type: "object" 1409 Test_v2: 1410 description: "spec3 has its own definition (the description doesn't match)" 1411 type: "object" 1412 `), &expected) 1413 1414 ast := assert.New(t) 1415 orig_spec2, _ := cloneSpec(spec2) 1416 orig_spec3, _ := cloneSpec(spec3) 1417 if !ast.NoError(MergeSpecs(spec1, spec2)) { 1418 return 1419 } 1420 if !ast.NoError(MergeSpecs(spec1, spec3)) { 1421 return 1422 } 1423 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 1424 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of spec2 input") 1425 ast.Equal(DebugSpec{orig_spec3}, DebugSpec{spec3}, "unexpected mutation of spec3 input") 1426 } 1427 1428 // This tests if there are three specs, where the last two use the same object definition, 1429 // while the first one uses its own. 1430 // We expect the merged schema to contain two versions of the object, not three 1431 func TestTwoMergeSpecsLastTwoSchemasHaveSameDefinition(t *testing.T) { 1432 var spec1, spec2, spec3, expected *spec.Swagger 1433 yaml.Unmarshal([]byte(` 1434 swagger: "2.0" 1435 paths: 1436 /test: 1437 post: 1438 parameters: 1439 - name: "body" 1440 schema: 1441 $ref: "#/definitions/Test" 1442 definitions: 1443 Test: 1444 type: "object" 1445 `), &spec1) 1446 1447 yaml.Unmarshal([]byte(` 1448 swagger: "2.0" 1449 paths: 1450 /othertest: 1451 post: 1452 parameters: 1453 - name: "body" 1454 schema: 1455 $ref: "#/definitions/Test" 1456 definitions: 1457 Test: 1458 description: "spec2 and spec3 use the same object definition, while spec1 doesn't" 1459 type: "object" 1460 `), &spec2) 1461 1462 yaml.Unmarshal([]byte(` 1463 swagger: "2.0" 1464 paths: 1465 /othertest2: 1466 post: 1467 parameters: 1468 - name: "body" 1469 schema: 1470 $ref: "#/definitions/Test" 1471 definitions: 1472 Test: 1473 description: "spec2 and spec3 use the same object definition, while spec1 doesn't" 1474 type: "object" 1475 `), &spec3) 1476 1477 yaml.Unmarshal([]byte(` 1478 swagger: "2.0" 1479 paths: 1480 /test: 1481 post: 1482 parameters: 1483 - name: "body" 1484 schema: 1485 $ref: "#/definitions/Test" 1486 /othertest: 1487 post: 1488 parameters: 1489 - name: "body" 1490 schema: 1491 $ref: "#/definitions/Test_v2" 1492 /othertest2: 1493 post: 1494 parameters: 1495 - name: "body" 1496 schema: 1497 $ref: "#/definitions/Test_v2" 1498 definitions: 1499 Test: 1500 type: "object" 1501 Test_v2: 1502 description: "spec2 and spec3 use the same object definition, while spec1 doesn't" 1503 type: "object" 1504 `), &expected) 1505 1506 ast := assert.New(t) 1507 orig_spec2, _ := cloneSpec(spec2) 1508 orig_spec3, _ := cloneSpec(spec3) 1509 if !ast.NoError(MergeSpecs(spec1, spec2)) { 1510 return 1511 } 1512 if !ast.NoError(MergeSpecs(spec1, spec3)) { 1513 return 1514 } 1515 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 1516 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of spec2 input") 1517 ast.Equal(DebugSpec{orig_spec3}, DebugSpec{spec3}, "unexpected mutation of spec3 input") 1518 1519 } 1520 1521 func TestSafeMergeSpecsSimple(t *testing.T) { 1522 var fooSpec, barSpec, expected *spec.Swagger 1523 yaml.Unmarshal([]byte(` 1524 swagger: "2.0" 1525 paths: 1526 /foo: 1527 post: 1528 summary: "Foo API" 1529 operationId: "fooTest" 1530 parameters: 1531 - in: "body" 1532 name: "body" 1533 description: "foo object" 1534 required: true 1535 schema: 1536 $ref: "#/definitions/Foo" 1537 responses: 1538 200: 1539 description: "OK" 1540 definitions: 1541 Foo: 1542 type: "object" 1543 properties: 1544 id: 1545 type: "integer" 1546 format: "int64" 1547 `), &fooSpec) 1548 1549 yaml.Unmarshal([]byte(` 1550 swagger: "2.0" 1551 paths: 1552 /bar: 1553 post: 1554 summary: "Bar API" 1555 operationId: "barTest" 1556 parameters: 1557 - in: "body" 1558 name: "body" 1559 description: "bar object" 1560 required: true 1561 schema: 1562 $ref: "#/definitions/Bar" 1563 responses: 1564 200: 1565 description: "OK" 1566 definitions: 1567 Bar: 1568 type: "object" 1569 properties: 1570 id: 1571 type: "integer" 1572 format: "int64" 1573 `), &barSpec) 1574 1575 yaml.Unmarshal([]byte(` 1576 swagger: "2.0" 1577 paths: 1578 /foo: 1579 post: 1580 summary: "Foo API" 1581 operationId: "fooTest" 1582 parameters: 1583 - in: "body" 1584 name: "body" 1585 description: "foo object" 1586 required: true 1587 schema: 1588 $ref: "#/definitions/Foo" 1589 responses: 1590 200: 1591 description: "OK" 1592 /bar: 1593 post: 1594 summary: "Bar API" 1595 operationId: "barTest" 1596 parameters: 1597 - in: "body" 1598 name: "body" 1599 description: "bar object" 1600 required: true 1601 schema: 1602 $ref: "#/definitions/Bar" 1603 responses: 1604 200: 1605 description: "OK" 1606 definitions: 1607 Foo: 1608 type: "object" 1609 properties: 1610 id: 1611 type: "integer" 1612 format: "int64" 1613 Bar: 1614 type: "object" 1615 properties: 1616 id: 1617 type: "integer" 1618 format: "int64" 1619 `), &expected) 1620 1621 ast := assert.New(t) 1622 orig_barSpec, err := cloneSpec(barSpec) 1623 if !ast.NoError(err) { 1624 return 1625 } 1626 if !ast.NoError(MergeSpecsFailOnDefinitionConflict(fooSpec, barSpec)) { 1627 return 1628 } 1629 ast.Equal(DebugSpec{expected}, DebugSpec{fooSpec}) 1630 ast.Equal(DebugSpec{orig_barSpec}, DebugSpec{barSpec}, "unexpected mutation of input") 1631 } 1632 1633 func TestSafeMergeSpecsReuseModel(t *testing.T) { 1634 var fooSpec, barSpec, expected *spec.Swagger 1635 if err := yaml.Unmarshal([]byte(` 1636 swagger: "2.0" 1637 paths: 1638 /foo: 1639 post: 1640 summary: "Foo API" 1641 operationId: "fooTest" 1642 parameters: 1643 - in: "body" 1644 name: "body" 1645 description: "foo object" 1646 required: true 1647 schema: 1648 $ref: "#/definitions/Foo" 1649 responses: 1650 200: 1651 description: "OK" 1652 definitions: 1653 Foo: 1654 type: "object" 1655 properties: 1656 id: 1657 type: "integer" 1658 format: "int64" 1659 x-kubernetes-group-version-kind: 1660 - group: group1 1661 version: v1 1662 kind: Foo 1663 - group: group3 1664 version: v1 1665 kind: Foo 1666 `), &fooSpec); err != nil { 1667 t.Fatal(err) 1668 } 1669 1670 if err := yaml.Unmarshal([]byte(` 1671 swagger: "2.0" 1672 paths: 1673 /refoo: 1674 post: 1675 summary: "Refoo API" 1676 operationId: "refooTest" 1677 parameters: 1678 - in: "body" 1679 name: "body" 1680 description: "foo object" 1681 required: true 1682 schema: 1683 $ref: "#/definitions/Foo" 1684 responses: 1685 200: 1686 description: "OK" 1687 definitions: 1688 Foo: 1689 type: "object" 1690 properties: 1691 id: 1692 type: "integer" 1693 format: "int64" 1694 x-kubernetes-group-version-kind: 1695 - group: group2 1696 version: v1 1697 kind: Foo 1698 `), &barSpec); err != nil { 1699 t.Fatal(err) 1700 } 1701 1702 if err := yaml.Unmarshal([]byte(` 1703 swagger: "2.0" 1704 paths: 1705 /foo: 1706 post: 1707 summary: "Foo API" 1708 operationId: "fooTest" 1709 parameters: 1710 - in: "body" 1711 name: "body" 1712 description: "foo object" 1713 required: true 1714 schema: 1715 $ref: "#/definitions/Foo" 1716 responses: 1717 200: 1718 description: "OK" 1719 /refoo: 1720 post: 1721 summary: "Refoo API" 1722 operationId: "refooTest" 1723 parameters: 1724 - in: "body" 1725 name: "body" 1726 description: "foo object" 1727 required: true 1728 schema: 1729 $ref: "#/definitions/Foo" 1730 responses: 1731 200: 1732 description: "OK" 1733 definitions: 1734 Foo: 1735 type: "object" 1736 properties: 1737 id: 1738 type: "integer" 1739 format: "int64" 1740 x-kubernetes-group-version-kind: 1741 - group: group1 1742 version: v1 1743 kind: Foo 1744 - group: group2 1745 version: v1 1746 kind: Foo 1747 - group: group3 1748 version: v1 1749 kind: Foo 1750 `), &expected); err != nil { 1751 t.Fatal(err) 1752 } 1753 1754 ast := assert.New(t) 1755 orig_barSpec, err := cloneSpec(barSpec) 1756 if !ast.NoError(err) { 1757 return 1758 } 1759 if !ast.NoError(MergeSpecsFailOnDefinitionConflict(fooSpec, barSpec)) { 1760 return 1761 } 1762 ast.Equal(DebugSpec{expected}, DebugSpec{fooSpec}) 1763 ast.Equal(DebugSpec{orig_barSpec}, DebugSpec{barSpec}, "unexpected mutation of input") 1764 } 1765 1766 func TestSafeMergeSpecsReuseModelFails(t *testing.T) { 1767 var fooSpec, barSpec, expected *spec.Swagger 1768 yaml.Unmarshal([]byte(` 1769 swagger: "2.0" 1770 paths: 1771 /foo: 1772 post: 1773 summary: "Foo API" 1774 operationId: "fooTest" 1775 parameters: 1776 - in: "body" 1777 name: "body" 1778 description: "foo object" 1779 required: true 1780 schema: 1781 $ref: "#/definitions/Foo" 1782 responses: 1783 200: 1784 description: "OK" 1785 definitions: 1786 Foo: 1787 type: "object" 1788 properties: 1789 id: 1790 type: "integer" 1791 format: "int64" 1792 `), &fooSpec) 1793 1794 yaml.Unmarshal([]byte(` 1795 swagger: "2.0" 1796 paths: 1797 /refoo: 1798 post: 1799 summary: "Refoo API" 1800 operationId: "refooTest" 1801 parameters: 1802 - in: "body" 1803 name: "body" 1804 description: "foo object" 1805 required: true 1806 schema: 1807 $ref: "#/definitions/Foo" 1808 responses: 1809 200: 1810 description: "OK" 1811 definitions: 1812 Foo: 1813 type: "object" 1814 properties: 1815 id: 1816 type: "integer" 1817 format: "int64" 1818 new_field: 1819 type: "string" 1820 `), &barSpec) 1821 1822 yaml.Unmarshal([]byte(` 1823 swagger: "2.0" 1824 paths: 1825 /foo: 1826 post: 1827 summary: "Foo API" 1828 operationId: "fooTest" 1829 parameters: 1830 - in: "body" 1831 name: "body" 1832 description: "foo object" 1833 required: true 1834 schema: 1835 $ref: "#/definitions/Foo" 1836 responses: 1837 200: 1838 description: "OK" 1839 /refoo: 1840 post: 1841 summary: "Refoo API" 1842 operationId: "refooTest" 1843 parameters: 1844 - in: "body" 1845 name: "body" 1846 description: "foo object" 1847 required: true 1848 schema: 1849 $ref: "#/definitions/Foo" 1850 responses: 1851 200: 1852 description: "OK" 1853 definitions: 1854 Foo: 1855 type: "object" 1856 properties: 1857 id: 1858 type: "integer" 1859 format: "int64" 1860 `), &expected) 1861 1862 ast := assert.New(t) 1863 ast.Error(MergeSpecsFailOnDefinitionConflict(fooSpec, barSpec)) 1864 } 1865 1866 func TestMergeSpecsIgnorePathConflicts(t *testing.T) { 1867 var fooSpec, barSpec, expected *spec.Swagger 1868 yaml.Unmarshal([]byte(` 1869 swagger: "2.0" 1870 paths: 1871 /foo: 1872 post: 1873 summary: "Foo API" 1874 operationId: "fooTest" 1875 parameters: 1876 - in: "body" 1877 name: "body" 1878 description: "foo object" 1879 required: true 1880 schema: 1881 $ref: "#/definitions/Foo" 1882 responses: 1883 200: 1884 description: "OK" 1885 definitions: 1886 Foo: 1887 type: "object" 1888 properties: 1889 id: 1890 type: "integer" 1891 format: "int64" 1892 `), &fooSpec) 1893 1894 yaml.Unmarshal([]byte(` 1895 swagger: "2.0" 1896 paths: 1897 /foo: 1898 post: 1899 summary: "Should be ignored" 1900 /bar: 1901 post: 1902 summary: "Bar API" 1903 operationId: "barTest" 1904 parameters: 1905 - in: "body" 1906 name: "body" 1907 description: "bar object" 1908 required: true 1909 schema: 1910 $ref: "#/definitions/Bar" 1911 responses: 1912 200: 1913 description: "OK" 1914 definitions: 1915 Bar: 1916 type: "object" 1917 properties: 1918 id: 1919 type: "integer" 1920 format: "int64" 1921 `), &barSpec) 1922 1923 yaml.Unmarshal([]byte(` 1924 swagger: "2.0" 1925 paths: 1926 /foo: 1927 post: 1928 summary: "Foo API" 1929 operationId: "fooTest" 1930 parameters: 1931 - in: "body" 1932 name: "body" 1933 description: "foo object" 1934 required: true 1935 schema: 1936 $ref: "#/definitions/Foo" 1937 responses: 1938 200: 1939 description: "OK" 1940 /bar: 1941 post: 1942 summary: "Bar API" 1943 operationId: "barTest" 1944 parameters: 1945 - in: "body" 1946 name: "body" 1947 description: "bar object" 1948 required: true 1949 schema: 1950 $ref: "#/definitions/Bar" 1951 responses: 1952 200: 1953 description: "OK" 1954 definitions: 1955 Foo: 1956 type: "object" 1957 properties: 1958 id: 1959 type: "integer" 1960 format: "int64" 1961 Bar: 1962 type: "object" 1963 properties: 1964 id: 1965 type: "integer" 1966 format: "int64" 1967 `), &expected) 1968 1969 ast := assert.New(t) 1970 actual, _ := cloneSpec(fooSpec) 1971 orig_barSpec, _ := cloneSpec(barSpec) 1972 if !ast.Error(MergeSpecs(actual, barSpec)) { 1973 return 1974 } 1975 ast.Equal(DebugSpec{orig_barSpec}, DebugSpec{barSpec}, "unexpected mutation of input") 1976 1977 actual, _ = cloneSpec(fooSpec) 1978 if !ast.NoError(MergeSpecsIgnorePathConflictDeprecated(actual, barSpec)) { 1979 return 1980 } 1981 ast.Equal(DebugSpec{expected}, DebugSpec{actual}) 1982 ast.Equal(DebugSpec{orig_barSpec}, DebugSpec{barSpec}, "unexpected mutation of input") 1983 } 1984 1985 func TestMergeSpecsIgnorePathConflictsAllConflicting(t *testing.T) { 1986 var fooSpec *spec.Swagger 1987 yaml.Unmarshal([]byte(` 1988 swagger: "2.0" 1989 paths: 1990 /foo: 1991 post: 1992 summary: "Foo API" 1993 operationId: "fooTest" 1994 parameters: 1995 - in: "body" 1996 name: "body" 1997 description: "foo object" 1998 required: true 1999 schema: 2000 $ref: "#/definitions/Foo" 2001 responses: 2002 200: 2003 description: "OK" 2004 definitions: 2005 Foo: 2006 type: "object" 2007 properties: 2008 id: 2009 type: "integer" 2010 format: "int64" 2011 `), &fooSpec) 2012 2013 ast := assert.New(t) 2014 foo2Spec, _ := cloneSpec(fooSpec) 2015 actual, _ := cloneSpec(fooSpec) 2016 if !ast.NoError(MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters(actual, foo2Spec)) { 2017 return 2018 } 2019 ast.Equal(DebugSpec{fooSpec}, DebugSpec{actual}) 2020 ast.Equal(DebugSpec{fooSpec}, DebugSpec{foo2Spec}, "unexpected mutation of input") 2021 } 2022 2023 func TestMergeSpecsIgnorePathConflictsWithKubeSpec(t *testing.T) { 2024 ast := assert.New(t) 2025 2026 specs, expected := loadTestData() 2027 sp, specs := specs[0], specs[1:] 2028 2029 origSpecs := make([]*spec.Swagger, len(specs)) 2030 for i := range specs { 2031 cpy, err := cloneSpec(specs[i]) 2032 if err != nil { 2033 t.Fatal(err) 2034 } 2035 ast.NoError(err) 2036 origSpecs[i] = cpy 2037 } 2038 2039 for i := range specs { 2040 if err := MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters(sp, specs[i]); err != nil { 2041 t.Fatalf("merging spec %d failed: %v", i, err) 2042 } 2043 } 2044 2045 ast.Equal(DebugSpec{expected}, DebugSpec{sp}) 2046 2047 for i := range specs { 2048 ast.Equal(DebugSpec{origSpecs[i]}, DebugSpec{specs[i]}, "unexpected mutation of specs[%d]", i) 2049 } 2050 } 2051 2052 func BenchmarkMergeSpecsIgnorePathConflictsWithKubeSpec(b *testing.B) { 2053 b.StopTimer() 2054 b.ReportAllocs() 2055 b.ResetTimer() 2056 2057 specs, _ := loadTestData() 2058 start, specs := specs[0], specs[1:] 2059 2060 for n := 0; n < b.N; n++ { 2061 sp, err := cloneSpec(start) 2062 if err != nil { 2063 b.Fatal(err) 2064 } 2065 2066 b.StartTimer() 2067 for i := range specs { 2068 if err := MergeSpecsIgnorePathConflictRenamingDefinitionsAndParameters(sp, specs[i]); err != nil { 2069 panic(err) 2070 } 2071 } 2072 2073 specBytes, _ := sp.MarshalJSON() 2074 handler.ToProtoBinary(specBytes) 2075 2076 b.StopTimer() 2077 } 2078 } 2079 2080 func TestMergeSpecReplacesAllPossibleRefs(t *testing.T) { 2081 var spec1, spec2, expected *spec.Swagger 2082 yaml.Unmarshal([]byte(` 2083 swagger: "2.0" 2084 paths: 2085 /test: 2086 post: 2087 parameters: 2088 - name: "body" 2089 schema: 2090 $ref: "#/definitions/Test" 2091 definitions: 2092 Test: 2093 type: "object" 2094 properties: 2095 foo: 2096 $ref: "#/definitions/TestProperty" 2097 TestProperty: 2098 type: "object" 2099 `), &spec1) 2100 2101 yaml.Unmarshal([]byte(` 2102 swagger: "2.0" 2103 paths: 2104 /test2: 2105 post: 2106 parameters: 2107 - name: "test2" 2108 schema: 2109 $ref: "#/definitions/Test2" 2110 - name: "test3" 2111 schema: 2112 $ref: "#/definitions/Test3" 2113 - name: "test4" 2114 schema: 2115 $ref: "#/definitions/Test4" 2116 - name: "test5" 2117 schema: 2118 $ref: "#/definitions/Test5" 2119 definitions: 2120 Test2: 2121 $ref: "#/definitions/TestProperty" 2122 Test3: 2123 type: "object" 2124 properties: 2125 withRef: 2126 $ref: "#/definitions/TestProperty" 2127 withAllOf: 2128 type: "object" 2129 allOf: 2130 - $ref: "#/definitions/TestProperty" 2131 - type: object 2132 properties: 2133 test: 2134 $ref: "#/definitions/TestProperty" 2135 withAnyOf: 2136 type: "object" 2137 anyOf: 2138 - $ref: "#/definitions/TestProperty" 2139 - type: object 2140 properties: 2141 test: 2142 $ref: "#/definitions/TestProperty" 2143 withOneOf: 2144 type: "object" 2145 oneOf: 2146 - $ref: "#/definitions/TestProperty" 2147 - type: object 2148 properties: 2149 test: 2150 $ref: "#/definitions/TestProperty" 2151 withNot: 2152 type: "object" 2153 not: 2154 $ref: "#/definitions/TestProperty" 2155 patternProperties: 2156 "prefix.*": 2157 $ref: "#/definitions/TestProperty" 2158 additionalProperties: 2159 $ref: "#/definitions/TestProperty" 2160 definitions: 2161 SomeDefinition: 2162 $ref: "#/definitions/TestProperty" 2163 Test4: 2164 type: "array" 2165 items: 2166 $ref: "#/definitions/TestProperty" 2167 additionalItems: 2168 $ref: "#/definitions/TestProperty" 2169 Test5: 2170 type: "array" 2171 items: 2172 - $ref: "#/definitions/TestProperty" 2173 - $ref: "#/definitions/TestProperty" 2174 TestProperty: 2175 description: "This TestProperty is different from the one in spec1" 2176 type: "object" 2177 `), &spec2) 2178 2179 yaml.Unmarshal([]byte(` 2180 swagger: "2.0" 2181 paths: 2182 /test: 2183 post: 2184 parameters: 2185 - name: "body" 2186 schema: 2187 $ref: "#/definitions/Test" 2188 /test2: 2189 post: 2190 parameters: 2191 - name: "test2" 2192 schema: 2193 $ref: "#/definitions/Test2" 2194 - name: "test3" 2195 schema: 2196 $ref: "#/definitions/Test3" 2197 - name: "test4" 2198 schema: 2199 $ref: "#/definitions/Test4" 2200 - name: "test5" 2201 schema: 2202 $ref: "#/definitions/Test5" 2203 definitions: 2204 Test: 2205 type: "object" 2206 properties: 2207 foo: 2208 $ref: "#/definitions/TestProperty" 2209 TestProperty: 2210 type: "object" 2211 Test2: 2212 $ref: "#/definitions/TestProperty_v2" 2213 Test3: 2214 type: "object" 2215 properties: 2216 withRef: 2217 $ref: "#/definitions/TestProperty_v2" 2218 withAllOf: 2219 type: "object" 2220 allOf: 2221 - $ref: "#/definitions/TestProperty_v2" 2222 - type: object 2223 properties: 2224 test: 2225 $ref: "#/definitions/TestProperty_v2" 2226 withAnyOf: 2227 type: "object" 2228 anyOf: 2229 - $ref: "#/definitions/TestProperty_v2" 2230 - type: object 2231 properties: 2232 test: 2233 $ref: "#/definitions/TestProperty_v2" 2234 withOneOf: 2235 type: "object" 2236 oneOf: 2237 - $ref: "#/definitions/TestProperty_v2" 2238 - type: object 2239 properties: 2240 test: 2241 $ref: "#/definitions/TestProperty_v2" 2242 withNot: 2243 type: "object" 2244 not: 2245 $ref: "#/definitions/TestProperty_v2" 2246 patternProperties: 2247 "prefix.*": 2248 $ref: "#/definitions/TestProperty_v2" 2249 additionalProperties: 2250 $ref: "#/definitions/TestProperty_v2" 2251 definitions: 2252 SomeDefinition: 2253 $ref: "#/definitions/TestProperty_v2" 2254 Test4: 2255 type: "array" 2256 items: 2257 $ref: "#/definitions/TestProperty_v2" 2258 additionalItems: 2259 $ref: "#/definitions/TestProperty_v2" 2260 Test5: 2261 type: "array" 2262 items: 2263 - $ref: "#/definitions/TestProperty_v2" 2264 - $ref: "#/definitions/TestProperty_v2" 2265 TestProperty_v2: 2266 description: "This TestProperty is different from the one in spec1" 2267 type: "object" 2268 `), &expected) 2269 2270 ast := assert.New(t) 2271 orig_spec2, _ := cloneSpec(spec2) 2272 if !ast.NoError(MergeSpecs(spec1, spec2)) { 2273 return 2274 } 2275 ast.Equal(DebugSpec{expected}, DebugSpec{spec1}) 2276 ast.Equal(DebugSpec{orig_spec2}, DebugSpec{spec2}, "unexpected mutation of input") 2277 } 2278 2279 func loadTestData() ([]*spec.Swagger, *spec.Swagger) { 2280 loadSpec := func(fileName string) *spec.Swagger { 2281 bs, err := os.ReadFile(filepath.Join("../../test/integration/testdata/aggregator", fileName)) 2282 if err != nil { 2283 panic(err) 2284 } 2285 sp := spec.Swagger{} 2286 2287 if err := json.Unmarshal(bs, &sp); err != nil { 2288 panic(err) 2289 } 2290 return &sp 2291 } 2292 2293 specs := []*spec.Swagger{ 2294 loadSpec("openapi-0.json"), 2295 loadSpec("openapi-1.json"), 2296 loadSpec("openapi-2.json"), 2297 } 2298 expected := loadSpec("openapi.json") 2299 2300 return specs, expected 2301 } 2302 2303 func TestCloneSpec(t *testing.T) { 2304 _, sp := loadTestData() 2305 clone, err := cloneSpec(sp) 2306 if err != nil { 2307 t.Fatalf("unexpected error: %v", err) 2308 } 2309 ast := assert.New(t) 2310 ast.Equal(DebugSpec{sp}, DebugSpec{clone}) 2311 } 2312 2313 func cloneSpec(source *spec.Swagger) (*spec.Swagger, error) { 2314 bytes, err := source.MarshalJSON() 2315 if err != nil { 2316 return nil, err 2317 } 2318 var ret spec.Swagger 2319 err = json.Unmarshal(bytes, &ret) 2320 if err != nil { 2321 return nil, err 2322 } 2323 return &ret, nil 2324 } 2325 2326 func TestMergedGVKs(t *testing.T) { 2327 gvk1 := map[string]interface{}{"group": "group1", "version": "v1", "kind": "Foo"} 2328 gvk2 := map[string]interface{}{"group": "group2", "version": "v1", "kind": "Bar"} 2329 gvk3 := map[string]interface{}{"group": "group3", "version": "v1", "kind": "Abc"} 2330 gvk4 := map[string]interface{}{"group": "group4", "version": "v1", "kind": "Abc"} 2331 2332 tests := []struct { 2333 name string 2334 gvks1 interface{} 2335 gvks2 interface{} 2336 want interface{} 2337 wantChanged bool 2338 wantErr bool 2339 }{ 2340 {"nil", nil, nil, nil, false, false}, 2341 {"first only", []interface{}{gvk1, gvk2}, nil, []interface{}{gvk1, gvk2}, false, false}, 2342 {"second only", nil, []interface{}{gvk1, gvk2}, []interface{}{gvk1, gvk2}, true, false}, 2343 {"both", []interface{}{gvk1, gvk2}, []interface{}{gvk3}, []interface{}{gvk1, gvk2, gvk3}, true, false}, 2344 {"equal, different order", []interface{}{gvk1, gvk2, gvk3}, []interface{}{gvk3, gvk2, gvk1}, []interface{}{gvk1, gvk2, gvk3}, false, false}, 2345 {"ordered", []interface{}{gvk3, gvk1, gvk4}, []interface{}{gvk2}, []interface{}{gvk1, gvk2, gvk3, gvk4}, true, false}, 2346 {"not ordered when not changed", []interface{}{gvk3, gvk1, gvk4}, []interface{}{}, []interface{}{gvk3, gvk1, gvk4}, false, false}, 2347 {"empty", []interface{}{}, []interface{}{}, []interface{}{}, false, false}, 2348 {"overlapping", []interface{}{gvk1, gvk2}, []interface{}{gvk2, gvk3}, []interface{}{gvk1, gvk2, gvk3}, true, false}, 2349 {"first no slice", 42, []interface{}{gvk1}, nil, false, true}, 2350 {"second no slice", []interface{}{gvk1}, 42, nil, false, true}, 2351 {"no map in slice", []interface{}{42}, []interface{}{gvk1}, nil, false, true}, 2352 } 2353 for _, tt := range tests { 2354 t.Run(tt.name, func(t *testing.T) { 2355 var ext1, ext2 map[string]interface{} 2356 if tt.gvks1 != nil { 2357 ext1 = map[string]interface{}{"x-kubernetes-group-version-kind": tt.gvks1} 2358 } 2359 if tt.gvks2 != nil { 2360 ext2 = map[string]interface{}{"x-kubernetes-group-version-kind": tt.gvks2} 2361 } 2362 2363 got, gotChanged, gotErr := mergedGVKs( 2364 &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: ext1}}, 2365 &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: ext2}}, 2366 ) 2367 if (gotErr != nil) != tt.wantErr { 2368 t.Errorf("mergedGVKs() error = %v, wantErr %v", gotErr, tt.wantErr) 2369 return 2370 } 2371 if gotChanged != tt.wantChanged { 2372 t.Errorf("mergedGVKs() changed = %v, want %v", gotChanged, tt.wantChanged) 2373 } 2374 if !reflect.DeepEqual(got, tt.want) { 2375 t.Errorf("mergedGVKs() got = %v, want %v", got, tt.want) 2376 } 2377 }) 2378 } 2379 } 2380 2381 func TestDeepEqualDefinitionsModuloGVKs(t *testing.T) { 2382 tests := []struct { 2383 name string 2384 s1 *spec.Schema 2385 s2 *spec.Schema 2386 equal bool 2387 }{ 2388 {name: "nil", equal: true}, 2389 {name: "nil, non-nil", s1: nil, s2: &spec.Schema{}}, 2390 {name: "equal", s1: &spec.Schema{}, s2: &spec.Schema{}, equal: true}, 2391 {name: "different", s1: &spec.Schema{SchemaProps: spec.SchemaProps{ID: "abc"}}, s2: &spec.Schema{}}, 2392 {name: "equal modulo: nil, empty", 2393 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: nil}}, 2394 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{}}}, 2395 equal: true, 2396 }, 2397 {name: "equal modulo: nil, gvk", 2398 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: nil}}, 2399 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2400 gvkKey: true, 2401 }}}, 2402 equal: true, 2403 }, 2404 {name: "equal modulo: empty, gvk", 2405 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{}}}, 2406 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2407 gvkKey: true, 2408 }}}, 2409 equal: true, 2410 }, 2411 {name: "equal modulo: non-empty, gvk", 2412 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{"foo": "bar"}}}, 2413 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2414 gvkKey: true, 2415 "foo": "bar", 2416 }}}, 2417 equal: true, 2418 }, 2419 {name: "equal modulo: gvk, gvk", 2420 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2421 gvkKey: false, 2422 "foo": "bar", 2423 }}}, 2424 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2425 gvkKey: true, 2426 "foo": "bar", 2427 }}}, 2428 equal: true, 2429 }, 2430 {name: "different values", 2431 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2432 gvkKey: false, 2433 "foo": "bar", 2434 }}}, 2435 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2436 gvkKey: true, 2437 "foo": "abc", 2438 }}}, 2439 }, 2440 {name: "different sizes", 2441 s1: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2442 gvkKey: false, 2443 "foo": "bar", 2444 "xyz": "123", 2445 }}}, 2446 s2: &spec.Schema{VendorExtensible: spec.VendorExtensible{Extensions: spec.Extensions{ 2447 gvkKey: true, 2448 "foo": "abc", 2449 }}}, 2450 }, 2451 } 2452 for _, tt := range tests { 2453 t.Run(tt.name, func(t *testing.T) { 2454 if got := deepEqualDefinitionsModuloGVKs(tt.s1, tt.s2); got != tt.equal { 2455 t.Errorf("deepEqualDefinitionsModuloGVKs(s1, v2) = %v, want %v", got, tt.equal) 2456 } 2457 2458 if got := deepEqualDefinitionsModuloGVKs(tt.s2, tt.s1); got != tt.equal { 2459 t.Errorf("deepEqualDefinitionsModuloGVKs(s2, s1) = %v, want %v", got, tt.equal) 2460 } 2461 }) 2462 } 2463 }