...

Source file src/github.com/go-openapi/loads/spec_test.go

Documentation: github.com/go-openapi/loads

     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  
    15  package loads
    16  
    17  import (
    18  	"encoding/json"
    19  	"path/filepath"
    20  	"regexp"
    21  	"strconv"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/require"
    27  )
    28  
    29  func TestUnknownSpecVersion(t *testing.T) {
    30  	_, err := Analyzed([]byte{}, "0.9")
    31  	require.Error(t, err)
    32  }
    33  
    34  func TestDefaultsTo20(t *testing.T) {
    35  	d, err := Analyzed(PetStoreJSONMessage, "")
    36  	require.NoError(t, err)
    37  	require.NotNil(t, d)
    38  
    39  	assert.Equal(t, "2.0", d.Version())
    40  	// assert.Equal(t, "2.0", d.data["swagger"].(string))
    41  	assert.Equal(t, "/api", d.BasePath())
    42  }
    43  
    44  func TestLoadsYAMLContent(t *testing.T) {
    45  	d, err := Analyzed(json.RawMessage([]byte(YAMLSpec)), "")
    46  	require.NoError(t, err)
    47  	require.NotNil(t, d)
    48  
    49  	sw := d.Spec()
    50  	assert.Equal(t, "1.0.0", sw.Info.Version)
    51  }
    52  
    53  // for issue 11
    54  func TestRegressionExpand(t *testing.T) {
    55  	swaggerFile := "fixtures/yaml/swagger/1/2/3/4/swagger.yaml"
    56  	document, err := Spec(swaggerFile)
    57  	require.NoError(t, err)
    58  	require.NotNil(t, document)
    59  
    60  	d, err := document.Expanded()
    61  	require.NoError(t, err)
    62  	require.NotNil(t, d)
    63  
    64  	b, _ := d.Spec().MarshalJSON()
    65  	assert.JSONEq(t, expectedExpanded, string(b))
    66  }
    67  
    68  func TestCascadingRefExpand(t *testing.T) {
    69  	swaggerFile := "fixtures/yaml/swagger/spec.yml"
    70  	document, err := Spec(swaggerFile)
    71  	require.NoError(t, err)
    72  	require.NotNil(t, document)
    73  
    74  	d, err := document.Expanded()
    75  	require.NoError(t, err)
    76  	require.NotNil(t, d)
    77  
    78  	b, _ := d.Spec().MarshalJSON()
    79  	assert.JSONEq(t, cascadeRefExpanded, string(b))
    80  }
    81  
    82  func TestFailsInvalidJSON(t *testing.T) {
    83  	_, err := Analyzed(json.RawMessage([]byte("{]")), "")
    84  
    85  	require.Error(t, err)
    86  }
    87  
    88  // issue go-swagger/go-swagger#1816 (regression when cloning original spec)
    89  func TestIssue1846(t *testing.T) {
    90  	swaggerFile := "fixtures/bugs/1816/fixture-1816.yaml"
    91  	document, err := Spec(swaggerFile)
    92  	require.NoError(t, err)
    93  	require.NotNil(t, document)
    94  
    95  	sp, err := cloneSpec(document.Spec())
    96  	require.NoError(t, err)
    97  
    98  	jazon, _ := json.MarshalIndent(sp, "", " ")
    99  	rex := regexp.MustCompile(`"\$ref":\s*"(.+)"`)
   100  	m := rex.FindAllStringSubmatch(string(jazon), -1)
   101  	require.NotNil(t, m)
   102  
   103  	for _, matched := range m {
   104  		subMatch := matched[1]
   105  		require.Truef(t,
   106  			strings.HasPrefix(subMatch, "#/definitions") || strings.HasPrefix(subMatch, "#/responses"),
   107  			"expected $ref to point either to definitions or responses section, got: %s", matched[0])
   108  	}
   109  }
   110  
   111  func TestEmbedded(t *testing.T) {
   112  	swaggerFile := "fixtures/yaml/swagger/spec.yml"
   113  	document, err := Spec(swaggerFile)
   114  	require.NoError(t, err)
   115  	require.NotNil(t, document)
   116  
   117  	raw, err := json.Marshal(document.Raw())
   118  	require.NoError(t, err)
   119  
   120  	spc, err := json.Marshal(document.Spec())
   121  	require.NoError(t, err)
   122  
   123  	d, err := Embedded(raw, spc)
   124  	require.NoError(t, err)
   125  	require.NotNil(t, d)
   126  
   127  	rawEmbedded, err := json.Marshal(d.Raw())
   128  	require.NoError(t, err)
   129  
   130  	spcEmbedded, err := json.Marshal(d.Spec())
   131  	require.NoError(t, err)
   132  
   133  	assert.JSONEq(t, string(raw), string(rawEmbedded))
   134  	assert.JSONEq(t, string(spc), string(spcEmbedded))
   135  }
   136  
   137  func TestDocument(t *testing.T) {
   138  	document, err := Embedded(PetStoreJSONMessage, PetStoreJSONMessage)
   139  	require.NoError(t, err)
   140  
   141  	require.Equal(t, "petstore.swagger.wordnik.com", document.Host())
   142  
   143  	orig, err := json.Marshal(document.OrigSpec())
   144  	require.NoError(t, err)
   145  
   146  	require.JSONEq(t, string(PetStoreJSONMessage), string(orig))
   147  
   148  	cloned, err := json.Marshal(document.Pristine().Spec())
   149  	require.NoError(t, err)
   150  
   151  	require.JSONEq(t, string(PetStoreJSONMessage), string(cloned))
   152  
   153  	spc := document.Spec()
   154  	spc.Definitions = nil
   155  
   156  	before := document.Spec()
   157  	require.Empty(t, before.Definitions)
   158  
   159  	reset := document.ResetDefinitions()
   160  
   161  	afterReset, err := json.Marshal(reset.Spec())
   162  	require.NoError(t, err)
   163  
   164  	require.JSONEq(t, string(PetStoreJSONMessage), string(afterReset))
   165  }
   166  
   167  func BenchmarkAnalyzed(b *testing.B) {
   168  	d := []byte(`{
   169    "swagger": "2.0",
   170    "info": {
   171      "version": "1.0.0",
   172      "title": "Swagger Petstore",
   173      "contact": {
   174        "name": "Wordnik API Team",
   175        "url": "http://developer.wordnik.com"
   176      },
   177      "license": {
   178        "name": "Creative Commons 4.0 International",
   179        "url": "http://creativecommons.org/licenses/by/4.0/"
   180      }
   181    },
   182    "host": "petstore.swagger.wordnik.com",
   183    "basePath": "/api",
   184    "schemes": [
   185      "http"
   186    ],
   187    "paths": {
   188      "/pets": {
   189        "get": {
   190          "security": [
   191            {
   192              "basic": []
   193            }
   194          ],
   195          "tags": [ "Pet Operations" ],
   196          "operationId": "getAllPets",
   197          "parameters": [
   198            {
   199              "name": "status",
   200              "in": "query",
   201              "description": "The status to filter by",
   202              "type": "string"
   203            },
   204            {
   205              "name": "limit",
   206              "in": "query",
   207              "description": "The maximum number of results to return",
   208              "type": "integer",
   209  						"format": "int64"
   210            }
   211          ],
   212          "summary": "Finds all pets in the system",
   213          "responses": {
   214            "200": {
   215              "description": "Pet response",
   216              "schema": {
   217                "type": "array",
   218                "items": {
   219                  "$ref": "#/definitions/Pet"
   220                }
   221              }
   222            },
   223            "default": {
   224              "description": "Unexpected error",
   225              "schema": {
   226                "$ref": "#/definitions/Error"
   227              }
   228            }
   229          }
   230        },
   231        "post": {
   232          "security": [
   233            {
   234              "basic": []
   235            }
   236          ],
   237          "tags": [ "Pet Operations" ],
   238          "operationId": "createPet",
   239          "summary": "Creates a new pet",
   240          "consumes": ["application/x-yaml"],
   241          "produces": ["application/x-yaml"],
   242          "parameters": [
   243            {
   244              "name": "pet",
   245              "in": "body",
   246              "description": "The Pet to create",
   247              "required": true,
   248              "schema": {
   249                "$ref": "#/definitions/newPet"
   250              }
   251            }
   252          ],
   253          "responses": {
   254            "200": {
   255              "description": "Created Pet response",
   256              "schema": {
   257                "$ref": "#/definitions/Pet"
   258              }
   259            },
   260            "default": {
   261              "description": "Unexpected error",
   262              "schema": {
   263                "$ref": "#/definitions/Error"
   264              }
   265            }
   266          }
   267        }
   268      }`)
   269  
   270  	for i := 0; i < 1000; i++ {
   271  		d = append(d, []byte(`,
   272      "/pets/`)...)
   273  		d = strconv.AppendInt(d, int64(i), 10)
   274  		d = append(d, []byte(`": {
   275        "delete": {
   276          "security": [
   277            {
   278              "apiKey": []
   279            }
   280          ],
   281          "description": "Deletes the Pet by id",
   282          "operationId": "deletePet",
   283          "parameters": [
   284            {
   285              "name": "id",
   286              "in": "path",
   287              "description": "ID of pet to delete",
   288              "required": true,
   289              "type": "integer",
   290              "format": "int64"
   291            }
   292          ],
   293          "responses": {
   294            "204": {
   295              "description": "pet deleted"
   296            },
   297            "default": {
   298              "description": "unexpected error",
   299              "schema": {
   300                "$ref": "#/definitions/Error"
   301              }
   302            }
   303          }
   304        },
   305        "get": {
   306          "tags": [ "Pet Operations" ],
   307          "operationId": "getPetById",
   308          "summary": "Finds the pet by id",
   309          "responses": {
   310            "200": {
   311              "description": "Pet response",
   312              "schema": {
   313                "$ref": "#/definitions/Pet"
   314              }
   315            },
   316            "default": {
   317              "description": "Unexpected error",
   318              "schema": {
   319                "$ref": "#/definitions/Error"
   320              }
   321            }
   322          }
   323        },
   324        "parameters": [
   325          {
   326            "name": "id",
   327            "in": "path",
   328            "description": "ID of pet",
   329            "required": true,
   330            "type": "integer",
   331            "format": "int64"
   332          }
   333        ]
   334      }`)...)
   335  	}
   336  
   337  	d = append(d, []byte(`
   338    },
   339    "definitions": {
   340      "Category": {
   341        "id": "Category",
   342        "properties": {
   343          "id": {
   344            "format": "int64",
   345            "type": "integer"
   346          },
   347          "name": {
   348            "type": "string"
   349          }
   350        }
   351      },
   352      "Pet": {
   353        "id": "Pet",
   354        "properties": {
   355          "category": {
   356            "$ref": "#/definitions/Category"
   357          },
   358          "id": {
   359            "description": "unique identifier for the pet",
   360            "format": "int64",
   361            "maximum": 100.0,
   362            "minimum": 0.0,
   363            "type": "integer"
   364          },
   365          "name": {
   366            "type": "string"
   367          },
   368          "photoUrls": {
   369            "items": {
   370              "type": "string"
   371            },
   372            "type": "array"
   373          },
   374          "status": {
   375            "description": "pet status in the store",
   376            "enum": [
   377              "available",
   378              "pending",
   379              "sold"
   380            ],
   381            "type": "string"
   382          },
   383          "tags": {
   384            "items": {
   385              "$ref": "#/definitions/Tag"
   386            },
   387            "type": "array"
   388          }
   389        },
   390        "required": [
   391          "id",
   392          "name"
   393        ]
   394      },
   395      "newPet": {
   396        "anyOf": [
   397          {
   398            "$ref": "#/definitions/Pet"
   399          },
   400          {
   401            "required": [
   402              "name"
   403            ]
   404          }
   405        ]
   406      },
   407      "Tag": {
   408        "id": "Tag",
   409        "properties": {
   410          "id": {
   411            "format": "int64",
   412            "type": "integer"
   413          },
   414          "name": {
   415            "type": "string"
   416          }
   417        }
   418      },
   419      "Error": {
   420        "required": [
   421          "code",
   422          "message"
   423        ],
   424        "properties": {
   425          "code": {
   426            "type": "integer",
   427            "format": "int32"
   428          },
   429          "message": {
   430            "type": "string"
   431          }
   432        }
   433      }
   434    },
   435    "consumes": [
   436      "application/json",
   437      "application/xml"
   438    ],
   439    "produces": [
   440      "application/json",
   441      "application/xml",
   442      "text/plain",
   443      "text/html"
   444    ],
   445    "securityDefinitions": {
   446      "basic": {
   447        "type": "basic"
   448      },
   449      "apiKey": {
   450        "type": "apiKey",
   451        "in": "header",
   452        "name": "X-API-KEY"
   453      }
   454    }
   455  }
   456  `)...)
   457  	rm := json.RawMessage(d)
   458  	b.ResetTimer()
   459  	for i := 0; i < b.N; i++ {
   460  		_, err := Analyzed(rm, "")
   461  		if err != nil {
   462  			b.Fatal(err)
   463  		}
   464  	}
   465  }
   466  
   467  const YAMLSpec = `swagger: '2.0'
   468  
   469  info:
   470    version: "1.0.0"
   471    title: Simple Search API
   472    description: |
   473      A very simple api description that makes a x-www-form-urlencoded only API to submit searches.
   474  
   475  produces:
   476    - application/json
   477  
   478  consumes:
   479    - application/json
   480  
   481  paths:
   482    /search:
   483      post:
   484        operationId: search
   485        summary: searches tasks
   486        description: searches the task titles and descriptions for a match
   487        consumes:
   488          - application/x-www-form-urlencoded
   489        parameters:
   490          - name: q
   491            in: formData
   492            type: string
   493            description: the search string
   494            required: true
   495    /tasks:
   496      get:
   497        operationId: getTasks
   498        summary: Gets Task objects.
   499        description: |
   500          Optional query param of **size** determines
   501          size of returned array
   502        tags:
   503          - tasks
   504        parameters:
   505          - name: size
   506            in: query
   507            description: Size of task list
   508            type: integer
   509            format: int32
   510            default: 20
   511          - name: completed
   512            in: query
   513            description: when true shows completed tasks
   514            type: boolean
   515  
   516        responses:
   517          default:
   518            description: Generic Error
   519          200:
   520            description: Successful response
   521            headers:
   522              X-Rate-Limit:
   523                type: integer
   524                format: int32
   525              X-Rate-Limit-Remaining:
   526                type: integer
   527                format: int32
   528                default: 42
   529              X-Rate-Limit-Reset:
   530                type: integer
   531                format: int32
   532                default: "1449875311"
   533              X-Rate-Limit-Reset-Human:
   534                type: string
   535                default: 3 days
   536              X-Rate-Limit-Reset-Human-Number:
   537                type: string
   538                default: 3
   539              Access-Control-Allow-Origin:
   540                type: string
   541                default: "*"
   542            schema:
   543              type: array
   544              items:
   545                $ref: "#/definitions/Task"
   546      post:
   547        operationId: createTask
   548        summary: Creates a 'Task' object.
   549        description: |
   550          Validates the content property for length etc.
   551        parameters:
   552          - name: body
   553            in: body
   554            schema:
   555              $ref: "#/definitions/Task"
   556        tags:
   557          - tasks
   558        responses:
   559          default:
   560            description: Generic Error
   561          201:
   562            description: Task Created
   563  
   564    /tasks/{id}:
   565      parameters:
   566        - name: id
   567          in: path
   568          type: integer
   569          format: int32
   570          description: The id of the task
   571          required: true
   572          minimum: 1
   573      put:
   574        operationId: updateTask
   575        summary: updates a task.
   576        description: |
   577          Validates the content property for length etc.
   578        tags:
   579          - tasks
   580        parameters:
   581          - name: body
   582            in: body
   583            description: the updated task
   584            schema:
   585              $ref: "#/definitions/Task"
   586        responses:
   587          default:
   588            description: Generic Error
   589          200:
   590            description: Task updated
   591            schema:
   592              $ref: "#/definitions/Task"
   593      delete:
   594        operationId: deleteTask
   595        summary: deletes a task
   596        description: |
   597          Deleting a task is irrevocable.
   598        tags:
   599          - tasks
   600        responses:
   601          default:
   602            description: Generic Error
   603          204:
   604            description: Task Deleted
   605  
   606  
   607  definitions:
   608    Task:
   609      title: A Task object
   610      description: |
   611        This describes a task. Tasks require a content property to be set.
   612      required:
   613        - content
   614      type: object
   615      properties:
   616        id:
   617          title: the unique id of the task
   618          description: |
   619            This id property is autogenerated when a task is created.
   620          type: integer
   621          format: int64
   622          readOnly: true
   623        content:
   624          title: The content of the task
   625          description: |
   626            Task content can contain [GFM](https://help.github.com/articles/github-flavored-markdown/).
   627          type: string
   628          minLength: 5
   629        completed:
   630          title: when true this task is completed
   631          type: boolean
   632        creditcard:
   633          title: the credit card format usage
   634          type: string
   635          format: creditcard
   636        createdAt:
   637          title: task creation time
   638          type: string
   639          format: date-time
   640          readOnly: true
   641  `
   642  
   643  // PetStoreJSONMessage json raw message for Petstore20
   644  var PetStoreJSONMessage = json.RawMessage([]byte(PetStore20))
   645  
   646  // PetStore20 json doc for swagger 2.0 pet store
   647  const PetStore20 = `{
   648    "swagger": "2.0",
   649    "info": {
   650      "version": "1.0.0",
   651      "title": "Swagger Petstore",
   652      "contact": {
   653        "name": "Wordnik API Team",
   654        "url": "http://developer.wordnik.com"
   655      },
   656      "license": {
   657        "name": "Creative Commons 4.0 International",
   658        "url": "http://creativecommons.org/licenses/by/4.0/"
   659      }
   660    },
   661    "host": "petstore.swagger.wordnik.com",
   662    "basePath": "/api",
   663    "schemes": [
   664      "http"
   665    ],
   666    "paths": {
   667      "/pets": {
   668        "get": {
   669          "security": [
   670            {
   671              "basic": []
   672            }
   673          ],
   674          "tags": [ "Pet Operations" ],
   675          "operationId": "getAllPets",
   676          "parameters": [
   677            {
   678              "name": "status",
   679              "in": "query",
   680              "description": "The status to filter by",
   681              "type": "string"
   682            },
   683            {
   684              "name": "limit",
   685              "in": "query",
   686              "description": "The maximum number of results to return",
   687              "type": "integer",
   688  						"format": "int64"
   689            }
   690          ],
   691          "summary": "Finds all pets in the system",
   692          "responses": {
   693            "200": {
   694              "description": "Pet response",
   695              "schema": {
   696                "type": "array",
   697                "items": {
   698                  "$ref": "#/definitions/Pet"
   699                }
   700              }
   701            },
   702            "default": {
   703              "description": "Unexpected error",
   704              "schema": {
   705                "$ref": "#/definitions/Error"
   706              }
   707            }
   708          }
   709        },
   710        "post": {
   711          "security": [
   712            {
   713              "basic": []
   714            }
   715          ],
   716          "tags": [ "Pet Operations" ],
   717          "operationId": "createPet",
   718          "summary": "Creates a new pet",
   719          "consumes": ["application/x-yaml"],
   720          "produces": ["application/x-yaml"],
   721          "parameters": [
   722            {
   723              "name": "pet",
   724              "in": "body",
   725              "description": "The Pet to create",
   726              "required": true,
   727              "schema": {
   728                "$ref": "#/definitions/newPet"
   729              }
   730            }
   731          ],
   732          "responses": {
   733            "200": {
   734              "description": "Created Pet response",
   735              "schema": {
   736                "$ref": "#/definitions/Pet"
   737              }
   738            },
   739            "default": {
   740              "description": "Unexpected error",
   741              "schema": {
   742                "$ref": "#/definitions/Error"
   743              }
   744            }
   745          }
   746        }
   747      },
   748      "/pets/{id}": {
   749        "delete": {
   750          "security": [
   751            {
   752              "apiKey": []
   753            }
   754          ],
   755          "description": "Deletes the Pet by id",
   756          "operationId": "deletePet",
   757          "parameters": [
   758            {
   759              "name": "id",
   760              "in": "path",
   761              "description": "ID of pet to delete",
   762              "required": true,
   763              "type": "integer",
   764              "format": "int64"
   765            }
   766          ],
   767          "responses": {
   768            "204": {
   769              "description": "pet deleted"
   770            },
   771            "default": {
   772              "description": "unexpected error",
   773              "schema": {
   774                "$ref": "#/definitions/Error"
   775              }
   776            }
   777          }
   778        },
   779        "get": {
   780          "tags": [ "Pet Operations" ],
   781          "operationId": "getPetById",
   782          "summary": "Finds the pet by id",
   783          "responses": {
   784            "200": {
   785              "description": "Pet response",
   786              "schema": {
   787                "$ref": "#/definitions/Pet"
   788              }
   789            },
   790            "default": {
   791              "description": "Unexpected error",
   792              "schema": {
   793                "$ref": "#/definitions/Error"
   794              }
   795            }
   796          }
   797        },
   798        "parameters": [
   799          {
   800            "name": "id",
   801            "in": "path",
   802            "description": "ID of pet",
   803            "required": true,
   804            "type": "integer",
   805            "format": "int64"
   806          }
   807        ]
   808      }
   809    },
   810    "definitions": {
   811      "Category": {
   812        "id": "Category",
   813        "properties": {
   814          "id": {
   815            "format": "int64",
   816            "type": "integer"
   817          },
   818          "name": {
   819            "type": "string"
   820          }
   821        }
   822      },
   823      "Pet": {
   824        "id": "Pet",
   825        "properties": {
   826          "category": {
   827            "$ref": "#/definitions/Category"
   828          },
   829          "id": {
   830            "description": "unique identifier for the pet",
   831            "format": "int64",
   832            "maximum": 100.0,
   833            "minimum": 0.0,
   834            "type": "integer"
   835          },
   836          "name": {
   837            "type": "string"
   838          },
   839          "photoUrls": {
   840            "items": {
   841              "type": "string"
   842            },
   843            "type": "array"
   844          },
   845          "status": {
   846            "description": "pet status in the store",
   847            "enum": [
   848              "available",
   849              "pending",
   850              "sold"
   851            ],
   852            "type": "string"
   853          },
   854          "tags": {
   855            "items": {
   856              "$ref": "#/definitions/Tag"
   857            },
   858            "type": "array"
   859          }
   860        },
   861        "required": [
   862          "id",
   863          "name"
   864        ]
   865      },
   866      "newPet": {
   867        "anyOf": [
   868          {
   869            "$ref": "#/definitions/Pet"
   870          },
   871          {
   872            "required": [
   873              "name"
   874            ]
   875          }
   876        ]
   877      },
   878      "Tag": {
   879        "id": "Tag",
   880        "properties": {
   881          "id": {
   882            "format": "int64",
   883            "type": "integer"
   884          },
   885          "name": {
   886            "type": "string"
   887          }
   888        }
   889      },
   890      "Error": {
   891        "required": [
   892          "code",
   893          "message"
   894        ],
   895        "properties": {
   896          "code": {
   897            "type": "integer",
   898            "format": "int32"
   899          },
   900          "message": {
   901            "type": "string"
   902          }
   903        }
   904      }
   905    },
   906    "consumes": [
   907      "application/json",
   908      "application/xml"
   909    ],
   910    "produces": [
   911      "application/json",
   912      "application/xml",
   913      "text/plain",
   914      "text/html"
   915    ],
   916    "securityDefinitions": {
   917      "basic": {
   918        "type": "basic"
   919      },
   920      "apiKey": {
   921        "type": "apiKey",
   922        "in": "header",
   923        "name": "X-API-KEY"
   924      }
   925    }
   926  }
   927  `
   928  
   929  const expectedExpanded = `
   930  {
   931     "produces":[
   932        "application/json",
   933        "plain/text"
   934     ],
   935     "schemes":[
   936        "https",
   937        "http"
   938     ],
   939     "swagger":"2.0",
   940     "info":{
   941        "description":"Something",
   942        "title":"Something",
   943        "contact":{
   944           "name":"Somebody",
   945           "url":"https://url.com",
   946           "email":"email@url.com"
   947        },
   948        "version":"v1"
   949     },
   950     "host":"security.sonusnet.com",
   951     "basePath":"/api",
   952     "paths":{
   953        "/whatnot":{
   954           "get":{
   955              "description":"Get something",
   956              "responses":{
   957                 "200":{
   958                    "description":"The something",
   959                    "schema":{
   960                       "description":"A collection of service events",
   961                       "type":"object",
   962                       "properties":{
   963                          "page":{
   964                             "description":"A description of a paged result",
   965                             "type":"object",
   966                             "properties":{
   967                                "page":{
   968                                   "description":"the page that was requested",
   969                                   "type":"integer"
   970                                },
   971                                "page_items":{
   972                                   "description":"the number of items per page requested",
   973                                   "type":"integer"
   974                                },
   975                                "pages":{
   976                                   "description":"the total number of pages available",
   977                                   "type":"integer"
   978                                },
   979                                "total_items":{
   980                                   "description":"the total number of items available",
   981                                   "type":"integer",
   982                                   "format":"int64"
   983                                }
   984                             }
   985                          },
   986                          "something":{
   987                             "description":"Something",
   988                             "type":"object",
   989                             "properties":{
   990                                "p1":{
   991                                   "description":"A string",
   992                                   "type":"string"
   993                                },
   994                                "p2":{
   995                                   "description":"An integer",
   996                                   "type":"integer"
   997                                }
   998                             }
   999                          }
  1000                       }
  1001                    }
  1002                 },
  1003                 "500":{
  1004                    "description":"Oops"
  1005                 }
  1006              }
  1007           }
  1008        }
  1009     },
  1010     "definitions":{
  1011        "Something":{
  1012           "description":"A collection of service events",
  1013           "type":"object",
  1014           "properties":{
  1015              "page":{
  1016                 "description":"A description of a paged result",
  1017                 "type":"object",
  1018                 "properties":{
  1019                    "page":{
  1020                       "description":"the page that was requested",
  1021                       "type":"integer"
  1022                    },
  1023                    "page_items":{
  1024                       "description":"the number of items per page requested",
  1025                       "type":"integer"
  1026                    },
  1027                    "pages":{
  1028                       "description":"the total number of pages available",
  1029                       "type":"integer"
  1030                    },
  1031                    "total_items":{
  1032                       "description":"the total number of items available",
  1033                       "type":"integer",
  1034                       "format":"int64"
  1035                    }
  1036                 }
  1037              },
  1038              "something":{
  1039                 "description":"Something",
  1040                 "type":"object",
  1041                 "properties":{
  1042                    "p1":{
  1043                       "description":"A string",
  1044                       "type":"string"
  1045                    },
  1046                    "p2":{
  1047                       "description":"An integer",
  1048                       "type":"integer"
  1049                    }
  1050                 }
  1051              }
  1052           }
  1053        }
  1054     }
  1055  }
  1056  `
  1057  
  1058  const cascadeRefExpanded = `
  1059  {
  1060    "swagger": "2.0",
  1061    "consumes":[
  1062       "application/json"
  1063    ],
  1064    "produces":[
  1065       "application/json"
  1066    ],
  1067    "schemes":[
  1068       "http"
  1069    ],
  1070  	"host": "api.example.com",
  1071    "info":{
  1072       "description":"recursively following JSON references",
  1073       "title":"test 1",
  1074       "contact":{
  1075          "name":"Fred"
  1076       },
  1077       "version":"0.1.1"
  1078    },
  1079    "paths":{
  1080       "/getAll":{
  1081          "get":{
  1082             "operationId":"getAll",
  1083             "parameters":[
  1084                {
  1085                   "description":"max number of results",
  1086                   "name":"a",
  1087                   "in":"body",
  1088                   "schema":{
  1089                      "type":"string"
  1090                   }
  1091                }
  1092             ],
  1093             "responses":{
  1094                "200":{
  1095                   "description":"Success",
  1096                   "schema":{
  1097                      "type":"array",
  1098                      "items":{
  1099                         "type":"string"
  1100                      }
  1101                   }
  1102                }
  1103             }
  1104          }
  1105       }
  1106    },
  1107    "definitions":{
  1108       "a":{
  1109          "type":"string"
  1110       },
  1111       "b":{
  1112          "type":"array",
  1113          "items":{
  1114             "type":"string"
  1115          }
  1116       }
  1117    }
  1118  }
  1119  `
  1120  
  1121  func TestSpecCircular(t *testing.T) {
  1122  	swaggerFile := "fixtures/json/resources/pathLoaderIssue.json"
  1123  	document, err := Spec(swaggerFile)
  1124  	require.NoError(t, err)
  1125  	require.NotNil(t, document)
  1126  }
  1127  
  1128  func TestIssueSpec145(t *testing.T) {
  1129  	t.Run("with remote $ref", func(t *testing.T) {
  1130  		docPath := filepath.Join("fixtures", "bugs", "145", "Program Files (x86)", "AppName", "todos.json")
  1131  
  1132  		t.Run("with Spec loader", func(t *testing.T) {
  1133  			document, err := Spec(docPath)
  1134  			require.NoError(t, err)
  1135  			require.NotNil(t, document)
  1136  
  1137  			_, err = document.Expanded()
  1138  			require.NoError(t, err)
  1139  		})
  1140  
  1141  		t.Run("with JSONSpec loader", func(t *testing.T) {
  1142  			document, err := JSONSpec(docPath)
  1143  			require.NoError(t, err)
  1144  			require.NotNil(t, document)
  1145  
  1146  			_, err = document.Expanded()
  1147  			require.NoError(t, err)
  1148  		})
  1149  	})
  1150  
  1151  	t.Run("with self-contained root", func(t *testing.T) {
  1152  		docPath := filepath.Join("fixtures", "bugs", "145", "Program Files (x86)", "AppName", "todos-expanded.json")
  1153  
  1154  		t.Run("with Spec loader", func(t *testing.T) {
  1155  			document, err := Spec(docPath)
  1156  			require.NoError(t, err)
  1157  			require.NotNil(t, document)
  1158  
  1159  			require.Equal(t, docPath, document.SpecFilePath())
  1160  
  1161  			expanded, err := document.Expanded()
  1162  			require.NoError(t, err)
  1163  
  1164  			require.Equal(t, docPath, expanded.SpecFilePath())
  1165  		})
  1166  
  1167  		t.Run("with JSONSpec loader", func(t *testing.T) {
  1168  			document, err := JSONSpec(docPath)
  1169  			require.NoError(t, err)
  1170  			require.NotNil(t, document)
  1171  
  1172  			_, err = document.Expanded()
  1173  			require.NoError(t, err)
  1174  
  1175  			t.Run("with Pristine", func(t *testing.T) {
  1176  				pristine := document.Pristine()
  1177  
  1178  				require.Equal(t, document.SpecFilePath(), pristine.SpecFilePath())
  1179  			})
  1180  		})
  1181  	})
  1182  }
  1183  

View as plain text