1// Copyright 2015 go-swagger maintainers 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package analysis 16 17import ( 18 "bytes" 19 "encoding/json" 20 "io/ioutil" 21 "log" 22 "os" 23 "path" 24 "path/filepath" 25 "regexp" 26 "strings" 27 "testing" 28 29 "github.com/go-openapi/jsonpointer" 30 "github.com/go-openapi/spec" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33) 34 35func TestSaveDefinition(t *testing.T) { 36 sp := &spec.Swagger{} 37 saveSchema(sp, "theName", spec.StringProperty()) 38 assert.Contains(t, sp.Definitions, "theName") 39} 40 41func TestNameFromRef(t *testing.T) { 42 values := []struct{ Source, Expected string }{ 43 {"#/definitions/errorModel", "errorModel"}, 44 {"http://somewhere.com/definitions/errorModel", "errorModel"}, 45 {"http://somewhere.com/definitions/errorModel.json", "errorModel"}, 46 {"/definitions/errorModel", "errorModel"}, 47 {"/definitions/errorModel.json", "errorModel"}, 48 {"http://somewhere.com", "somewhereCom"}, 49 {"#", ""}, 50 } 51 52 for _, v := range values { 53 assert.Equal(t, v.Expected, nameFromRef(spec.MustCreateRef(v.Source))) 54 } 55} 56 57func TestDefinitionName(t *testing.T) { 58 values := []struct { 59 Source, Expected string 60 Definitions spec.Definitions 61 }{ 62 {"#/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)}, 63 {"http://somewhere.com/definitions/errorModel", "errorModel", map[string]spec.Schema(nil)}, 64 {"#/definitions/errorModel", "errorModel", map[string]spec.Schema{"apples": *spec.StringProperty()}}, 65 {"#/definitions/errorModel", "errorModelOAIGen", map[string]spec.Schema{"errorModel": *spec.StringProperty()}}, 66 {"#/definitions/errorModel", "errorModelOAIGen1", 67 map[string]spec.Schema{"errorModel": *spec.StringProperty(), "errorModelOAIGen": *spec.StringProperty()}}, 68 {"#", "oaiGen", nil}, 69 } 70 71 for _, v := range values { 72 u, _ := uniqifyName(v.Definitions, nameFromRef(spec.MustCreateRef(v.Source))) 73 assert.Equal(t, v.Expected, u) 74 } 75} 76 77var refFixture = []struct { 78 Key string 79 Ref spec.Ref 80}{ 81 {"#/parameters/someParam/schema", spec.MustCreateRef("#/definitions/record")}, 82 {"#/paths/~1some~1where~1{id}/parameters/1/schema", spec.MustCreateRef("#/definitions/record")}, 83 {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", spec.MustCreateRef("#/definitions/record")}, 84 {"#/responses/someResponse/schema", spec.MustCreateRef("#/definitions/record")}, 85 {"#/paths/~1some~1where~1{id}/get/responses/default/schema", spec.MustCreateRef("#/definitions/record")}, 86 {"#/paths/~1some~1where~1{id}/get/responses/200/schema", spec.MustCreateRef("#/definitions/record")}, 87 {"#/definitions/namedAgain", spec.MustCreateRef("#/definitions/named")}, 88 {"#/definitions/datedTag/allOf/1", spec.MustCreateRef("#/definitions/tag")}, 89 {"#/definitions/datedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, 90 {"#/definitions/datedTaggedRecords/items/1", spec.MustCreateRef("#/definitions/record")}, 91 {"#/definitions/datedTaggedRecords/additionalItems", spec.MustCreateRef("#/definitions/tag")}, 92 {"#/definitions/otherRecords/items", spec.MustCreateRef("#/definitions/record")}, 93 {"#/definitions/tags/additionalProperties", spec.MustCreateRef("#/definitions/tag")}, 94 {"#/definitions/namedThing/properties/name", spec.MustCreateRef("#/definitions/named")}, 95} 96 97func TestUpdateRef(t *testing.T) { 98 bp := filepath.Join("fixtures", "external_definitions.yml") 99 sp, erl := loadSpec(bp) 100 require.NoError(t, erl) 101 102 for _, v := range refFixture { 103 err := updateRef(sp, v.Key, v.Ref) 104 require.NoError(t, err) 105 106 ptr, err := jsonpointer.New(v.Key[1:]) 107 require.NoError(t, err) 108 109 vv, _, err := ptr.Get(sp) 110 require.NoError(t, err) 111 112 switch tv := vv.(type) { 113 case *spec.Schema: 114 assert.Equal(t, v.Ref.String(), tv.Ref.String()) 115 case spec.Schema: 116 assert.Equal(t, v.Ref.String(), tv.Ref.String()) 117 case *spec.SchemaOrBool: 118 assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String()) 119 case *spec.SchemaOrArray: 120 assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String()) 121 default: 122 assert.Fail(t, "unknown type", "got %T", vv) 123 } 124 } 125} 126 127func TestImportExternalReferences(t *testing.T) { 128 log.SetOutput(ioutil.Discard) 129 defer log.SetOutput(os.Stdout) 130 131 // this fixture is the same as external_definitions.yml, but no more 132 // checks if invalid construct is supported (i.e. $ref in parameters items) 133 bp := filepath.Join(".", "fixtures", "external_definitions_valid.yml") 134 sp, err := loadSpec(bp) 135 require.NoError(t, err) 136 137 opts := &FlattenOpts{ 138 Spec: New(sp), 139 BasePath: bp, 140 } 141 // NOTE(fredbi): now we no more expand, but merely resolve and iterate until there is no more ext ref 142 // so calling importExternalReferences is no more idempotent 143 _, err = importExternalReferences(opts) 144 require.NoError(t, err) 145 146 for i, v := range refFixture { 147 // there is 1 notable difference with the updateRef test: 148 if i == 5 { 149 v.Ref = spec.MustCreateRef("#/definitions/tag") 150 } 151 152 ptr, erj := jsonpointer.New(v.Key[1:]) 153 require.NoErrorf(t, erj, "error on jsonpointer.New(%q)", v.Key[1:]) 154 155 vv, _, erg := ptr.Get(sp) 156 require.NoErrorf(t, erg, "error on ptr.Get(p for key=%s)", v.Key[1:]) 157 158 switch tv := vv.(type) { 159 case *spec.Schema: 160 require.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key) 161 case spec.Schema: 162 require.Equal(t, v.Ref.String(), tv.Ref.String(), "for %s", v.Key) 163 case *spec.SchemaOrBool: 164 require.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key) 165 case *spec.SchemaOrArray: 166 require.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "for %s", v.Key) 167 default: 168 require.Fail(t, "unknown type", "got %T", vv) 169 } 170 171 require.Len(t, sp.Definitions, 11) 172 require.Contains(t, sp.Definitions, "tag") 173 require.Contains(t, sp.Definitions, "named") 174 require.Contains(t, sp.Definitions, "record") 175 } 176 177 // check the complete result for clarity 178 bb, _ := json.MarshalIndent(sp, "", " ") 179 assert.JSONEq(t, `{ 180 "swagger": "2.0", 181 "info": { 182 "title": "reference analysis", 183 "version": "0.1.0" 184 }, 185 "paths": { 186 "/other/place": { 187 "$ref": "external/pathItem.yml" 188 }, 189 "/some/where/{id}": { 190 "get": { 191 "parameters": [ 192 { 193 "$ref": "external/parameters.yml#/parameters/limitParam" 194 }, 195 { 196 "type": "array", 197 "items": { 198 "type": "string" 199 }, 200 "name": "other", 201 "in": "query" 202 }, 203 { 204 "name": "body", 205 "in": "body", 206 "schema": { 207 "$ref": "#/definitions/record" 208 } 209 } 210 ], 211 "responses": { 212 "200": { 213 "description": "", 214 "schema": { 215 "$ref": "#/definitions/tag" 216 } 217 }, 218 "404": { 219 "$ref": "external/responses.yml#/responses/notFound" 220 }, 221 "default": { 222 "description": "", 223 "schema": { 224 "$ref": "#/definitions/record" 225 } 226 } 227 } 228 }, 229 "parameters": [ 230 { 231 "$ref": "external/parameters.yml#/parameters/idParam" 232 }, 233 { 234 "name": "bodyId", 235 "in": "body", 236 "schema": { 237 "$ref": "#/definitions/record" 238 } 239 } 240 ] 241 } 242 }, 243 "definitions": { 244 "datedRecords": { 245 "type": "array", 246 "items": [ 247 { 248 "type": "string", 249 "format": "date-time" 250 }, 251 { 252 "$ref": "#/definitions/record" 253 } 254 ] 255 }, 256 "datedTag": { 257 "allOf": [ 258 { 259 "type": "string", 260 "format": "date" 261 }, 262 { 263 "$ref": "#/definitions/tag" 264 } 265 ] 266 }, 267 "datedTaggedRecords": { 268 "type": "array", 269 "items": [ 270 { 271 "type": "string", 272 "format": "date-time" 273 }, 274 { 275 "$ref": "#/definitions/record" 276 } 277 ], 278 "additionalItems": { 279 "$ref": "#/definitions/tag" 280 } 281 }, 282 "named": { 283 "type": "string" 284 }, 285 "namedAgain": { 286 "$ref": "#/definitions/named" 287 }, 288 "namedThing": { 289 "type": "object", 290 "properties": { 291 "name": { 292 "$ref": "#/definitions/named" 293 } 294 } 295 }, 296 "otherRecords": { 297 "type": "array", 298 "items": { 299 "$ref": "#/definitions/record" 300 } 301 }, 302 "record": { 303 "type": "object", 304 "properties": { 305 "createdAt": { 306 "type": "string", 307 "format": "date-time" 308 } 309 } 310 }, 311 "records": { 312 "type": "array", 313 "items": [ 314 { 315 "$ref": "#/definitions/record" 316 } 317 ] 318 }, 319 "tag": { 320 "type": "object", 321 "properties": { 322 "audit": { 323 "$ref": "external/definitions.yml#/definitions/record" 324 }, 325 "id": { 326 "type": "integer", 327 "format": "int64" 328 }, 329 "value": { 330 "type": "string" 331 } 332 } 333 }, 334 "tags": { 335 "type": "object", 336 "additionalProperties": { 337 "$ref": "#/definitions/tag" 338 } 339 } 340 }, 341 "parameters": { 342 "someParam": { 343 "name": "someParam", 344 "in": "body", 345 "schema": { 346 "$ref": "#/definitions/record" 347 } 348 } 349 }, 350 "responses": { 351 "someResponse": { 352 "description": "", 353 "schema": { 354 "$ref": "#/definitions/record" 355 } 356 } 357 } 358 }`, string(bb)) 359 360 // iterate again: this time all external schema $ref's should be reinlined 361 opts.Spec.reload() 362 _, err = importExternalReferences(&FlattenOpts{ 363 Spec: New(sp), 364 BasePath: bp, 365 }) 366 require.NoError(t, err) 367 368 opts.Spec.reload() 369 for _, ref := range opts.Spec.references.schemas { 370 require.True(t, ref.HasFragmentOnly) 371 } 372 373 // now try complete flatten 374 sp = loadOrFail(t, bp) 375 an := New(sp) 376 err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: true}) 377 require.NoError(t, err) 378 379 bbb, _ := json.MarshalIndent(an.spec, "", " ") 380 assert.JSONEq(t, `{ 381 "swagger": "2.0", 382 "info": { 383 "title": "reference analysis", 384 "version": "0.1.0" 385 }, 386 "paths": { 387 "/other/place": { 388 "get": { 389 "description": "Used to see if a codegen can render all the possible parameter variations for a header param", 390 "tags": [ 391 "testcgen" 392 ], 393 "summary": "many model variations", 394 "operationId": "modelOp", 395 "responses": { 396 "default": { 397 "description": "Generic Out" 398 } 399 } 400 } 401 }, 402 "/some/where/{id}": { 403 "get": { 404 "parameters": [ 405 { 406 "type": "integer", 407 "format": "int32", 408 "name": "limit", 409 "in": "query" 410 }, 411 { 412 "type": "array", 413 "items": { 414 "type": "string" 415 }, 416 "name": "other", 417 "in": "query" 418 }, 419 { 420 "name": "body", 421 "in": "body", 422 "schema": { 423 "$ref": "#/definitions/record" 424 } 425 } 426 ], 427 "responses": { 428 "200": { 429 "description": "", 430 "schema": { 431 "$ref": "#/definitions/tag" 432 } 433 }, 434 "404": { 435 "description": "", 436 "schema": { 437 "$ref": "#/definitions/error" 438 } 439 }, 440 "default": { 441 "description": "", 442 "schema": { 443 "$ref": "#/definitions/record" 444 } 445 } 446 } 447 }, 448 "parameters": [ 449 { 450 "type": "integer", 451 "format": "int32", 452 "name": "id", 453 "in": "path" 454 }, 455 { 456 "name": "bodyId", 457 "in": "body", 458 "schema": { 459 "$ref": "#/definitions/record" 460 } 461 } 462 ] 463 } 464 }, 465 "definitions": { 466 "error": { 467 "type": "object", 468 "required": [ 469 "id", 470 "message" 471 ], 472 "properties": { 473 "id": { 474 "type": "integer", 475 "format": "int64", 476 "readOnly": true 477 }, 478 "message": { 479 "type": "string", 480 "readOnly": true 481 } 482 } 483 }, 484 "named": { 485 "type": "string" 486 }, 487 "record": { 488 "type": "object", 489 "properties": { 490 "createdAt": { 491 "type": "string", 492 "format": "date-time" 493 } 494 } 495 }, 496 "tag": { 497 "type": "object", 498 "properties": { 499 "audit": { 500 "$ref": "#/definitions/record" 501 }, 502 "id": { 503 "type": "integer", 504 "format": "int64" 505 }, 506 "value": { 507 "type": "string" 508 } 509 } 510 } 511 } 512 }`, string(bbb)) 513} 514 515func TestRewriteSchemaRef(t *testing.T) { 516 bp := filepath.Join("fixtures", "inline_schemas.yml") 517 sp, err := loadSpec(bp) 518 require.NoError(t, err) 519 520 for i, v := range refFixture { 521 err := rewriteSchemaToRef(sp, v.Key, v.Ref) 522 require.NoError(t, err) 523 524 ptr, err := jsonpointer.New(v.Key[1:]) 525 require.NoError(t, err) 526 527 vv, _, err := ptr.Get(sp) 528 require.NoError(t, err) 529 530 switch tv := vv.(type) { 531 case *spec.Schema: 532 assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) 533 case spec.Schema: 534 assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) 535 case *spec.SchemaOrBool: 536 assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "at %d for %s", i, v.Key) 537 case *spec.SchemaOrArray: 538 assert.Equal(t, v.Ref.String(), tv.Schema.Ref.String(), "at %d for %s", i, v.Key) 539 default: 540 assert.Fail(t, "unknown type", "got %T", vv) 541 } 542 } 543} 544 545func TestSplitKey(t *testing.T) { 546 type KeyFlag uint64 547 548 const ( 549 isOperation KeyFlag = 1 << iota 550 isDefinition 551 isSharedOperationParam 552 isOperationParam 553 isOperationResponse 554 isDefaultResponse 555 isStatusCodeResponse 556 ) 557 558 values := []struct { 559 Key string 560 Flags KeyFlag 561 PathItemRef spec.Ref 562 PathRef spec.Ref 563 Name string 564 }{ 565 { 566 "#/paths/~1some~1where~1{id}/parameters/1/schema", 567 isOperation | isSharedOperationParam, 568 spec.Ref{}, 569 spec.MustCreateRef("#/paths/~1some~1where~1{id}"), 570 "", 571 }, 572 { 573 "#/paths/~1some~1where~1{id}/get/parameters/2/schema", 574 isOperation | isOperationParam, 575 spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), 576 spec.MustCreateRef("#/paths/~1some~1where~1{id}"), 577 "", 578 }, 579 { 580 "#/paths/~1some~1where~1{id}/get/responses/default/schema", 581 isOperation | isOperationResponse | isDefaultResponse, 582 spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), 583 spec.MustCreateRef("#/paths/~1some~1where~1{id}"), 584 "Default", 585 }, 586 { 587 "#/paths/~1some~1where~1{id}/get/responses/200/schema", 588 isOperation | isOperationResponse | isStatusCodeResponse, 589 spec.MustCreateRef("#/paths/~1some~1where~1{id}/GET"), 590 spec.MustCreateRef("#/paths/~1some~1where~1{id}"), 591 "OK", 592 }, 593 { 594 "#/definitions/namedAgain", 595 isDefinition, 596 spec.Ref{}, 597 spec.Ref{}, 598 "namedAgain", 599 }, 600 { 601 "#/definitions/datedRecords/items/1", 602 isDefinition, 603 spec.Ref{}, 604 spec.Ref{}, 605 "datedRecords", 606 }, 607 { 608 "#/definitions/datedRecords/items/1", 609 isDefinition, 610 spec.Ref{}, 611 spec.Ref{}, 612 "datedRecords", 613 }, 614 { 615 "#/definitions/datedTaggedRecords/items/1", 616 isDefinition, 617 spec.Ref{}, 618 spec.Ref{}, 619 "datedTaggedRecords", 620 }, 621 { 622 "#/definitions/datedTaggedRecords/additionalItems", 623 isDefinition, 624 spec.Ref{}, 625 spec.Ref{}, 626 "datedTaggedRecords", 627 }, 628 { 629 "#/definitions/otherRecords/items", 630 isDefinition, 631 spec.Ref{}, 632 spec.Ref{}, 633 "otherRecords", 634 }, 635 { 636 "#/definitions/tags/additionalProperties", 637 isDefinition, 638 spec.Ref{}, 639 spec.Ref{}, 640 "tags", 641 }, 642 { 643 "#/definitions/namedThing/properties/name", 644 isDefinition, 645 spec.Ref{}, 646 spec.Ref{}, 647 "namedThing", 648 }, 649 } 650 651 for i, v := range values { 652 parts := keyParts(v.Key) 653 pref := parts.PathRef() 654 piref := parts.PathItemRef() 655 assert.Equal(t, v.PathRef.String(), pref.String(), "pathRef: %s at %d", v.Key, i) 656 assert.Equal(t, v.PathItemRef.String(), piref.String(), "pathItemRef: %s at %d", v.Key, i) 657 658 if v.Flags&isOperation != 0 { 659 assert.True(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i) 660 } else { 661 assert.False(t, parts.IsOperation(), "isOperation: %s at %d", v.Key, i) 662 } 663 664 if v.Flags&isDefinition != 0 { 665 assert.True(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i) 666 assert.Equal(t, v.Name, parts.DefinitionName(), "definition name: %s at %d", v.Key, i) 667 } else { 668 assert.False(t, parts.IsDefinition(), "isDefinition: %s at %d", v.Key, i) 669 if v.Name != "" { 670 assert.Equal(t, v.Name, parts.ResponseName(), "response name: %s at %d", v.Key, i) 671 } 672 } 673 674 if v.Flags&isOperationParam != 0 { 675 assert.True(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i) 676 } else { 677 assert.False(t, parts.IsOperationParam(), "isOperationParam: %s at %d", v.Key, i) 678 } 679 680 if v.Flags&isSharedOperationParam != 0 { 681 assert.True(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i) 682 } else { 683 assert.False(t, parts.IsSharedOperationParam(), "isSharedOperationParam: %s at %d", v.Key, i) 684 } 685 686 if v.Flags&isOperationResponse != 0 { 687 assert.True(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i) 688 } else { 689 assert.False(t, parts.IsOperationResponse(), "isOperationResponse: %s at %d", v.Key, i) 690 } 691 692 if v.Flags&isDefaultResponse != 0 { 693 assert.True(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i) 694 } else { 695 assert.False(t, parts.IsDefaultResponse(), "isDefaultResponse: %s at %d", v.Key, i) 696 } 697 698 if v.Flags&isStatusCodeResponse != 0 { 699 assert.True(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i) 700 } else { 701 assert.False(t, parts.IsStatusCodeResponse(), "isStatusCodeResponse: %s at %d", v.Key, i) 702 } 703 } 704} 705 706func TestNamesFromKey(t *testing.T) { 707 bp := filepath.Join("fixtures", "inline_schemas.yml") 708 sp, erl := loadSpec(bp) 709 require.NoError(t, erl) 710 711 values := []struct { 712 Key string 713 Names []string 714 }{ 715 {"#/paths/~1some~1where~1{id}/parameters/1/schema", 716 []string{"GetSomeWhereID params body", "PostSomeWhereID params body"}}, 717 {"#/paths/~1some~1where~1{id}/get/parameters/2/schema", []string{"GetSomeWhereID params body"}}, 718 {"#/paths/~1some~1where~1{id}/get/responses/default/schema", []string{"GetSomeWhereID Default body"}}, 719 {"#/paths/~1some~1where~1{id}/get/responses/200/schema", []string{"GetSomeWhereID OK body"}}, 720 {"#/definitions/namedAgain", []string{"namedAgain"}}, 721 {"#/definitions/datedTag/allOf/1", []string{"datedTag allOf 1"}}, 722 {"#/definitions/datedRecords/items/1", []string{"datedRecords tuple 1"}}, 723 {"#/definitions/datedTaggedRecords/items/1", []string{"datedTaggedRecords tuple 1"}}, 724 {"#/definitions/datedTaggedRecords/additionalItems", []string{"datedTaggedRecords tuple additionalItems"}}, 725 {"#/definitions/otherRecords/items", []string{"otherRecords items"}}, 726 {"#/definitions/tags/additionalProperties", []string{"tags additionalProperties"}}, 727 {"#/definitions/namedThing/properties/name", []string{"namedThing name"}}, 728 } 729 730 for i, v := range values { 731 ptr, err := jsonpointer.New(definitionPtr(v.Key)[1:]) 732 require.NoError(t, err) 733 734 vv, _, err := ptr.Get(sp) 735 require.NoError(t, err) 736 737 switch tv := vv.(type) { 738 case *spec.Schema: 739 aschema, err := Schema(SchemaOpts{Schema: tv, Root: sp, BasePath: bp}) 740 if assert.NoError(t, err) { 741 names := namesFromKey(keyParts(v.Key), aschema, opRefsByRef(gatherOperations(New(sp), nil))) 742 assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i) 743 } 744 case spec.Schema: 745 aschema, err := Schema(SchemaOpts{Schema: &tv, Root: sp, BasePath: bp}) 746 if assert.NoError(t, err) { 747 names := namesFromKey(keyParts(v.Key), aschema, opRefsByRef(gatherOperations(New(sp), nil))) 748 assert.Equal(t, v.Names, names, "for %s at %d", v.Key, i) 749 } 750 default: 751 assert.Fail(t, "unknown type", "got %T", vv) 752 } 753 } 754} 755 756func TestDepthFirstSort(t *testing.T) { 757 bp := filepath.Join("fixtures", "inline_schemas.yml") 758 sp, erl := loadSpec(bp) 759 require.NoError(t, erl) 760 761 values := []string{ 762 // Added shared parameters and responses 763 "#/parameters/someParam/schema/properties/createdAt", 764 "#/parameters/someParam/schema", 765 "#/responses/someResponse/schema/properties/createdAt", 766 "#/responses/someResponse/schema", 767 "#/paths/~1some~1where~1{id}/parameters/1/schema/properties/createdAt", 768 "#/paths/~1some~1where~1{id}/parameters/1/schema", 769 "#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/createdAt", 770 "#/paths/~1some~1where~1{id}/get/parameters/2/schema", 771 "#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/id", 772 "#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/value", 773 "#/paths/~1some~1where~1{id}/get/responses/200/schema", 774 "#/paths/~1some~1where~1{id}/get/responses/404/schema", 775 "#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/createdAt", 776 "#/paths/~1some~1where~1{id}/get/responses/default/schema", 777 "#/definitions/datedRecords/items/1/properties/createdAt", 778 "#/definitions/datedTaggedRecords/items/1/properties/createdAt", 779 "#/definitions/namedThing/properties/name/properties/id", 780 "#/definitions/records/items/0/properties/createdAt", 781 "#/definitions/datedTaggedRecords/additionalItems/properties/id", 782 "#/definitions/datedTaggedRecords/additionalItems/properties/value", 783 "#/definitions/otherRecords/items/properties/createdAt", 784 "#/definitions/tags/additionalProperties/properties/id", 785 "#/definitions/tags/additionalProperties/properties/value", 786 "#/definitions/datedRecords/items/0", 787 "#/definitions/datedRecords/items/1", 788 "#/definitions/datedTag/allOf/0", 789 "#/definitions/datedTag/allOf/1", 790 "#/definitions/datedTag/properties/id", 791 "#/definitions/datedTag/properties/value", 792 "#/definitions/datedTaggedRecords/items/0", 793 "#/definitions/datedTaggedRecords/items/1", 794 "#/definitions/namedAgain/properties/id", 795 "#/definitions/namedThing/properties/name", 796 "#/definitions/pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism/properties/" + 797 "floccinaucinihilipilificationCreatedAt", 798 "#/definitions/records/items/0", 799 "#/definitions/datedTaggedRecords/additionalItems", 800 "#/definitions/otherRecords/items", 801 "#/definitions/tags/additionalProperties", 802 "#/definitions/datedRecords", 803 "#/definitions/datedTag", 804 "#/definitions/datedTaggedRecords", 805 "#/definitions/namedAgain", 806 "#/definitions/namedThing", 807 "#/definitions/otherRecords", 808 "#/definitions/pneumonoultramicroscopicsilicovolcanoconiosisAntidisestablishmentarianism", 809 "#/definitions/records", 810 "#/definitions/tags", 811 } 812 813 a := New(sp) 814 result := sortDepthFirst(a.allSchemas) 815 assert.Equal(t, values, result) 816} 817 818func TestBuildNameWithReservedKeyWord(t *testing.T) { 819 s := splitKey([]string{"definitions", "fullview", "properties", "properties"}) 820 startIdx := 2 821 segments := []string{"fullview"} 822 newName := s.BuildName(segments, startIdx, nil) 823 assert.Equal(t, "fullview properties", newName) 824 825 s = splitKey([]string{"definitions", "fullview", 826 "properties", "properties", "properties", "properties", "properties", "properties"}) 827 newName = s.BuildName(segments, startIdx, nil) 828 assert.Equal(t, "fullview properties properties properties", newName) 829} 830 831func TestNameInlinedSchemas(t *testing.T) { 832 values := []struct { 833 Key string 834 Location string 835 Ref spec.Ref 836 }{ 837 {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2/properties/name", 838 "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name", 839 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"), 840 }, 841 {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/1", 842 "#/definitions/getSomeWhereIdParamsBodyRecord/items/1", 843 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"), 844 }, 845 846 {"#/paths/~1some~1where~1{id}/get/parameters/2/schema/properties/record/items/2", 847 "#/definitions/getSomeWhereIdParamsBodyRecord/items/2", 848 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"), 849 }, 850 851 {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2/properties/name", 852 "#/definitions/getSomeWhereIdOKBodyRecordItems2/properties/name", 853 spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2Name"), 854 }, 855 856 {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/1", 857 "#/definitions/getSomeWhereIdOKBodyRecord/items/1", 858 spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems1"), 859 }, 860 861 {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record/items/2", 862 "#/definitions/getSomeWhereIdOKBodyRecord/items/2", 863 spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecordItems2"), 864 }, 865 866 {"#/paths/~1some~1where~1{id}/get/responses/200/schema/properties/record", 867 "#/definitions/getSomeWhereIdOKBody/properties/record", 868 spec.MustCreateRef("#/definitions/getSomeWhereIdOKBodyRecord"), 869 }, 870 871 {"#/paths/~1some~1where~1{id}/get/responses/200/schema", 872 "#/paths/~1some~1where~1{id}/get/responses/200/schema", 873 spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"), 874 }, 875 876 {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2/properties/name", 877 "#/definitions/getSomeWhereIdDefaultBodyRecordItems2/properties/name", 878 spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2Name"), 879 }, 880 881 {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/1", 882 "#/definitions/getSomeWhereIdDefaultBodyRecord/items/1", 883 spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems1"), 884 }, 885 886 {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record/items/2", 887 "#/definitions/getSomeWhereIdDefaultBodyRecord/items/2", 888 spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecordItems2"), 889 }, 890 891 {"#/paths/~1some~1where~1{id}/get/responses/default/schema/properties/record", 892 "#/definitions/getSomeWhereIdDefaultBody/properties/record", 893 spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBodyRecord"), 894 }, 895 896 {"#/paths/~1some~1where~1{id}/get/responses/default/schema", 897 "#/paths/~1some~1where~1{id}/get/responses/default/schema", 898 spec.MustCreateRef("#/definitions/getSomeWhereIdDefaultBody"), 899 }, 900 // maps: 901 // {"#/definitions/nestedThing/properties/record/items/2/allOf/1/additionalProperties", 902 // "#/definitions/nestedThingRecordItems2AllOf1/additionalProperties", 903 // spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1AdditionalProperties"), 904 // }, 905 906 // {"#/definitions/nestedThing/properties/record/items/2/allOf/1", 907 // "#/definitions/nestedThingRecordItems2/allOf/1", 908 // spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"), 909 // }, 910 {"#/definitions/nestedThing/properties/record/items/2/properties/name", 911 "#/definitions/nestedThingRecordItems2/properties/name", 912 spec.MustCreateRef("#/definitions/nestedThingRecordItems2Name"), 913 }, 914 915 {"#/definitions/nestedThing/properties/record/items/1", 916 "#/definitions/nestedThingRecord/items/1", 917 spec.MustCreateRef("#/definitions/nestedThingRecordItems1"), 918 }, 919 920 {"#/definitions/nestedThing/properties/record/items/2", 921 "#/definitions/nestedThingRecord/items/2", 922 spec.MustCreateRef("#/definitions/nestedThingRecordItems2"), 923 }, 924 925 {"#/definitions/datedRecords/items/1", 926 "#/definitions/datedRecords/items/1", 927 spec.MustCreateRef("#/definitions/datedRecordsItems1"), 928 }, 929 930 {"#/definitions/datedTaggedRecords/items/1", 931 "#/definitions/datedTaggedRecords/items/1", 932 spec.MustCreateRef("#/definitions/datedTaggedRecordsItems1"), 933 }, 934 935 {"#/definitions/namedThing/properties/name", 936 "#/definitions/namedThing/properties/name", 937 spec.MustCreateRef("#/definitions/namedThingName"), 938 }, 939 940 {"#/definitions/nestedThing/properties/record", 941 "#/definitions/nestedThing/properties/record", 942 spec.MustCreateRef("#/definitions/nestedThingRecord"), 943 }, 944 945 {"#/definitions/records/items/0", 946 "#/definitions/records/items/0", 947 spec.MustCreateRef("#/definitions/recordsItems0"), 948 }, 949 950 {"#/definitions/datedTaggedRecords/additionalItems", 951 "#/definitions/datedTaggedRecords/additionalItems", 952 spec.MustCreateRef("#/definitions/datedTaggedRecordsItemsAdditionalItems"), 953 }, 954 955 {"#/definitions/otherRecords/items", 956 "#/definitions/otherRecords/items", 957 spec.MustCreateRef("#/definitions/otherRecordsItems"), 958 }, 959 960 {"#/definitions/tags/additionalProperties", 961 "#/definitions/tags/additionalProperties", 962 spec.MustCreateRef("#/definitions/tagsAdditionalProperties"), 963 }, 964 } 965 966 bp := filepath.Join("fixtures", "nested_inline_schemas.yml") 967 sp := loadOrFail(t, bp) 968 969 require.NoError(t, spec.ExpandSpec(sp, &spec.ExpandOptions{ 970 RelativeBase: bp, 971 SkipSchemas: true, 972 })) 973 974 require.NoError(t, nameInlinedSchemas(&FlattenOpts{ 975 Spec: New(sp), 976 BasePath: bp, 977 })) 978 979 for i, v := range values { 980 ptr, err := jsonpointer.New(v.Location[1:]) 981 require.NoErrorf(t, err, "at %d for %s", i, v.Key) 982 983 vv, _, err := ptr.Get(sp) 984 require.NoErrorf(t, err, "at %d for %s", i, v.Key) 985 986 switch tv := vv.(type) { 987 case *spec.Schema: 988 assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) 989 case spec.Schema: 990 assert.Equal(t, v.Ref.String(), tv.Ref.String(), "at %d for %s", i, v.Key) 991 case *spec.SchemaOrBool: 992 var sRef spec.Ref 993 if tv != nil && tv.Schema != nil { 994 sRef = tv.Schema.Ref 995 } 996 assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) 997 case *spec.SchemaOrArray: 998 var sRef spec.Ref 999 if tv != nil && tv.Schema != nil { 1000 sRef = tv.Schema.Ref 1001 } 1002 assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) 1003 default: 1004 assert.Fail(t, "unknown type", "got %T", vv) 1005 } 1006 } 1007 1008 for k, rr := range New(sp).allSchemas { 1009 if strings.HasPrefix(k, "#/responses") || strings.HasPrefix(k, "#/parameters") { 1010 continue 1011 } 1012 if rr.Schema == nil || rr.Schema.Ref.String() != "" || rr.TopLevel { 1013 continue 1014 } 1015 asch, err := Schema(SchemaOpts{Schema: rr.Schema, Root: sp, BasePath: bp}) 1016 require.NoErrorf(t, err, "for key: %s", k) 1017 1018 if !asch.IsSimpleSchema && !asch.IsArray && !asch.IsMap { 1019 assert.Fail(t, "not a top level schema", "for key: %s", k) 1020 } 1021 } 1022} 1023 1024func TestFlatten(t *testing.T) { 1025 cwd, _ := os.Getwd() 1026 bp := filepath.Join(cwd, "fixtures", "flatten.yml") 1027 sp, erl := loadSpec(bp) 1028 require.NoError(t, erl) 1029 1030 values := []struct { 1031 Key string 1032 Location string 1033 Ref spec.Ref 1034 Expected interface{} 1035 }{ 1036 { 1037 "#/responses/notFound/schema", 1038 "#/responses/notFound/schema", 1039 spec.MustCreateRef("#/definitions/error"), 1040 nil, 1041 }, 1042 { 1043 "#/paths/~1some~1where~1{id}/parameters/0", 1044 "#/paths/~1some~1where~1{id}/parameters/0/name", 1045 spec.Ref{}, 1046 "id", 1047 }, 1048 { 1049 "#/paths/~1other~1place", 1050 "#/paths/~1other~1place/get/operationId", 1051 spec.Ref{}, 1052 "modelOp", 1053 }, 1054 { 1055 "#/paths/~1some~1where~1{id}/get/parameters/0", 1056 "#/paths/~1some~1where~1{id}/get/parameters/0/name", 1057 spec.Ref{}, 1058 "limit", 1059 }, 1060 { 1061 "#/paths/~1some~1where~1{id}/get/parameters/1", 1062 "#/paths/~1some~1where~1{id}/get/parameters/1/name", 1063 spec.Ref{}, 1064 "some", 1065 }, 1066 { 1067 "#/paths/~1some~1where~1{id}/get/parameters/2", 1068 "#/paths/~1some~1where~1{id}/get/parameters/2/name", 1069 spec.Ref{}, 1070 "other", 1071 }, 1072 { 1073 "#/paths/~1some~1where~1{id}/get/parameters/3", 1074 "#/paths/~1some~1where~1{id}/get/parameters/3/schema", 1075 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBody"), 1076 "", 1077 }, 1078 { 1079 "#/paths/~1some~1where~1{id}/get/responses/200", 1080 "#/paths/~1some~1where~1{id}/get/responses/200/schema", 1081 spec.MustCreateRef("#/definitions/getSomeWhereIdOKBody"), 1082 "", 1083 }, 1084 { 1085 "#/definitions/namedAgain", 1086 "", 1087 spec.MustCreateRef("#/definitions/named"), 1088 "", 1089 }, 1090 { 1091 "#/definitions/namedThing/properties/name", 1092 "", 1093 spec.MustCreateRef("#/definitions/named"), 1094 "", 1095 }, 1096 { 1097 "#/definitions/namedThing/properties/namedAgain", 1098 "", 1099 spec.MustCreateRef("#/definitions/namedAgain"), 1100 "", 1101 }, 1102 { 1103 "#/definitions/datedRecords/items/1", 1104 "", 1105 spec.MustCreateRef("#/definitions/record"), 1106 "", 1107 }, 1108 { 1109 "#/definitions/otherRecords/items", 1110 "", 1111 spec.MustCreateRef("#/definitions/record"), 1112 "", 1113 }, 1114 { 1115 "#/definitions/tags/additionalProperties", 1116 "", 1117 spec.MustCreateRef("#/definitions/tag"), 1118 "", 1119 }, 1120 { 1121 "#/definitions/datedTag/allOf/1", 1122 "", 1123 spec.MustCreateRef("#/definitions/tag"), 1124 "", 1125 }, 1126 /* Maps are now considered simple schemas 1127 { 1128 "#/definitions/nestedThingRecordItems2/allOf/1", 1129 "", 1130 spec.MustCreateRef("#/definitions/nestedThingRecordItems2AllOf1"), 1131 "", 1132 }, 1133 */ 1134 { 1135 "#/definitions/nestedThingRecord/items/1", 1136 "", 1137 spec.MustCreateRef("#/definitions/nestedThingRecordItems1"), 1138 "", 1139 }, 1140 { 1141 "#/definitions/nestedThingRecord/items/2", 1142 "", 1143 spec.MustCreateRef("#/definitions/nestedThingRecordItems2"), 1144 "", 1145 }, 1146 { 1147 "#/definitions/nestedThing/properties/record", 1148 "", 1149 spec.MustCreateRef("#/definitions/nestedThingRecord"), 1150 "", 1151 }, 1152 { 1153 "#/definitions/named", 1154 "#/definitions/named/type", 1155 spec.Ref{}, 1156 spec.StringOrArray{"string"}, 1157 }, 1158 { 1159 "#/definitions/error", 1160 "#/definitions/error/properties/id/type", 1161 spec.Ref{}, 1162 spec.StringOrArray{"integer"}, 1163 }, 1164 { 1165 "#/definitions/record", 1166 "#/definitions/record/properties/createdAt/format", 1167 spec.Ref{}, 1168 "date-time", 1169 }, 1170 { 1171 "#/definitions/getSomeWhereIdOKBody", 1172 "#/definitions/getSomeWhereIdOKBody/properties/record", 1173 spec.MustCreateRef("#/definitions/nestedThing"), 1174 nil, 1175 }, 1176 { 1177 "#/definitions/getSomeWhereIdParamsBody", 1178 "#/definitions/getSomeWhereIdParamsBody/properties/record", 1179 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecord"), 1180 nil, 1181 }, 1182 { 1183 "#/definitions/getSomeWhereIdParamsBodyRecord", 1184 "#/definitions/getSomeWhereIdParamsBodyRecord/items/1", 1185 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems1"), 1186 nil, 1187 }, 1188 { 1189 "#/definitions/getSomeWhereIdParamsBodyRecord", 1190 "#/definitions/getSomeWhereIdParamsBodyRecord/items/2", 1191 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2"), 1192 nil, 1193 }, 1194 { 1195 "#/definitions/getSomeWhereIdParamsBodyRecordItems2", 1196 "#/definitions/getSomeWhereIdParamsBodyRecordItems2/allOf/0/format", 1197 spec.Ref{}, 1198 "date", 1199 }, 1200 { 1201 "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name", 1202 "#/definitions/getSomeWhereIdParamsBodyRecordItems2Name/properties/createdAt/format", 1203 spec.Ref{}, 1204 "date-time", 1205 }, 1206 { 1207 "#/definitions/getSomeWhereIdParamsBodyRecordItems2", 1208 "#/definitions/getSomeWhereIdParamsBodyRecordItems2/properties/name", 1209 spec.MustCreateRef("#/definitions/getSomeWhereIdParamsBodyRecordItems2Name"), 1210 "date", 1211 }, 1212 } 1213 1214 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp})) 1215 1216 for i, v := range values { 1217 pk := v.Key[1:] 1218 if v.Location != "" { 1219 pk = v.Location[1:] 1220 } 1221 1222 ptr, err := jsonpointer.New(pk) 1223 require.NoError(t, err, "at %d for %s", i, v.Key) 1224 1225 d, _, err := ptr.Get(sp) 1226 require.NoError(t, err) 1227 1228 if v.Ref.String() == "" { 1229 assert.Equal(t, v.Expected, d) 1230 continue 1231 } 1232 1233 switch s := d.(type) { 1234 case *spec.Schema: 1235 assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key) 1236 case spec.Schema: 1237 assert.Equal(t, v.Ref.String(), s.Ref.String(), "at %d for %s", i, v.Key) 1238 case *spec.SchemaOrArray: 1239 var sRef spec.Ref 1240 if s != nil && s.Schema != nil { 1241 sRef = s.Schema.Ref 1242 } 1243 assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) 1244 case *spec.SchemaOrBool: 1245 var sRef spec.Ref 1246 if s != nil && s.Schema != nil { 1247 sRef = s.Schema.Ref 1248 } 1249 assert.Equal(t, v.Ref.String(), sRef.String(), "at %d for %s", i, v.Key) 1250 default: 1251 assert.Fail(t, "unknown type", "got %T at %d for %s", d, i, v.Key) 1252 } 1253 } 1254} 1255 1256func TestFlatten_oaigenFull(t *testing.T) { 1257 var sp *spec.Swagger 1258 defer func() { 1259 if t.Failed() && sp != nil { 1260 bbb, _ := json.MarshalIndent(sp, "", " ") 1261 t.Logf("%s", string(bbb)) 1262 } 1263 }() 1264 1265 cwd, _ := os.Getwd() 1266 bp := filepath.Join(cwd, "fixtures", "oaigen", "fixture-oaigen.yaml") 1267 sp, erl := loadSpec(bp) 1268 require.NoError(t, erl) 1269 1270 var logCapture bytes.Buffer 1271 log.SetOutput(&logCapture) 1272 defer log.SetOutput(os.Stdout) 1273 1274 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false})) 1275 1276 msg := logCapture.String() 1277 1278 if !assert.Containsf(t, msg, "warning: duplicate flattened definition name resolved as aAOAIGen", 1279 "Expected log message") { 1280 t.Logf("Captured log: %s", msg) 1281 } 1282 if !assert.NotContainsf(t, msg, "warning: duplicate flattened definition name resolved as uniqueName2OAIGen", 1283 "Expected log message") { 1284 t.Logf("Captured log: %s", msg) 1285 } 1286 1287 res := getInPath(t, sp, "/some/where", "/get/responses/204/schema") 1288 assert.JSONEqf(t, `{"$ref": "#/definitions/uniqueName1"}`, res, "Expected a simple schema for response") 1289 1290 res = getInPath(t, sp, "/some/where", "/post/responses/204/schema") 1291 assert.JSONEqf(t, `{"$ref": "#/definitions/d"}`, res, "Expected a simple schema for response") 1292 1293 res = getInPath(t, sp, "/some/where", "/get/responses/206/schema") 1294 assert.JSONEqf(t, `{"$ref": "#/definitions/a"}`, res, "Expected a simple schema for response") 1295 1296 res = getInPath(t, sp, "/some/where", "/get/responses/304/schema") 1297 assert.JSONEqf(t, `{"$ref": "#/definitions/transitive11"}`, res, "Expected a simple schema for response") 1298 1299 res = getInPath(t, sp, "/some/where", "/get/responses/205/schema") 1300 assert.JSONEqf(t, `{"$ref": "#/definitions/b"}`, res, "Expected a simple schema for response") 1301 1302 res = getInPath(t, sp, "/some/where", "/post/responses/200/schema") 1303 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response") 1304 1305 res = getInPath(t, sp, "/some/where", "/post/responses/default/schema") 1306 // pointer expanded 1307 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response") 1308 1309 res = getDefinition(t, sp, "a") 1310 assert.JSONEqf(t, 1311 `{"type": "object", "properties": { "a": { "$ref": "#/definitions/aAOAIGen" }}}`, 1312 res, "Expected a simple schema for response") 1313 1314 res = getDefinition(t, sp, "aA") 1315 assert.JSONEqf(t, `{"type": "string", "format": "date"}`, res, "Expected a simple schema for response") 1316 1317 res = getDefinition(t, sp, "aAOAIGen") 1318 assert.JSONEqf(t, `{ 1319 "type": "object", 1320 "properties": { 1321 "b": { 1322 "type": "integer" 1323 }}, 1324 "x-go-gen-location": "models"}`, res, "Expected a simple schema for response") 1325 1326 res = getDefinition(t, sp, "bB") 1327 assert.JSONEqf(t, `{"type": "string", "format": "date-time"}`, res, "Expected a simple schema for response") 1328 1329 _, ok := sp.Definitions["bItems"] 1330 assert.Falsef(t, ok, "Did not expect a definition for %s", "bItems") 1331 1332 res = getDefinition(t, sp, "d") 1333 assert.JSONEqf(t, `{ 1334 "type": "object", 1335 "properties": { 1336 "c": { 1337 "type": "integer" 1338 } 1339 } 1340 }`, res, "Expected a simple schema for response") 1341 1342 res = getDefinition(t, sp, "b") 1343 assert.JSONEqf(t, `{ 1344 "type": "array", 1345 "items": { 1346 "$ref": "#/definitions/d" 1347 } 1348 }`, res, "Expected a ref in response") 1349 1350 res = getDefinition(t, sp, "myBody") 1351 assert.JSONEqf(t, `{ 1352 "type": "object", 1353 "properties": { 1354 "aA": { 1355 "$ref": "#/definitions/aA" 1356 }, 1357 "prop1": { 1358 "type": "integer" 1359 } 1360 } 1361 }`, res, "Expected a simple schema for response") 1362 1363 res = getDefinition(t, sp, "uniqueName2") 1364 assert.JSONEqf(t, `{"$ref": "#/definitions/notUniqueName2"}`, res, "Expected a simple schema for response") 1365 1366 res = getDefinition(t, sp, "notUniqueName2") 1367 assert.JSONEqf(t, `{ 1368 "type": "object", 1369 "properties": { 1370 "prop6": { 1371 "type": "integer" 1372 } 1373 } 1374 }`, res, "Expected a simple schema for response") 1375 1376 res = getDefinition(t, sp, "uniqueName1") 1377 assert.JSONEqf(t, `{ 1378 "type": "object", 1379 "properties": { 1380 "prop5": { 1381 "type": "integer" 1382 }}}`, res, "Expected a simple schema for response") 1383 1384 // allOf container: []spec.Schema 1385 res = getDefinition(t, sp, "getWithSliceContainerDefaultBody") 1386 assert.JSONEqf(t, `{ 1387 "allOf": [ 1388 { 1389 "$ref": "#/definitions/uniqueName3" 1390 }, 1391 { 1392 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1" 1393 } 1394 ], 1395 "x-go-gen-location": "operations" 1396 }`, res, "Expected a simple schema for response") 1397 1398 res = getDefinition(t, sp, "getWithSliceContainerDefaultBodyAllOf1") 1399 assert.JSONEqf(t, `{ 1400 "type": "object", 1401 "properties": { 1402 "prop8": { 1403 "type": "string" 1404 } 1405 }, 1406 "x-go-gen-location": "models" 1407 }`, res, "Expected a simple schema for response") 1408 1409 res = getDefinition(t, sp, "getWithTupleContainerDefaultBody") 1410 assert.JSONEqf(t, `{ 1411 "type": "array", 1412 "items": [ 1413 { 1414 "$ref": "#/definitions/uniqueName3" 1415 }, 1416 { 1417 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1" 1418 } 1419 ], 1420 "x-go-gen-location": "operations" 1421 }`, res, "Expected a simple schema for response") 1422 1423 // with container SchemaOrArray 1424 res = getDefinition(t, sp, "getWithTupleConflictDefaultBody") 1425 assert.JSONEqf(t, `{ 1426 "type": "array", 1427 "items": [ 1428 { 1429 "$ref": "#/definitions/uniqueName4" 1430 }, 1431 { 1432 "$ref": "#/definitions/getWithTupleConflictDefaultBodyItems1" 1433 } 1434 ], 1435 "x-go-gen-location": "operations" 1436 }`, res, "Expected a simple schema for response") 1437 1438 res = getDefinition(t, sp, "getWithTupleConflictDefaultBodyItems1") 1439 assert.JSONEqf(t, `{ 1440 "type": "object", 1441 "properties": { 1442 "prop10": { 1443 "type": "string" 1444 } 1445 }, 1446 "x-go-gen-location": "models" 1447 }`, res, "Expected a simple schema for response") 1448} 1449 1450func TestFlatten_oaigenMinimal(t *testing.T) { 1451 var sp *spec.Swagger 1452 defer func() { 1453 if t.Failed() && sp != nil { 1454 bbb, _ := json.MarshalIndent(sp, "", " ") 1455 t.Logf("%s", string(bbb)) 1456 } 1457 }() 1458 1459 cwd, _ := os.Getwd() 1460 bp := filepath.Join(cwd, "fixtures", "oaigen", "fixture-oaigen.yaml") 1461 sp, erl := loadSpec(bp) 1462 require.NoError(t, erl) 1463 1464 var logCapture bytes.Buffer 1465 log.SetOutput(&logCapture) 1466 defer log.SetOutput(os.Stdout) 1467 1468 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 1469 1470 msg := logCapture.String() 1471 if !assert.NotContainsf(t, msg, 1472 "warning: duplicate flattened definition name resolved as aAOAIGen", "Expected log message") { 1473 t.Logf("Captured log: %s", msg) 1474 } 1475 if !assert.NotContainsf(t, msg, 1476 "warning: duplicate flattened definition name resolved as uniqueName2OAIGen", "Expected log message") { 1477 t.Logf("Captured log: %s", msg) 1478 } 1479 res := getInPath(t, sp, "/some/where", "/get/responses/204/schema") 1480 assert.JSONEqf(t, `{"$ref": "#/definitions/uniqueName1"}`, res, "Expected a simple schema for response") 1481 1482 res = getInPath(t, sp, "/some/where", "/post/responses/204/schema") 1483 assert.JSONEqf(t, `{"$ref": "#/definitions/d"}`, res, "Expected a simple schema for response") 1484 1485 res = getInPath(t, sp, "/some/where", "/get/responses/206/schema") 1486 assert.JSONEqf(t, `{"$ref": "#/definitions/a"}`, res, "Expected a simple schema for response") 1487 1488 res = getInPath(t, sp, "/some/where", "/get/responses/304/schema") 1489 assert.JSONEqf(t, `{"$ref": "#/definitions/transitive11"}`, res, "Expected a simple schema for response") 1490 1491 res = getInPath(t, sp, "/some/where", "/get/responses/205/schema") 1492 assert.JSONEqf(t, `{"$ref": "#/definitions/b"}`, res, "Expected a simple schema for response") 1493 1494 res = getInPath(t, sp, "/some/where", "/post/responses/200/schema") 1495 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response") 1496 1497 res = getInPath(t, sp, "/some/where", "/post/responses/default/schema") 1498 // This JSON pointer is expanded 1499 assert.JSONEqf(t, `{"type": "integer"}`, res, "Expected a simple schema for response") 1500 1501 res = getDefinition(t, sp, "aA") 1502 assert.JSONEqf(t, `{"type": "string", "format": "date"}`, res, "Expected a simple schema for response") 1503 1504 res = getDefinition(t, sp, "a") 1505 assert.JSONEqf(t, `{ 1506 "type": "object", 1507 "properties": { 1508 "a": { 1509 "type": "object", 1510 "properties": { 1511 "b": { 1512 "type": "integer" 1513 } 1514 } 1515 } 1516 } 1517 }`, res, "Expected a simple schema for response") 1518 1519 res = getDefinition(t, sp, "bB") 1520 assert.JSONEqf(t, `{"type": "string", "format": "date-time"}`, res, "Expected a simple schema for response") 1521 1522 _, ok := sp.Definitions["bItems"] 1523 assert.Falsef(t, ok, "Did not expect a definition for %s", "bItems") 1524 1525 res = getDefinition(t, sp, "d") 1526 assert.JSONEqf(t, `{ 1527 "type": "object", 1528 "properties": { 1529 "c": { 1530 "type": "integer" 1531 } 1532 } 1533 }`, res, "Expected a simple schema for response") 1534 1535 res = getDefinition(t, sp, "b") 1536 assert.JSONEqf(t, `{ 1537 "type": "array", 1538 "items": { 1539 "$ref": "#/definitions/d" 1540 } 1541 }`, res, "Expected a ref in response") 1542 1543 res = getDefinition(t, sp, "myBody") 1544 assert.JSONEqf(t, `{ 1545 "type": "object", 1546 "properties": { 1547 "aA": { 1548 "$ref": "#/definitions/aA" 1549 }, 1550 "prop1": { 1551 "type": "integer" 1552 } 1553 } 1554 }`, res, "Expected a simple schema for response") 1555 1556 res = getDefinition(t, sp, "uniqueName2") 1557 assert.JSONEqf(t, `{"$ref": "#/definitions/notUniqueName2"}`, res, "Expected a simple schema for response") 1558 1559 // with allOf container: []spec.Schema 1560 res = getInPath(t, sp, "/with/slice/container", "/get/responses/default/schema") 1561 assert.JSONEqf(t, `{ 1562 "allOf": [ 1563 { 1564 "$ref": "#/definitions/uniqueName3" 1565 }, 1566 { 1567 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1" 1568 } 1569 ] 1570 }`, res, "Expected a simple schema for response") 1571 1572 // with tuple container 1573 res = getInPath(t, sp, "/with/tuple/container", "/get/responses/default/schema") 1574 assert.JSONEqf(t, `{ 1575 "type": "array", 1576 "items": [ 1577 { 1578 "$ref": "#/definitions/uniqueName3" 1579 }, 1580 { 1581 "$ref": "#/definitions/getWithSliceContainerDefaultBodyAllOf1" 1582 } 1583 ] 1584 }`, res, "Expected a simple schema for response") 1585 1586 // with SchemaOrArray container 1587 res = getInPath(t, sp, "/with/tuple/conflict", "/get/responses/default/schema") 1588 assert.JSONEqf(t, `{ 1589 "type": "array", 1590 "items": [ 1591 { 1592 "$ref": "#/definitions/uniqueName4" 1593 }, 1594 { 1595 "type": "object", 1596 "properties": { 1597 "prop10": { 1598 "type": "string" 1599 } 1600 } 1601 } 1602 ] 1603 }`, res, "Expected a simple schema for response") 1604} 1605 1606func assertNoOAIGen(t *testing.T, bp string, sp *spec.Swagger) (success bool) { 1607 var logCapture bytes.Buffer 1608 log.SetOutput(&logCapture) 1609 defer log.SetOutput(os.Stdout) 1610 1611 defer func() { 1612 success = !t.Failed() 1613 }() 1614 1615 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false})) 1616 1617 msg := logCapture.String() 1618 assert.NotContains(t, msg, "warning") 1619 1620 for k := range sp.Definitions { 1621 require.NotContains(t, k, "OAIGen") 1622 } 1623 1624 return 1625} 1626 1627func TestFlatten_oaigen_1260(t *testing.T) { 1628 // test fixture from issue go-swagger/go-swagger#1260 1629 bp := filepath.Join("fixtures", "oaigen", "test3-swagger.yaml") 1630 sp := loadOrFail(t, bp) 1631 assert.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here") 1632} 1633 1634func TestFlatten_oaigen_1260bis(t *testing.T) { 1635 // test fixture from issue go-swagger/go-swagger#1260 1636 bp := filepath.Join("fixtures", "oaigen", "test3-bis-swagger.yaml") 1637 sp := loadOrFail(t, bp) 1638 assert.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here") 1639} 1640 1641func TestFlatten_oaigen_1260ter(t *testing.T) { 1642 // test fixture from issue go-swagger/go-swagger#1260 1643 bp := filepath.Join("fixtures", "oaigen", "test3-ter-swagger.yaml") 1644 sp := loadOrFail(t, bp) 1645 assert.Truef(t, assertNoOAIGen(t, bp, sp), "did not expect an OAIGen definition here") 1646} 1647 1648func getDefinition(t *testing.T, sp *spec.Swagger, key string) string { 1649 d, ok := sp.Definitions[key] 1650 require.Truef(t, ok, "Expected definition for %s", key) 1651 res, _ := json.Marshal(d) 1652 return string(res) 1653} 1654 1655func getInPath(t *testing.T, sp *spec.Swagger, path, key string) string { 1656 ptr, erp := jsonpointer.New(key) 1657 require.NoError(t, erp, "at %s no key", key) 1658 1659 d, _, erg := ptr.Get(sp.Paths.Paths[path]) 1660 require.NoError(t, erg, "at %s no value for %s", path, key) 1661 1662 res, _ := json.Marshal(d) 1663 return string(res) 1664} 1665 1666func TestMoreNameInlinedSchemas(t *testing.T) { 1667 bp := filepath.Join("fixtures", "more_nested_inline_schemas.yml") 1668 sp := loadOrFail(t, bp) 1669 1670 err := Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}) 1671 require.NoError(t, err) 1672 1673 res := getInPath(t, sp, "/some/where/{id}", "/post/responses/200/schema") 1674 assert.JSONEqf(t, 1675 `{"type": "object", "additionalProperties":`+ 1676 `{ "type": "object", "additionalProperties": { "type": "object", "additionalProperties":`+ 1677 ` { "$ref":`+ 1678 ` "#/definitions/postSomeWhereIdOKBodyAdditionalPropertiesAdditionalPropertiesAdditionalProperties"}}}}`, 1679 res, "Expected a simple schema for response") 1680 1681 res = getInPath(t, sp, "/some/where/{id}", "/post/responses/204/schema") 1682 assert.JSONEqf(t, `{ 1683 "type": "object", 1684 "additionalProperties": { 1685 "type": "array", 1686 "items": { 1687 "type": "object", 1688 "additionalProperties": { 1689 "type": "array", 1690 "items": { 1691 "type": "object", 1692 "additionalProperties": { 1693 "type": "array", 1694 "items": { 1695 "$ref":`+ 1696 `"#/definitions/`+ 1697 `postSomeWhereIdNoContentBodyAdditionalPropertiesItemsAdditionalPropertiesItemsAdditionalPropertiesItems" 1698 } 1699 } 1700 } 1701 } 1702 } 1703 } 1704 }`, res, "Expected a simple schema for response") 1705 1706} 1707 1708func TestRemoveUnused(t *testing.T) { 1709 bp := filepath.Join("fixtures", "oaigen", "fixture-oaigen.yaml") 1710 sp := loadOrFail(t, bp) 1711 1712 err := Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: false, Minimal: true, RemoveUnused: true}) 1713 require.NoError(t, err) 1714 1715 assert.Nil(t, sp.Parameters) 1716 assert.Nil(t, sp.Responses) 1717 1718 bp = filepath.Join("fixtures", "parameters", "fixture-parameters.yaml") 1719 sp = loadOrFail(t, bp) 1720 an := New(sp) 1721 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: true, RemoveUnused: true})) 1722 1723 assert.Nil(t, sp.Parameters) 1724 assert.Nil(t, sp.Responses) 1725 1726 op, ok := an.OperationFor("GET", "/some/where") 1727 assert.True(t, ok) 1728 assert.Lenf(t, op.Parameters, 4, "Expected 4 parameters expanded for this operation") 1729 assert.Lenf(t, an.ParamsFor("GET", "/some/where"), 7, 1730 "Expected 7 parameters (with default) expanded for this operation") 1731 1732 op, ok = an.OperationFor("PATCH", "/some/remote") 1733 assert.True(t, ok) 1734 assert.Lenf(t, op.Parameters, 1, "Expected 1 parameter expanded for this operation") 1735 assert.Lenf(t, an.ParamsFor("PATCH", "/some/remote"), 2, 1736 "Expected 2 parameters (with default) expanded for this operation") 1737 1738 _, ok = sp.Definitions["unused"] 1739 assert.False(t, ok, "Did not expect to find #/definitions/unused") 1740 1741 bp = filepath.Join("fixtures", "parameters", "fixture-parameters.yaml") 1742 sp, erl := loadSpec(bp) 1743 require.NoError(t, erl) 1744 1745 var logCapture bytes.Buffer 1746 log.SetOutput(&logCapture) 1747 defer log.SetOutput(os.Stdout) 1748 1749 require.NoError(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: true})) 1750 1751 msg := logCapture.String() 1752 if !assert.Containsf(t, msg, "info: removing unused definition: unused", "Expected log message") { 1753 t.Logf("Captured log: %s", msg) 1754 } 1755 1756 assert.Nil(t, sp.Parameters) 1757 assert.Nil(t, sp.Responses) 1758 _, ok = sp.Definitions["unused"] 1759 assert.Falsef(t, ok, "Did not expect to find #/definitions/unused") 1760} 1761 1762func TestOperationIDs(t *testing.T) { 1763 bp := filepath.Join("fixtures", "operations", "fixture-operations.yaml") 1764 sp := loadOrFail(t, bp) 1765 1766 an := New(sp) 1767 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, RemoveUnused: false})) 1768 1769 res := gatherOperations(New(sp), []string{"getSomeWhere", "getSomeWhereElse"}) 1770 _, ok := res["getSomeWhere"] 1771 assert.Truef(t, ok, "Expected to find operation") 1772 _, ok = res["getSomeWhereElse"] 1773 assert.Truef(t, ok, "Expected to find operation") 1774 _, ok = res["postSomeWhere"] 1775 assert.Falsef(t, ok, "Did not expect to find operation") 1776 1777 op, ok := an.OperationFor("GET", "/some/where/else") 1778 assert.True(t, ok) 1779 assert.NotNil(t, op) 1780 assert.Len(t, an.ParametersFor("getSomeWhereElse"), 2) 1781 1782 op, ok = an.OperationFor("POST", "/some/where/else") 1783 assert.True(t, ok) 1784 assert.NotNil(t, op) 1785 assert.Len(t, an.ParametersFor("postSomeWhereElse"), 1) 1786 1787 op, ok = an.OperationFor("PUT", "/some/where/else") 1788 assert.True(t, ok) 1789 assert.NotNil(t, op) 1790 assert.Len(t, an.ParametersFor("putSomeWhereElse"), 1) 1791 1792 op, ok = an.OperationFor("PATCH", "/some/where/else") 1793 assert.True(t, ok) 1794 assert.NotNil(t, op) 1795 assert.Len(t, an.ParametersFor("patchSomeWhereElse"), 1) 1796 1797 op, ok = an.OperationFor("DELETE", "/some/where/else") 1798 assert.True(t, ok) 1799 assert.NotNil(t, op) 1800 assert.Len(t, an.ParametersFor("deleteSomeWhereElse"), 1) 1801 1802 op, ok = an.OperationFor("HEAD", "/some/where/else") 1803 assert.True(t, ok) 1804 assert.NotNil(t, op) 1805 assert.Len(t, an.ParametersFor("headSomeWhereElse"), 1) 1806 1807 op, ok = an.OperationFor("OPTIONS", "/some/where/else") 1808 assert.True(t, ok) 1809 assert.NotNil(t, op) 1810 assert.Len(t, an.ParametersFor("optionsSomeWhereElse"), 1) 1811 1812 assert.Len(t, an.ParametersFor("outOfThisWorld"), 0) 1813} 1814 1815func TestFlatten_Pointers(t *testing.T) { 1816 var sp *spec.Swagger 1817 1818 defer func() { 1819 if t.Failed() && sp != nil { 1820 bbb, _ := json.MarshalIndent(sp, "", " ") 1821 t.Logf("%s", string(bbb)) 1822 } 1823 }() 1824 1825 bp := filepath.Join("fixtures", "pointers", "fixture-pointers.yaml") 1826 sp = loadOrFail(t, bp) 1827 1828 var logCapture bytes.Buffer 1829 log.SetOutput(&logCapture) 1830 defer log.SetOutput(os.Stdout) 1831 1832 an := New(sp) 1833 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 1834 1835 msg := logCapture.String() 1836 require.NotContainsf(t, msg, "warning", msg) 1837 1838 // re-analyse and check all $ref's point to #/definitions 1839 bn := New(sp) 1840 for _, r := range bn.AllRefs() { 1841 assert.True(t, path.Dir(r.String()) == definitionsPath) 1842 } 1843} 1844 1845// unit test guards in flatten not easily testable with actual specs 1846func TestFlatten_ErrorHandling(t *testing.T) { 1847 const wantedFailure = "Expected a failure" 1848 bp := filepath.Join("fixtures", "errors", "fixture-unexpandable.yaml") 1849 1850 // invalid spec expansion 1851 sp := loadOrFail(t, bp) 1852 1853 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: true}), wantedFailure) 1854 1855 // reload original spec 1856 sp = loadOrFail(t, bp) 1857 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: false}), wantedFailure) 1858 1859 bp = filepath.Join("fixtures", "errors", "fixture-unexpandable-2.yaml") 1860 sp = loadOrFail(t, bp) 1861 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Expand: false}), wantedFailure) 1862 1863 // reload original spec 1864 sp = loadOrFail(t, bp) 1865 require.Errorf(t, Flatten(FlattenOpts{Spec: New(sp), BasePath: bp, Minimal: true, Expand: false}), wantedFailure) 1866 1867 // reload original spec 1868 sp = loadOrFail(t, bp) 1869 require.Errorf(t, rewriteSchemaToRef(sp, "#/invalidPointer/key", spec.Ref{}), wantedFailure) 1870 1871 require.Errorf(t, rewriteParentRef(sp, "#/invalidPointer/key", spec.Ref{}), wantedFailure) 1872 1873 require.Errorf(t, updateRef(sp, "#/invalidPointer/key", spec.Ref{}), wantedFailure) 1874 1875 require.Errorf(t, updateRefWithSchema(sp, "#/invalidPointer/key", &spec.Schema{}), wantedFailure) 1876 1877 _, _, err := getPointerFromKey(sp, "#/invalidPointer/key") 1878 require.Errorf(t, err, wantedFailure) 1879 1880 _, _, err = getPointerFromKey(sp, "--->#/invalidJsonPointer") 1881 require.Errorf(t, err, wantedFailure) 1882 1883 _, _, _, err = getParentFromKey(sp, "#/invalidPointer/key") 1884 require.Errorf(t, err, wantedFailure) 1885 1886 _, _, _, err = getParentFromKey(sp, "--->#/invalidJsonPointer") 1887 require.Errorf(t, err, wantedFailure) 1888 1889 assert.NotPanics(t, saveNilSchema) 1890} 1891 1892func saveNilSchema() { 1893 cwd, _ := os.Getwd() 1894 bp := filepath.Join(cwd, "fixtures", "errors", "fixture-unexpandable-2.yaml") 1895 sp, _ := loadSpec(bp) 1896 saveSchema(sp, "ThisNilSchema", nil) 1897} 1898 1899func TestFlatten_UnitGuards(t *testing.T) { 1900 parts := keyParts("#/nowhere/arbitrary/pointer") 1901 res := genLocation(parts) 1902 assert.Equal(t, "", res) 1903 1904 res = parts.DefinitionName() 1905 assert.Equal(t, "", res) 1906 1907 res = parts.ResponseName() 1908 assert.Equal(t, "", res) 1909 1910 b := parts.isKeyName(-1) 1911 assert.False(t, b) 1912 1913} 1914 1915func TestFlatten_PointersLoop(t *testing.T) { 1916 log.SetOutput(ioutil.Discard) 1917 defer log.SetOutput(os.Stdout) 1918 1919 bp := filepath.Join("fixtures", "pointers", "fixture-pointers-loop.yaml") 1920 sp := loadOrFail(t, bp) 1921 1922 an := New(sp) 1923 require.Error(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 1924} 1925 1926func TestFlatten_Bitbucket(t *testing.T) { 1927 log.SetOutput(ioutil.Discard) 1928 defer log.SetOutput(os.Stdout) 1929 1930 bp := filepath.Join("fixtures", "bugs", "bitbucket.json") 1931 sp := loadOrFail(t, bp) 1932 1933 an := New(sp) 1934 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 1935 1936 // reload original spec 1937 sp = loadOrFail(t, bp) 1938 an = New(sp) 1939 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false})) 1940 1941 // reload original spec 1942 sp = loadOrFail(t, bp) 1943 an = New(sp) 1944 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: false})) 1945 1946 // reload original spec 1947 sp = loadOrFail(t, bp) 1948 an = New(sp) 1949 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: true})) 1950 1951 assert.Len(t, sp.Definitions, 2) // only 2 remaining refs after expansion: circular $ref 1952 _, ok := sp.Definitions["base_commit"] 1953 assert.True(t, ok) 1954 _, ok = sp.Definitions["repository"] 1955 assert.True(t, ok) 1956} 1957 1958func TestFlatten_Issue_1602(t *testing.T) { 1959 log.SetOutput(ioutil.Discard) 1960 defer log.SetOutput(os.Stdout) 1961 1962 // $ref as schema to #/responses or #/parameters 1963 1964 // minimal repro test case 1965 bp := filepath.Join("fixtures", "bugs", "1602", "fixture-1602-1.yaml") 1966 sp := loadOrFail(t, bp) 1967 an := New(sp) 1968 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 1969 RemoveUnused: false})) 1970 1971 // reload spec 1972 sp = loadOrFail(t, bp) 1973 an = New(sp) 1974 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, Expand: false, 1975 RemoveUnused: false})) 1976 1977 // reload spec 1978 // with prior expansion, a pseudo schema is produced 1979 sp = loadOrFail(t, bp) 1980 an = New(sp) 1981 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: false, Expand: true, 1982 RemoveUnused: false})) 1983 1984 // full testcase 1985 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-full.yaml") 1986 sp = loadOrFail(t, bp) 1987 an = New(sp) 1988 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: false, Minimal: true, Expand: false, 1989 RemoveUnused: false})) 1990 1991 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-1.yaml") 1992 sp = loadOrFail(t, bp) 1993 an = New(sp) 1994 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 1995 RemoveUnused: false})) 1996 1997 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-2.yaml") 1998 sp = loadOrFail(t, bp) 1999 an = New(sp) 2000 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2001 RemoveUnused: false})) 2002 2003 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-3.yaml") 2004 sp = loadOrFail(t, bp) 2005 an = New(sp) 2006 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2007 RemoveUnused: false})) 2008 2009 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-4.yaml") 2010 sp = loadOrFail(t, bp) 2011 an = New(sp) 2012 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2013 RemoveUnused: false})) 2014 2015 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-5.yaml") 2016 sp = loadOrFail(t, bp) 2017 an = New(sp) 2018 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2019 RemoveUnused: false})) 2020 2021 bp = filepath.Join("fixtures", "bugs", "1602", "fixture-1602-6.yaml") 2022 sp = loadOrFail(t, bp) 2023 an = New(sp) 2024 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2025 RemoveUnused: false})) 2026} 2027 2028func TestFlatten_Issue_1614(t *testing.T) { 2029 var logCapture bytes.Buffer 2030 log.SetOutput(&logCapture) 2031 defer log.SetOutput(os.Stdout) 2032 2033 // $ref as schema to #/responses or #/parameters 2034 // test warnings 2035 2036 bp := filepath.Join("fixtures", "bugs", "1614", "gitea.yaml") 2037 sp := loadOrFail(t, bp) 2038 an := New(sp) 2039 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2040 RemoveUnused: false})) 2041 2042 msg := logCapture.String() 2043 require.Containsf(t, msg, `warning: found $ref "#/responses/empty" (response) interpreted as schema`, 2044 "Expected log message. Captured log: %s", msg) 2045 2046 require.Containsf(t, msg, `warning: found $ref "#/responses/forbidden" (response) interpreted as schema`, 2047 "Expected log message. Captured log: %s", msg) 2048 2049 // check responses subject to warning have been expanded 2050 bbb, _ := json.Marshal(sp) 2051 assert.NotContains(t, string(bbb), `#/responses/forbidden`) 2052 assert.NotContains(t, string(bbb), `#/responses/empty`) 2053} 2054 2055func TestFlatten_Issue_1621(t *testing.T) { 2056 // repeated remote refs 2057 2058 // minimal repro test case 2059 bp := filepath.Join("fixtures", "bugs", "1621", "fixture-1621.yaml") 2060 sp := loadOrFail(t, bp) 2061 an := New(sp) 2062 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2063 RemoveUnused: false})) 2064 2065 sch1 := sp.Paths.Paths["/v4/users/"].Get.Responses.StatusCodeResponses[200].Schema 2066 bbb, _ := json.Marshal(sch1) 2067 assert.JSONEq(t, `{ 2068 "type": "array", 2069 "items": { 2070 "$ref": "#/definitions/v4UserListItem" 2071 } 2072 }`, string(bbb)) 2073 2074 sch2 := sp.Paths.Paths["/v4/user/"].Get.Responses.StatusCodeResponses[200].Schema 2075 bbb, _ = json.Marshal(sch2) 2076 assert.JSONEq(t, `{ 2077 "$ref": "#/definitions/v4UserListItem" 2078 }`, string(bbb)) 2079 2080 sch3 := sp.Paths.Paths["/v4/users/{email}/"].Get.Responses.StatusCodeResponses[200].Schema 2081 bbb, _ = json.Marshal(sch3) 2082 assert.JSONEq(t, `{ 2083 "$ref": "#/definitions/v4UserListItem" 2084 }`, string(bbb)) 2085} 2086 2087func Test_NormalizePath(t *testing.T) { 2088 values := []struct{ Source, Expected string }{ 2089 {"#/definitions/A", "#/definitions/A"}, 2090 {"http://somewhere.com/definitions/A", "http://somewhere.com/definitions/A"}, 2091 {wrapWindowsPath("/definitions/A"), wrapWindowsPath("/definitions/A")}, // considered absolute on unix but not on windows 2092 {wrapWindowsPath("/definitions/errorModel.json") + "#/definitions/A", wrapWindowsPath("/definitions/errorModel.json") + "#/definitions/A"}, 2093 {"http://somewhere.com", "http://somewhere.com"}, 2094 {wrapWindowsPath("./definitions/definitions.yaml") + "#/definitions/A", wrapWindowsPath("/abs/to/spec/definitions/definitions.yaml") + "#/definitions/A"}, 2095 {"#", wrapWindowsPath("/abs/to/spec")}, 2096 } 2097 2098 for _, v := range values { 2099 assert.Equal(t, v.Expected, normalizePath(spec.MustCreateRef(v.Source), 2100 &FlattenOpts{BasePath: wrapWindowsPath("/abs/to/spec/spec.json")})) 2101 } 2102} 2103 2104func TestFlatten_Issue_1796(t *testing.T) { 2105 var sp *spec.Swagger 2106 defer func() { 2107 if t.Failed() && sp != nil { 2108 bbb, _ := json.MarshalIndent(sp, "", " ") 2109 t.Logf("%s", string(bbb)) 2110 } 2111 }() 2112 2113 // remote cyclic ref 2114 bp := filepath.Join("fixtures", "bugs", "1796", "queryIssue.json") 2115 sp = loadOrFail(t, bp) 2116 an := New(sp) 2117 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2118 RemoveUnused: false})) 2119 2120 // assert all $ref match "$ref": "#/definitions/something" 2121 for _, ref := range an.AllReferences() { 2122 assert.True(t, strings.HasPrefix(ref, "#/definitions")) 2123 } 2124} 2125 2126func TestFlatten_Issue_1767(t *testing.T) { 2127 // remote cyclic ref again 2128 bp := filepath.Join("fixtures", "bugs", "1767", "fixture-1767.yaml") 2129 sp := loadOrFail(t, bp) 2130 an := New(sp) 2131 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, Expand: false, 2132 RemoveUnused: false})) 2133 2134 // assert all $ref match "$ref": "#/definitions/something" 2135 for _, ref := range an.AllReferences() { 2136 assert.True(t, strings.HasPrefix(ref, "#/definitions")) 2137 } 2138} 2139 2140func TestFlatten_Issue_1774(t *testing.T) { 2141 var sp *spec.Swagger 2142 defer func() { 2143 if t.Failed() && sp != nil { 2144 bbb, _ := json.MarshalIndent(sp, "", " ") 2145 t.Logf("%s", string(bbb)) 2146 } 2147 }() 2148 2149 // remote cyclic ref again 2150 bp := filepath.Join("fixtures", "bugs", "1774", "def_api.yaml") 2151 sp = loadOrFail(t, bp) 2152 an := New(sp) 2153 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, Expand: false, 2154 RemoveUnused: false})) 2155 2156 // assert all $ref match "$ref": "#/definitions/something" 2157 for _, ref := range an.AllReferences() { 2158 assert.True(t, strings.HasPrefix(ref, "#/definitions")) 2159 } 2160} 2161 2162func TestFlatten_1429(t *testing.T) { 2163 // nested / remote $ref in response / param schemas 2164 // issue go-swagger/go-swagger#1429 2165 bp := filepath.Join("fixtures", "bugs", "1429", "swagger.yaml") 2166 sp := loadOrFail(t, bp) 2167 2168 an := New(sp) 2169 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 2170} 2171 2172func TestRebaseRef(t *testing.T) { 2173 assert.Equal(t, "#/definitions/abc", rebaseRef("#/definitions/base", "#/definitions/abc")) 2174 assert.Equal(t, "#/definitions/abc", rebaseRef("", "#/definitions/abc")) 2175 assert.Equal(t, "#/definitions/abc", rebaseRef(".", "#/definitions/abc")) 2176 assert.Equal(t, "otherfile#/definitions/abc", rebaseRef("file#/definitions/base", "otherfile#/definitions/abc")) 2177 assert.Equal(t, wrapWindowsPath("../otherfile")+"#/definitions/abc", rebaseRef(wrapWindowsPath("../file")+"#/definitions/base", wrapWindowsPath("./otherfile")+"#/definitions/abc")) 2178 assert.Equal(t, wrapWindowsPath("../otherfile")+"#/definitions/abc", rebaseRef(wrapWindowsPath("../file")+"#/definitions/base", wrapWindowsPath("otherfile")+"#/definitions/abc")) 2179 assert.Equal(t, wrapWindowsPath("local/remote/otherfile")+"#/definitions/abc", rebaseRef(wrapWindowsPath("local/file")+"#/definitions/base", wrapWindowsPath("remote/otherfile")+"#/definitions/abc")) 2180 assert.Equal(t, wrapWindowsPath("local/remote/otherfile.yaml"), rebaseRef(wrapWindowsPath("local/file.yaml"), wrapWindowsPath("remote/otherfile.yaml"))) 2181 2182 assert.Equal(t, "file#/definitions/abc", rebaseRef("file#/definitions/base", "#/definitions/abc")) 2183 2184 // with remote 2185 assert.Equal(t, "https://example.com/base#/definitions/abc", rebaseRef("https://example.com/base", "https://example.com/base#/definitions/abc")) 2186 assert.Equal(t, "https://example.com/base#/definitions/abc", rebaseRef("https://example.com/base", "#/definitions/abc")) 2187 assert.Equal(t, "https://example.com/base#/dir/definitions/abc", rebaseRef("https://example.com/base", "#/dir/definitions/abc")) 2188 assert.Equal(t, "https://example.com/base/dir/definitions/abc", rebaseRef("https://example.com/base/spec.yaml", "dir/definitions/abc")) 2189 assert.Equal(t, "https://example.com/base/dir/definitions/abc", rebaseRef("https://example.com/base/", "dir/definitions/abc")) 2190 assert.Equal(t, "https://example.com/dir/definitions/abc", rebaseRef("https://example.com/base", "dir/definitions/abc")) 2191} 2192 2193func TestFlatten_1851(t *testing.T) { 2194 // nested / remote $ref in response / param schemas 2195 // issue go-swagger/go-swagger#1851 2196 bp := filepath.Join("fixtures", "bugs", "1851", "fixture-1851.yaml") 2197 sp := loadOrFail(t, bp) 2198 2199 an := New(sp) 2200 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 2201 2202 var jazon []byte 2203 2204 serverDefinition, ok := an.spec.Definitions["server"] 2205 assert.True(t, ok) 2206 serverStatusDefinition, ok := an.spec.Definitions["serverStatus"] 2207 assert.True(t, ok) 2208 serverStatusProperty, ok := serverDefinition.Properties["Status"] 2209 assert.True(t, ok) 2210 jazon, _ = json.Marshal(serverStatusProperty) 2211 assert.JSONEq(t, `{"$ref": "#/definitions/serverStatus"}`, string(jazon)) 2212 jazon, _ = json.Marshal(serverStatusDefinition) 2213 assert.JSONEq(t, `{ 2214 "type": "string", 2215 "enum": [ 2216 "OK", 2217 "Not OK" 2218 ] 2219 }`, string(jazon)) 2220 2221 // additional test case: this one used to work 2222 bp = filepath.Join("fixtures", "bugs", "1851", "fixture-1851-2.yaml") 2223 sp = loadOrFail(t, bp) 2224 2225 an = New(sp) 2226 require.NoError(t, Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false})) 2227 2228 serverDefinition, ok = an.spec.Definitions["Server"] 2229 assert.True(t, ok) 2230 serverStatusDefinition, ok = an.spec.Definitions["ServerStatus"] 2231 assert.True(t, ok) 2232 serverStatusProperty, ok = serverDefinition.Properties["Status"] 2233 assert.True(t, ok) 2234 jazon, _ = json.Marshal(serverStatusProperty) 2235 assert.JSONEq(t, `{"$ref": "#/definitions/ServerStatus"}`, string(jazon)) 2236 jazon, _ = json.Marshal(serverStatusDefinition) 2237 assert.JSONEq(t, `{ 2238 "type": "string", 2239 "enum": [ 2240 "OK", 2241 "Not OK" 2242 ] 2243 }`, string(jazon)) 2244} 2245 2246var ( 2247 rex = regexp.MustCompile(`"\$ref":\s*"(.+)"`) 2248 oairex = regexp.MustCompile(`oiagen`) 2249) 2250 2251func checkRefs(t *testing.T, spec *spec.Swagger, expectNoConflict bool) { 2252 // all $ref resolve locally 2253 jazon, _ := json.MarshalIndent(spec, "", " ") 2254 m := rex.FindAllStringSubmatch(string(jazon), -1) 2255 require.NotNil(t, m) 2256 for _, matched := range m { 2257 subMatch := matched[1] 2258 assert.True(t, strings.HasPrefix(subMatch, "#/definitions/"), 2259 "expected $ref to be inlined, got: %s", matched[0]) 2260 } 2261 2262 if expectNoConflict { 2263 // no naming conflict 2264 m := oairex.FindAllStringSubmatch(string(jazon), -1) 2265 assert.Empty(t, m) 2266 } 2267} 2268 2269func testFlattenWithDefaults(t *testing.T, bp string) *Spec { 2270 sp := loadOrFail(t, bp) 2271 an := New(sp) 2272 err := Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}) 2273 require.NoError(t, err) 2274 return an 2275} 2276 2277func TestFlatten_RemoteAbsolute(t *testing.T) { 2278 // this one has simple remote ref pattern 2279 an := testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-mini.json")) 2280 checkRefs(t, an.spec, true) 2281 2282 // this has no remote ref 2283 an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger.json")) 2284 checkRefs(t, an.spec, true) 2285 2286 // this one has local ref, no naming conflict (same as previous but with external ref imported) 2287 an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-local-ref.json")) 2288 checkRefs(t, an.spec, true) 2289 2290 // this one has remote ref, no naming conflict (same as previous but with external ref imported) 2291 an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-remote-only-ref.json")) 2292 checkRefs(t, an.spec, true) 2293 2294 // this one has both remote and local ref with naming conflict. 2295 // This creates some "oiagen" definitions to address naming conflict, which are removed by the oaigen pruning process (reinlined / merged with parents). 2296 an = testFlattenWithDefaults(t, filepath.Join("fixtures", "bugs", "remote-absolute", "swagger-with-ref.json")) 2297 checkRefs(t, an.spec, false) 2298} 2299 2300func TestTopmostFirs(t *testing.T) { 2301 assert.Equal(t, []string{"/a/b", "/a/b/c"}, topmostFirst([]string{"/a/b/c", "/a/b"})) 2302 assert.Equal(t, []string{"/a/b", "/a/c"}, topmostFirst([]string{"/a/c", "/a/b"})) 2303 assert.Equal(t, []string{"/a/b", "/a/c", "/a/b/c", "/a/b/d", "/a/a/b/d"}, topmostFirst([]string{"/a/a/b/d", "/a/b", "/a/b/c", "/a/b/d", "/a/c"})) 2304} 2305 2306func TestFlatten_2092(t *testing.T) { 2307 log.SetOutput(ioutil.Discard) 2308 defer log.SetOutput(os.Stdout) 2309 2310 bp := filepath.Join("fixtures", "bugs", "2092", "swagger.yaml") 2311 rexOAIGen := regexp.MustCompile(`(?i)("\$ref":\s*")(.?oaigen.?)"`) 2312 2313 // #2092 exhibits a stability issue: repeat 100 times the process to make sure it is stable 2314 var bb, bb2 string 2315 for i := 0; i < 100; i++ { 2316 sp := loadOrFail(t, bp) 2317 an := New(sp) 2318 err := Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}) 2319 require.NoError(t, err) 2320 2321 bbb, _ := json.MarshalIndent(an.spec, "", " ") 2322 2323 if i == 0 { 2324 // verify we don't have dangling oaigen refs 2325 bb = string(bbb) 2326 require.Falsef(t, rexOAIGen.Match(bbb), "unmatched regexp for: %s", bb) 2327 } else { 2328 // verify that we produce a stable result 2329 assert.JSONEq(t, bb, string(bbb)) 2330 } 2331 2332 err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: true}) 2333 require.NoError(t, err) 2334 2335 err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: false, RemoveUnused: false}) 2336 require.NoError(t, err) 2337 2338 bbb, _ = json.MarshalIndent(an.spec, "", " ") 2339 if i == 0 { 2340 bb2 = string(bbb) 2341 require.Falsef(t, rexOAIGen.Match(bbb), "unmatched regexp for: %s") 2342 } else { 2343 // verify that we produce a stable result 2344 assert.JSONEq(t, bb2, string(bbb)) 2345 } 2346 } 2347} 2348 2349func TestFlatten_2113(t *testing.T) { 2350 // flatten $ref under path 2351 2352 log.SetOutput(ioutil.Discard) 2353 defer log.SetOutput(os.Stdout) 2354 2355 bp := filepath.Join("fixtures", "bugs", "2113", "base.yaml") 2356 2357 sp := loadOrFail(t, bp) 2358 an := New(sp) 2359 err := Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Expand: true, RemoveUnused: false}) 2360 require.NoError(t, err) 2361 2362 sp = loadOrFail(t, bp) 2363 an = New(sp) 2364 err = Flatten(FlattenOpts{Spec: an, BasePath: bp, Verbose: true, Minimal: true, RemoveUnused: false}) 2365 require.NoError(t, err) 2366 2367 jazon, err := json.MarshalIndent(sp, "", " ") 2368 require.NoError(t, err) 2369 2370 expected := ` 2371 { 2372 "swagger": "2.0", 2373 "info": { 2374 "title": "nested $ref fixture", 2375 "version": "1" 2376 }, 2377 "paths": { 2378 "/dummy": { 2379 "get": { 2380 "responses": { 2381 "200": { 2382 "description": "OK", 2383 "schema": { 2384 "$ref": "#/definitions/dummy" 2385 } 2386 } 2387 } 2388 } 2389 }, 2390 "/example": { 2391 "get": { 2392 "responses": { 2393 "200": { 2394 "description": "OK", 2395 "schema": { 2396 "$ref": "#/definitions/example" 2397 } 2398 } 2399 } 2400 } 2401 } 2402 }, 2403 "definitions": { 2404 "dummy": { 2405 "required": [ 2406 "dummyPayload" 2407 ], 2408 "properties": { 2409 "dummyPayload": { 2410 "type": "string" 2411 } 2412 } 2413 }, 2414 "example": { 2415 "required": [ 2416 "payload" 2417 ], 2418 "properties": { 2419 "payload": { 2420 "type": "string" 2421 } 2422 }, 2423 "$schema": "http://json-schema.org/draft-07/schema" 2424 } 2425 } 2426 }` 2427 require.JSONEq(t, expected, string(jazon)) 2428} 2429