...

Source file src/github.com/go-openapi/spec/expander_test.go

Documentation: github.com/go-openapi/spec

     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 spec
    16  
    17  import (
    18  	"encoding/json"
    19  	"io"
    20  	"log"
    21  	"net/http"
    22  	"net/http/httptest"
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  )
    30  
    31  const (
    32  	crossFileRefFixture = "fixtures/expansion/crossFileRef.json"
    33  	withoutSchemaID     = "removed"
    34  	withSchemaID        = "schema"
    35  	pathItemsFixture    = "fixtures/expansion/pathItems.json"
    36  	extraRefFixture     = "fixtures/expansion/extraRef.json"
    37  )
    38  
    39  var (
    40  	// PetStoreJSONMessage json raw message for Petstore20
    41  	PetStoreJSONMessage = json.RawMessage([]byte(PetStore20))
    42  	specs               = filepath.Join("fixtures", "specs")
    43  )
    44  
    45  func TestExpand_Issue148(t *testing.T) {
    46  	fp := filepath.Join("fixtures", "bugs", "schema-148.json")
    47  	b, err := jsonDoc(fp)
    48  	require.NoError(t, err)
    49  
    50  	assertAdditionalProps := func(sp Swagger) func(*testing.T) {
    51  		return func(t *testing.T) {
    52  			require.Len(t, sp.Definitions, 2)
    53  
    54  			require.Contains(t, sp.Definitions, "empty")
    55  			empty := sp.Definitions["empty"]
    56  			require.NotNil(t, empty.AdditionalProperties)
    57  			require.NotNil(t, empty.AdditionalProperties.Schema)
    58  			require.True(t, empty.AdditionalProperties.Allows)
    59  
    60  			require.Contains(t, sp.Definitions, "false")
    61  			additionalIsFalse := sp.Definitions["false"]
    62  			require.NotNil(t, additionalIsFalse.AdditionalProperties)
    63  			require.Nil(t, additionalIsFalse.AdditionalProperties.Schema)
    64  			require.False(t, additionalIsFalse.AdditionalProperties.Allows)
    65  		}
    66  	}
    67  
    68  	var sp Swagger
    69  	require.NoError(t, json.Unmarshal(b, &sp))
    70  	t.Run("check additional properties", assertAdditionalProps(sp))
    71  
    72  	require.NoError(t, ExpandSpec(&sp, nil))
    73  	t.Run("check additional properties after expansion", assertAdditionalProps(sp))
    74  }
    75  
    76  func TestExpand_KnownRef(t *testing.T) {
    77  	// json schema draft 4 meta schema is embedded by default: it resolves without setup
    78  	schema := RefProperty("http://json-schema.org/draft-04/schema#")
    79  	require.NoError(t, ExpandSchema(schema, nil, nil))
    80  
    81  	assert.Equal(t, "Core schema meta-schema", schema.Description)
    82  
    83  	// from the expanded schema, verify that all remaining $ref actually resolve
    84  	jazon := asJSON(t, schema)
    85  
    86  	assertRefResolve(t, jazon, "", schema)
    87  }
    88  
    89  func TestExpand_ResponseSchema(t *testing.T) {
    90  	fp := filepath.Join("fixtures", "local_expansion", "spec.json")
    91  	b, err := jsonDoc(fp)
    92  	require.NoError(t, err)
    93  
    94  	var spec Swagger
    95  	require.NoError(t, json.Unmarshal(b, &spec))
    96  
    97  	require.NoError(t, ExpandSpec(&spec, &ExpandOptions{RelativeBase: fp}))
    98  
    99  	// verify that the document is full expanded
   100  	jazon := asJSON(t, spec)
   101  	assertNoRef(t, jazon)
   102  
   103  	sch := spec.Paths.Paths["/item"].Get.Responses.StatusCodeResponses[200].Schema
   104  	require.NotNil(t, sch)
   105  
   106  	assert.Empty(t, sch.Ref.String())
   107  	assert.Contains(t, sch.Type, "object")
   108  	assert.Len(t, sch.Properties, 2)
   109  }
   110  
   111  func TestExpand_EmptySpec(t *testing.T) {
   112  	spec := new(Swagger)
   113  
   114  	// expansion of an empty spec
   115  	require.NoError(t, ExpandSpec(spec, nil))
   116  
   117  	// expansion of a nil schema
   118  	var schema *Schema
   119  	require.NoError(t, ExpandSchemaWithBasePath(schema, nil, nil))
   120  
   121  	// expansion of a nil paths
   122  	var paths *PathItem
   123  	resolver := defaultSchemaLoader(spec, nil, nil, nil)
   124  	require.NoError(t, expandPathItem(paths, resolver, ""))
   125  
   126  	// expansion of a nil Parameter
   127  	var param *Parameter
   128  	require.NoError(t, expandParameterOrResponse(param, resolver, ""))
   129  }
   130  
   131  func TestExpand_Spec(t *testing.T) {
   132  
   133  	// expansion of a rich spec
   134  	specPath := filepath.Join("fixtures", "expansion", "all-the-things.json")
   135  	specDoc, err := jsonDoc(specPath)
   136  	require.NoError(t, err)
   137  
   138  	opts := &ExpandOptions{
   139  		RelativeBase: specPath,
   140  	}
   141  
   142  	spec := new(Swagger)
   143  	require.NoError(t, json.Unmarshal(specDoc, spec))
   144  
   145  	// verify the resulting unmarshaled structure
   146  	pet := spec.Definitions["pet"]
   147  	errorModel := spec.Definitions["errorModel"]
   148  	petResponse := spec.Responses["petResponse"]
   149  	petResponse.Schema = &pet
   150  	stringResponse := spec.Responses["stringResponse"]
   151  	tagParam := spec.Parameters["tag"]
   152  	idParam := spec.Parameters["idParam"]
   153  
   154  	require.NoError(t, ExpandSpec(spec, opts))
   155  
   156  	// verify that the spec is fully expanded
   157  	assertNoRef(t, asJSON(t, spec))
   158  
   159  	assert.Equal(t, tagParam, spec.Parameters["query"])
   160  	assert.Equal(t, petResponse, spec.Responses["petResponse"])
   161  	assert.Equal(t, petResponse, spec.Responses["anotherPet"])
   162  	assert.Equal(t, pet, *spec.Responses["petResponse"].Schema)
   163  	assert.Equal(t, stringResponse, *spec.Paths.Paths["/"].Get.Responses.Default)
   164  	assert.Equal(t, petResponse, spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200])
   165  	assert.Equal(t, pet, *spec.Paths.Paths["/pets"].Get.Responses.StatusCodeResponses[200].Schema.Items.Schema)
   166  	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Get.Responses.Default.Schema)
   167  	assert.Equal(t, pet, spec.Definitions["petInput"].AllOf[0])
   168  	assert.Equal(t, spec.Definitions["petInput"], *spec.Paths.Paths["/pets"].Post.Parameters[0].Schema)
   169  	assert.Equal(t, petResponse, spec.Paths.Paths["/pets"].Post.Responses.StatusCodeResponses[200])
   170  	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Post.Responses.Default.Schema)
   171  
   172  	pi := spec.Paths.Paths["/pets/{id}"]
   173  	assert.Equal(t, idParam, pi.Get.Parameters[0])
   174  	assert.Equal(t, petResponse, pi.Get.Responses.StatusCodeResponses[200])
   175  	assert.Equal(t, errorModel, *pi.Get.Responses.Default.Schema)
   176  	assert.Equal(t, idParam, pi.Delete.Parameters[0])
   177  	assert.Equal(t, errorModel, *pi.Delete.Responses.Default.Schema)
   178  }
   179  
   180  func TestExpand_InternalResponse(t *testing.T) {
   181  	basePath := normalizeBase(filepath.Join("fixtures", "expansion", "all-the-things.json"))
   182  	specDoc, err := jsonDoc(basePath)
   183  	require.NoError(t, err)
   184  
   185  	spec := new(Swagger)
   186  	require.NoError(t, json.Unmarshal(specDoc, spec))
   187  
   188  	resolver := defaultSchemaLoader(spec, nil, nil, nil)
   189  
   190  	expectedPet := spec.Responses["petResponse"]
   191  	require.NoError(t, expandParameterOrResponse(&expectedPet, resolver, basePath))
   192  
   193  	jazon := asJSON(t, expectedPet)
   194  
   195  	assert.JSONEq(t, `{
   196           "description": "pet response",
   197           "schema": {
   198            "required": [
   199             "id",
   200             "name"
   201            ],
   202            "properties": {
   203             "id": {
   204              "type": "integer",
   205              "format": "int64"
   206             },
   207             "name": {
   208              "type": "string"
   209             },
   210             "tag": {
   211              "type": "string"
   212             }
   213            }
   214           }
   215  			 }`, jazon)
   216  
   217  	// response pointing to the same target: result is unchanged
   218  	another := spec.Responses["anotherPet"]
   219  	require.NoError(t, expandParameterOrResponse(&another, resolver, basePath))
   220  	assert.Equal(t, expectedPet, another)
   221  
   222  	defaultResponse := spec.Paths.Paths["/"].Get.Responses.Default
   223  
   224  	require.NoError(t, expandParameterOrResponse(defaultResponse, resolver, basePath))
   225  
   226  	expectedString := spec.Responses["stringResponse"]
   227  	assert.Equal(t, expectedString, *defaultResponse)
   228  
   229  	// cascading ref
   230  	successResponse := spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
   231  
   232  	jazon = asJSON(t, successResponse)
   233  
   234  	assert.JSONEq(t, `{
   235  		"$ref": "#/responses/anotherPet"
   236    }`, jazon)
   237  
   238  	require.NoError(t, expandParameterOrResponse(&successResponse, resolver, basePath))
   239  	assert.Equal(t, expectedPet, successResponse)
   240  }
   241  
   242  func TestExpand_Response(t *testing.T) {
   243  	basePath := filepath.Join("fixtures", "expansion", "all-the-things.json")
   244  
   245  	specDoc, err := jsonDoc(basePath)
   246  	require.NoError(t, err)
   247  
   248  	spec := new(Swagger)
   249  	require.NoError(t, json.Unmarshal(specDoc, spec))
   250  
   251  	resp := spec.Responses["anotherPet"]
   252  	expected := spec.Responses["petResponse"]
   253  
   254  	require.NoError(t, ExpandResponse(&expected, basePath))
   255  
   256  	require.NoError(t, ExpandResponse(&resp, basePath))
   257  	assert.Equal(t, expected, resp)
   258  
   259  	resp2 := spec.Paths.Paths["/"].Get.Responses.Default
   260  	expected = spec.Responses["stringResponse"]
   261  
   262  	require.NoError(t, ExpandResponse(resp2, basePath))
   263  	assert.Equal(t, expected, *resp2)
   264  
   265  	resp = spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200]
   266  	expected = spec.Responses["petResponse"]
   267  
   268  	require.NoError(t, ExpandResponse(&resp, basePath))
   269  	assert.Equal(t, expected, resp)
   270  }
   271  
   272  func TestExpand_ResponseAndParamWithRoot(t *testing.T) {
   273  	specDoc, err := jsonDoc("fixtures/bugs/1614/gitea.json")
   274  	require.NoError(t, err)
   275  
   276  	var spec Swagger
   277  	err = json.Unmarshal(specDoc, &spec)
   278  	require.NoError(t, err)
   279  
   280  	// check responses with $ref
   281  	resp := spec.Paths.Paths["/admin/users"].Post.Responses.StatusCodeResponses[201]
   282  	require.NoError(t, ExpandResponseWithRoot(&resp, spec, nil))
   283  
   284  	jazon := asJSON(t, resp)
   285  	assertNoRef(t, jazon)
   286  
   287  	resp = spec.Paths.Paths["/admin/users"].Post.Responses.StatusCodeResponses[403]
   288  	require.NoError(t, ExpandResponseWithRoot(&resp, spec, nil))
   289  
   290  	jazon = asJSON(t, resp)
   291  	assertNoRef(t, jazon)
   292  
   293  	// check param with $ref
   294  	param := spec.Paths.Paths["/admin/users"].Post.Parameters[0]
   295  	require.NoError(t, ExpandParameterWithRoot(&param, spec, nil))
   296  
   297  	jazon = asJSON(t, param)
   298  	assertNoRef(t, jazon)
   299  }
   300  
   301  func TestExpand_InternalParameter(t *testing.T) {
   302  	basePath := normalizeBase(filepath.Join("fixtures", "expansion", "params.json"))
   303  
   304  	paramDoc, err := jsonDoc(basePath)
   305  	require.NoError(t, err)
   306  
   307  	spec := new(Swagger)
   308  	require.NoError(t, json.Unmarshal(paramDoc, spec))
   309  
   310  	resolver := defaultSchemaLoader(spec, nil, nil, nil)
   311  
   312  	param := spec.Parameters["query"]
   313  	expected := spec.Parameters["tag"]
   314  
   315  	require.NoError(t, expandParameterOrResponse(&param, resolver, basePath))
   316  
   317  	assert.Equal(t, expected, param)
   318  
   319  	param = spec.Paths.Paths["/cars/{id}"].Parameters[0]
   320  	expected = spec.Parameters["id"]
   321  
   322  	require.NoError(t, expandParameterOrResponse(&param, resolver, basePath))
   323  
   324  	assert.Equal(t, expected, param)
   325  }
   326  
   327  func TestExpand_Parameter(t *testing.T) {
   328  	basePath := filepath.Join("fixtures", "expansion", "params.json")
   329  
   330  	paramDoc, err := jsonDoc(basePath)
   331  	require.NoError(t, err)
   332  
   333  	spec := new(Swagger)
   334  	require.NoError(t, json.Unmarshal(paramDoc, spec))
   335  
   336  	param := spec.Parameters["query"]
   337  	expected := spec.Parameters["tag"]
   338  
   339  	require.NoError(t, ExpandParameter(&param, basePath))
   340  	assert.Equal(t, expected, param)
   341  
   342  	param = spec.Paths.Paths["/cars/{id}"].Parameters[0]
   343  	expected = spec.Parameters["id"]
   344  
   345  	require.NoError(t, ExpandParameter(&param, basePath))
   346  	assert.Equal(t, expected, param)
   347  }
   348  
   349  func TestExpand_JSONSchemaDraft4(t *testing.T) {
   350  	fixturePath := filepath.Join("schemas", "jsonschema-draft-04.json")
   351  	jazon, sch := expandThisSchemaOrDieTrying(t, fixturePath)
   352  
   353  	// expansion leaves some circular $ref behind: let's assert those
   354  
   355  	// assert all $ref match
   356  	// "$ref": "http://json-schema.org/draft-04/something"
   357  	//
   358  	// Case: Circular $refs against schema ID
   359  	assertRefInJSONRegexp(t, jazon, `^(http://json-schema.org/draft-04/)|(#/definitions/schemaArray)`)
   360  
   361  	// verify that all remaining $ref may be expanded in the new root schema
   362  	assertRefExpand(t, jazon, "", sch)
   363  }
   364  
   365  func TestExpand_SwaggerSchema(t *testing.T) {
   366  	fixturePath := filepath.Join("schemas", "v2", "schema.json")
   367  	jazon, sch := expandThisSchemaOrDieTrying(t, fixturePath)
   368  
   369  	// expansion leaves some circular $ref behind: let's assert those
   370  
   371  	// assert all $ref match
   372  	// "$ref": "#/definitions/something"
   373  	assertRefInJSON(t, jazon, "#/definitions/")
   374  
   375  	// verify that all $ref resolve in the new root schema
   376  	assertRefExpand(t, jazon, "", sch)
   377  }
   378  
   379  func TestExpand_ContinueOnError(t *testing.T) {
   380  	specPath := filepath.Join("fixtures", "expansion", "missingRef.json")
   381  
   382  	defer log.SetOutput(os.Stdout)
   383  	log.SetOutput(io.Discard)
   384  
   385  	// missing $ref in spec
   386  	missingRefDoc, err := jsonDoc(specPath)
   387  	require.NoError(t, err)
   388  
   389  	testCase := struct {
   390  		Input    *Swagger `json:"input"`
   391  		Expected *Swagger `json:"expected"`
   392  	}{}
   393  	require.NoError(t, json.Unmarshal(missingRefDoc, &testCase))
   394  
   395  	opts := &ExpandOptions{
   396  		ContinueOnError: true,
   397  		RelativeBase:    specPath,
   398  	}
   399  	require.NoError(t, ExpandSpec(testCase.Input, opts))
   400  
   401  	assert.Equal(t, testCase.Expected, testCase.Input, "Should continue expanding spec when a definition can't be found.")
   402  
   403  	// missing $ref in items
   404  	doc, err := jsonDoc("fixtures/expansion/missingItemRef.json")
   405  	require.NoError(t, err)
   406  
   407  	spec := new(Swagger)
   408  	require.NoError(t, json.Unmarshal(doc, spec))
   409  
   410  	assert.NotPanics(t, func() {
   411  		require.NoError(t, ExpandSpec(spec, opts))
   412  	}, "Array of missing refs should not cause a panic, and continue to expand spec.")
   413  }
   414  
   415  func TestExpand_InternalSchemas2(t *testing.T) {
   416  	basePath := normalizeBase(filepath.Join("fixtures", "expansion", "schemas2.json"))
   417  
   418  	carsDoc, err := jsonDoc(basePath)
   419  	require.NoError(t, err)
   420  
   421  	spec := new(Swagger)
   422  	require.NoError(t, json.Unmarshal(carsDoc, spec))
   423  
   424  	resolver := defaultSchemaLoader(spec, nil, nil, nil)
   425  
   426  	// verify unmarshaled structure
   427  	schema := spec.Definitions["car"]
   428  	oldBrand := schema.Properties["brand"]
   429  	require.NotEmpty(t, oldBrand.Items.Schema.Ref.String()) // this is a $ref
   430  	require.NotEqual(t, spec.Definitions["brand"], oldBrand)
   431  
   432  	_, err = expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
   433  	require.NoError(t, err)
   434  
   435  	// verify expanded schema for Car, in the document passed
   436  	newBrand := schema.Properties["brand"]
   437  	assert.Empty(t, newBrand.Items.Schema.Ref.String())
   438  	assert.Equal(t, spec.Definitions["brand"], *newBrand.Items.Schema)
   439  
   440  	// verify expanded schema for Truck, in the returned schema
   441  	schema = spec.Definitions["truck"]
   442  	require.NotEmpty(t, schema.Items.Schema.Ref.String())
   443  	s, err := expandSchema(schema, []string{"#/definitions/truck"}, resolver, basePath)
   444  	require.NoError(t, err)
   445  	require.NotNil(t, s)
   446  
   447  	schema = *s
   448  	assert.Empty(t, schema.Items.Schema.Ref.String()) // no more a $ref
   449  	assert.False(t, schema.Items.Schema.Ref.IsRoot()) // no more a $ref
   450  	assert.Equal(t, spec.Definitions["car"], *schema.Items.Schema)
   451  
   452  	sch := new(Schema)
   453  	_, err = expandSchema(*sch, []string{""}, resolver, basePath)
   454  	require.NoError(t, err)
   455  
   456  	// verify expanded schema for Batch, in the returned schema
   457  	schema = spec.Definitions["batch"]
   458  	s, err = expandSchema(schema, []string{"#/definitions/batch"}, resolver, basePath)
   459  	require.NoError(t, err)
   460  	require.NotNil(t, s)
   461  
   462  	schema = *s
   463  	assert.Empty(t, schema.Items.Schema.Items.Schema.Ref.String())
   464  	assert.Equal(t, *schema.Items.Schema.Items.Schema, spec.Definitions["brand"])
   465  
   466  	// verify expanded schema for Batch2, in the returned schema
   467  	schema = spec.Definitions["batch2"]
   468  	s, err = expandSchema(schema, []string{"#/definitions/batch2"}, resolver, basePath)
   469  	require.NoError(t, err)
   470  	require.NotNil(t, s)
   471  
   472  	schema = *s
   473  	assert.Empty(t, schema.Items.Schemas[0].Items.Schema.Ref.String())
   474  	assert.Empty(t, schema.Items.Schemas[1].Items.Schema.Ref.String())
   475  	assert.Equal(t, *schema.Items.Schemas[0].Items.Schema, spec.Definitions["brand"])
   476  	assert.Equal(t, *schema.Items.Schemas[1].Items.Schema, spec.Definitions["tag"])
   477  
   478  	// verify expanded schema for AllOfBoth, in the returned schema [expand allOf]
   479  	schema = spec.Definitions["allofBoth"]
   480  	s, err = expandSchema(schema, []string{"#/definitions/allofBoth"}, resolver, basePath)
   481  	require.NoError(t, err)
   482  	require.NotNil(t, s)
   483  
   484  	schema = *s
   485  	assert.Empty(t, schema.AllOf[0].Items.Schema.Ref.String())
   486  	assert.Empty(t, schema.AllOf[1].Items.Schema.Ref.String())
   487  	assert.Equal(t, *schema.AllOf[0].Items.Schema, spec.Definitions["brand"])
   488  	assert.Equal(t, *schema.AllOf[1].Items.Schema, spec.Definitions["tag"])
   489  
   490  	// verify expanded schema for AnyOfBoth, in the returned schema [expand anyOf]
   491  	schema = spec.Definitions["anyofBoth"]
   492  	s, err = expandSchema(schema, []string{"#/definitions/anyofBoth"}, resolver, basePath)
   493  	require.NoError(t, err)
   494  	require.NotNil(t, s)
   495  
   496  	schema = *s
   497  	assert.Empty(t, schema.AnyOf[0].Items.Schema.Ref.String())
   498  	assert.Empty(t, schema.AnyOf[1].Items.Schema.Ref.String())
   499  	assert.Equal(t, *schema.AnyOf[0].Items.Schema, spec.Definitions["brand"])
   500  	assert.Equal(t, *schema.AnyOf[1].Items.Schema, spec.Definitions["tag"])
   501  
   502  	// verify expanded schema for OneOfBoth, in the returned schema [expand oneOf]
   503  	schema = spec.Definitions["oneofBoth"]
   504  	s, err = expandSchema(schema, []string{"#/definitions/oneofBoth"}, resolver, basePath)
   505  	require.NoError(t, err)
   506  	require.NotNil(t, s)
   507  
   508  	schema = *s
   509  	assert.Empty(t, schema.OneOf[0].Items.Schema.Ref.String())
   510  	assert.Empty(t, schema.OneOf[1].Items.Schema.Ref.String())
   511  	assert.Equal(t, *schema.OneOf[0].Items.Schema, spec.Definitions["brand"])
   512  	assert.Equal(t, *schema.OneOf[1].Items.Schema, spec.Definitions["tag"])
   513  
   514  	// verify expanded schema for NotSomething, in the returned schema [expand not]
   515  	schema = spec.Definitions["notSomething"]
   516  	s, err = expandSchema(schema, []string{"#/definitions/notSomething"}, resolver, basePath)
   517  	require.NoError(t, err)
   518  	require.NotNil(t, s)
   519  
   520  	schema = *s
   521  	assert.Empty(t, schema.Not.Items.Schema.Ref.String())
   522  	assert.Equal(t, *schema.Not.Items.Schema, spec.Definitions["tag"])
   523  
   524  	// verify expanded schema for WithAdditional, in the returned schema [expand additionalProperties]
   525  	schema = spec.Definitions["withAdditional"]
   526  	s, err = expandSchema(schema, []string{"#/definitions/withAdditional"}, resolver, basePath)
   527  	require.NoError(t, err)
   528  	require.NotNil(t, s)
   529  
   530  	schema = *s
   531  	assert.Empty(t, schema.AdditionalProperties.Schema.Items.Schema.Ref.String())
   532  	assert.Equal(t, *schema.AdditionalProperties.Schema.Items.Schema, spec.Definitions["tag"])
   533  
   534  	// verify expanded schema for WithAdditionalItems, in the returned schema [expand additionalItems]
   535  	schema = spec.Definitions["withAdditionalItems"]
   536  	s, err = expandSchema(schema, []string{"#/definitions/withAdditionalItems"}, resolver, basePath)
   537  	require.NoError(t, err)
   538  	require.NotNil(t, s)
   539  
   540  	schema = *s
   541  	assert.Empty(t, schema.AdditionalItems.Schema.Items.Schema.Ref.String())
   542  	assert.Equal(t, *schema.AdditionalItems.Schema.Items.Schema, spec.Definitions["tag"])
   543  
   544  	// verify expanded schema for WithPattern, in the returned schema [expand PatternProperties]
   545  	schema = spec.Definitions["withPattern"]
   546  	s, err = expandSchema(schema, []string{"#/definitions/withPattern"}, resolver, basePath)
   547  	require.NoError(t, err)
   548  	require.NotNil(t, s)
   549  
   550  	schema = *s
   551  	prop := schema.PatternProperties["^x-ab"]
   552  	assert.Empty(t, prop.Items.Schema.Ref.String())
   553  	assert.Equal(t, *prop.Items.Schema, spec.Definitions["tag"])
   554  
   555  	// verify expanded schema for Deps, in the returned schema [expand dependencies]
   556  	schema = spec.Definitions["deps"]
   557  	s, err = expandSchema(schema, []string{"#/definitions/deps"}, resolver, basePath)
   558  	require.NoError(t, err)
   559  	require.NotNil(t, s)
   560  
   561  	schema = *s
   562  	prop2 := schema.Dependencies["something"]
   563  	assert.Empty(t, prop2.Schema.Items.Schema.Ref.String())
   564  	assert.Equal(t, *prop2.Schema.Items.Schema, spec.Definitions["tag"])
   565  
   566  	// verify expanded schema for Defined, in the returned schema [expand nested definitions]
   567  	schema = spec.Definitions["defined"]
   568  	s, err = expandSchema(schema, []string{"#/definitions/defined"}, resolver, basePath)
   569  	require.NoError(t, err)
   570  	require.NotNil(t, s)
   571  
   572  	schema = *s
   573  	prop = schema.Definitions["something"]
   574  	assert.Empty(t, prop.Items.Schema.Ref.String())
   575  	assert.Equal(t, *prop.Items.Schema, spec.Definitions["tag"])
   576  }
   577  
   578  func TestExpand_InternalSchemas1(t *testing.T) {
   579  	basePath := normalizeBase(filepath.Join("fixtures", "expansion", "schemas1.json"))
   580  
   581  	carsDoc, err := jsonDoc(basePath)
   582  	require.NoError(t, err)
   583  
   584  	spec := new(Swagger)
   585  	require.NoError(t, json.Unmarshal(carsDoc, spec))
   586  
   587  	resolver := defaultSchemaLoader(spec, nil, nil, nil)
   588  
   589  	schema := spec.Definitions["car"]
   590  	oldBrand := schema.Properties["brand"]
   591  	assert.NotEmpty(t, oldBrand.Ref.String())
   592  	assert.NotEqual(t, spec.Definitions["brand"], oldBrand)
   593  
   594  	s, err := expandSchema(schema, []string{"#/definitions/car"}, resolver, basePath)
   595  	require.NoError(t, err)
   596  	require.NotNil(t, s)
   597  
   598  	schema = *s
   599  
   600  	newBrand := schema.Properties["brand"]
   601  	assert.Empty(t, newBrand.Ref.String())
   602  	assert.Equal(t, spec.Definitions["brand"], newBrand)
   603  
   604  	schema = spec.Definitions["truck"]
   605  	assert.NotEmpty(t, schema.Ref.String())
   606  
   607  	s, err = expandSchema(schema, []string{"#/definitions/truck"}, resolver, basePath)
   608  	require.NoError(t, err)
   609  	require.NotNil(t, s)
   610  
   611  	schema = *s
   612  	assert.Empty(t, schema.Ref.String())
   613  	assert.Equal(t, spec.Definitions["car"], schema)
   614  
   615  	sch := new(Schema)
   616  	_, err = expandSchema(*sch, []string{""}, resolver, basePath)
   617  	require.NoError(t, err)
   618  
   619  	schema = spec.Definitions["batch"]
   620  	s, err = expandSchema(schema, []string{"#/definitions/batch"}, resolver, basePath)
   621  	require.NoError(t, err)
   622  	require.NotNil(t, s)
   623  
   624  	schema = *s
   625  	assert.Empty(t, schema.Items.Schema.Ref.String())
   626  	assert.Equal(t, *schema.Items.Schema, spec.Definitions["brand"])
   627  
   628  	schema = spec.Definitions["batch2"]
   629  	s, err = expandSchema(schema, []string{"#/definitions/batch2"}, resolver, basePath)
   630  	require.NoError(t, err)
   631  	require.NotNil(t, s)
   632  
   633  	schema = *s
   634  	assert.Empty(t, schema.Items.Schemas[0].Ref.String())
   635  	assert.Empty(t, schema.Items.Schemas[1].Ref.String())
   636  	assert.Equal(t, schema.Items.Schemas[0], spec.Definitions["brand"])
   637  	assert.Equal(t, schema.Items.Schemas[1], spec.Definitions["tag"])
   638  
   639  	schema = spec.Definitions["allofBoth"]
   640  	s, err = expandSchema(schema, []string{"#/definitions/allofBoth"}, resolver, basePath)
   641  	require.NoError(t, err)
   642  	require.NotNil(t, s)
   643  
   644  	schema = *s
   645  	assert.Empty(t, schema.AllOf[0].Ref.String())
   646  	assert.Empty(t, schema.AllOf[1].Ref.String())
   647  	assert.Equal(t, schema.AllOf[0], spec.Definitions["brand"])
   648  	assert.Equal(t, schema.AllOf[1], spec.Definitions["tag"])
   649  
   650  	schema = spec.Definitions["anyofBoth"]
   651  	s, err = expandSchema(schema, []string{"#/definitions/anyofBoth"}, resolver, basePath)
   652  	require.NoError(t, err)
   653  	require.NotNil(t, s)
   654  
   655  	schema = *s
   656  	assert.Empty(t, schema.AnyOf[0].Ref.String())
   657  	assert.Empty(t, schema.AnyOf[1].Ref.String())
   658  	assert.Equal(t, schema.AnyOf[0], spec.Definitions["brand"])
   659  	assert.Equal(t, schema.AnyOf[1], spec.Definitions["tag"])
   660  
   661  	schema = spec.Definitions["oneofBoth"]
   662  	s, err = expandSchema(schema, []string{"#/definitions/oneofBoth"}, resolver, basePath)
   663  	require.NoError(t, err)
   664  	require.NotNil(t, s)
   665  
   666  	schema = *s
   667  	assert.Empty(t, schema.OneOf[0].Ref.String())
   668  	assert.Empty(t, schema.OneOf[1].Ref.String())
   669  	assert.Equal(t, schema.OneOf[0], spec.Definitions["brand"])
   670  	assert.Equal(t, schema.OneOf[1], spec.Definitions["tag"])
   671  
   672  	schema = spec.Definitions["notSomething"]
   673  	s, err = expandSchema(schema, []string{"#/definitions/notSomething"}, resolver, basePath)
   674  	require.NoError(t, err)
   675  	require.NotNil(t, s)
   676  
   677  	schema = *s
   678  	assert.Empty(t, schema.Not.Ref.String())
   679  	assert.Equal(t, *schema.Not, spec.Definitions["tag"])
   680  
   681  	schema = spec.Definitions["withAdditional"]
   682  	s, err = expandSchema(schema, []string{"#/definitions/withAdditional"}, resolver, basePath)
   683  	require.NoError(t, err)
   684  	require.NotNil(t, s)
   685  
   686  	schema = *s
   687  	assert.Empty(t, schema.AdditionalProperties.Schema.Ref.String())
   688  	assert.Equal(t, *schema.AdditionalProperties.Schema, spec.Definitions["tag"])
   689  
   690  	schema = spec.Definitions["withAdditionalItems"]
   691  	s, err = expandSchema(schema, []string{"#/definitions/withAdditionalItems"}, resolver, basePath)
   692  	require.NoError(t, err)
   693  	require.NotNil(t, s)
   694  
   695  	schema = *s
   696  	assert.Empty(t, schema.AdditionalItems.Schema.Ref.String())
   697  	assert.Equal(t, *schema.AdditionalItems.Schema, spec.Definitions["tag"])
   698  
   699  	schema = spec.Definitions["withPattern"]
   700  	s, err = expandSchema(schema, []string{"#/definitions/withPattern"}, resolver, basePath)
   701  	require.NoError(t, err)
   702  	require.NotNil(t, s)
   703  
   704  	schema = *s
   705  	prop := schema.PatternProperties["^x-ab"]
   706  	assert.Empty(t, prop.Ref.String())
   707  	assert.Equal(t, prop, spec.Definitions["tag"])
   708  
   709  	schema = spec.Definitions["deps"]
   710  	s, err = expandSchema(schema, []string{"#/definitions/deps"}, resolver, basePath)
   711  	require.NoError(t, err)
   712  	require.NotNil(t, s)
   713  
   714  	schema = *s
   715  	prop2 := schema.Dependencies["something"]
   716  	assert.Empty(t, prop2.Schema.Ref.String())
   717  	assert.Equal(t, *prop2.Schema, spec.Definitions["tag"])
   718  
   719  	schema = spec.Definitions["defined"]
   720  	s, err = expandSchema(schema, []string{"#/definitions/defined"}, resolver, basePath)
   721  	require.NoError(t, err)
   722  	require.NotNil(t, s)
   723  
   724  	schema = *s
   725  	prop = schema.Definitions["something"]
   726  	assert.Empty(t, prop.Ref.String())
   727  	assert.Equal(t, prop, spec.Definitions["tag"])
   728  }
   729  
   730  func TestExpand_RelativeBaseURI(t *testing.T) {
   731  	server := httptest.NewServer(http.FileServer(http.Dir("fixtures/remote")))
   732  	defer server.Close()
   733  
   734  	spec := new(Swagger)
   735  
   736  	require.NoError(t, ExpandSpec(spec, nil))
   737  
   738  	// this a spec on local fs...
   739  	specDoc, err := jsonDoc("fixtures/remote/all-the-things.json")
   740  	require.NoError(t, err)
   741  
   742  	spec = new(Swagger)
   743  	require.NoError(t, json.Unmarshal(specDoc, spec))
   744  
   745  	pet := spec.Definitions["pet"]
   746  	errorModel := spec.Definitions["errorModel"]
   747  	petResponse := spec.Responses["petResponse"]
   748  	petResponse.Schema = &pet
   749  	stringResponse := spec.Responses["stringResponse"]
   750  	tagParam := spec.Parameters["tag"]
   751  	idParam := spec.Parameters["idParam"]
   752  	anotherPet := spec.Responses["anotherPet"]
   753  
   754  	// ... with some $ref with http scheme
   755  	anotherPet.Ref = MustCreateRef(server.URL + "/" + anotherPet.Ref.String())
   756  
   757  	opts := &ExpandOptions{
   758  		RelativeBase: server.URL + "/all-the-things.json",
   759  	}
   760  
   761  	require.NoError(t, ExpandResponse(&anotherPet, opts.RelativeBase))
   762  
   763  	spec.Responses["anotherPet"] = anotherPet
   764  
   765  	circularA := spec.Responses["circularA"]
   766  	circularA.Ref = MustCreateRef(server.URL + "/" + circularA.Ref.String())
   767  
   768  	require.NoError(t, ExpandResponse(&circularA, opts.RelativeBase))
   769  
   770  	// circularA is self-referencing: results in an empty response: this is okay and expected
   771  	// for the expand use case. The flatten use case should however be expected to fail on this.
   772  	assert.Empty(t, circularA.Description)
   773  	assert.Empty(t, circularA.Ref)
   774  
   775  	spec.Responses["circularA"] = circularA
   776  
   777  	// expand again, no issue should arise
   778  	require.NoError(t, ExpandSpec(spec, opts))
   779  
   780  	// backRef navigates back to the root document (relative $ref)
   781  	backRef := spec.Responses["backRef"]
   782  	require.NoError(t, ExpandResponse(&backRef, opts.RelativeBase))
   783  	assert.Equal(t, "pet response", backRef.Description)
   784  	assert.NotEmpty(t, backRef.Schema)
   785  	assert.Empty(t, backRef.Ref)
   786  
   787  	assert.Equal(t, tagParam, spec.Parameters["query"])
   788  
   789  	assert.Equal(t, petResponse, spec.Responses["petResponse"])
   790  	assert.Equal(t, petResponse, spec.Responses["anotherPet"])
   791  	assert.Equal(t, petResponse, spec.Paths.Paths["/pets"].Post.Responses.StatusCodeResponses[200])
   792  	assert.Equal(t, petResponse, spec.Paths.Paths["/"].Get.Responses.StatusCodeResponses[200])
   793  
   794  	assert.Equal(t, pet, *spec.Paths.Paths["/pets"].Get.Responses.StatusCodeResponses[200].Schema.Items.Schema)
   795  	assert.Equal(t, pet, *spec.Responses["petResponse"].Schema)
   796  	assert.Equal(t, pet, spec.Definitions["petInput"].AllOf[0])
   797  
   798  	assert.Equal(t, spec.Definitions["petInput"], *spec.Paths.Paths["/pets"].Post.Parameters[0].Schema)
   799  
   800  	assert.Equal(t, stringResponse, *spec.Paths.Paths["/"].Get.Responses.Default)
   801  
   802  	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Get.Responses.Default.Schema)
   803  	assert.Equal(t, errorModel, *spec.Paths.Paths["/pets"].Post.Responses.Default.Schema)
   804  
   805  	pi := spec.Paths.Paths["/pets/{id}"]
   806  	assert.Equal(t, idParam, pi.Get.Parameters[0])
   807  	assert.Equal(t, petResponse, pi.Get.Responses.StatusCodeResponses[200])
   808  	assert.Equal(t, idParam, pi.Delete.Parameters[0])
   809  
   810  	assert.Equal(t, errorModel, *pi.Get.Responses.Default.Schema)
   811  	assert.Equal(t, errorModel, *pi.Delete.Responses.Default.Schema)
   812  }
   813  
   814  func resolutionContextServer() *httptest.Server {
   815  	var servedAt string
   816  	server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
   817  		if req.URL.Path == "/resolution.json" {
   818  
   819  			b, _ := os.ReadFile(filepath.Join(specs, "resolution.json"))
   820  			var ctnt map[string]interface{}
   821  			_ = json.Unmarshal(b, &ctnt)
   822  			ctnt["id"] = servedAt
   823  
   824  			rw.Header().Set("Content-Type", "application/json")
   825  			rw.WriteHeader(http.StatusOK)
   826  			bb, _ := json.Marshal(ctnt)
   827  			_, _ = rw.Write(bb)
   828  			return
   829  		}
   830  		if req.URL.Path == "/resolution2.json" {
   831  			b, _ := os.ReadFile(filepath.Join(specs, "resolution2.json"))
   832  			var ctnt map[string]interface{}
   833  			_ = json.Unmarshal(b, &ctnt)
   834  			ctnt["id"] = servedAt
   835  
   836  			rw.Header().Set("Content-Type", "application/json")
   837  			bb, _ := json.Marshal(ctnt)
   838  			_, _ = rw.Write(bb)
   839  			return
   840  		}
   841  
   842  		if req.URL.Path == "/boolProp.json" {
   843  			rw.Header().Set("Content-Type", "application/json")
   844  			b, _ := json.Marshal(map[string]interface{}{
   845  				"type": "boolean",
   846  			})
   847  			_, _ = rw.Write(b)
   848  			return
   849  		}
   850  
   851  		if req.URL.Path == "/deeper/stringProp.json" {
   852  			rw.Header().Set("Content-Type", "application/json")
   853  			b, _ := json.Marshal(map[string]interface{}{
   854  				"type": "string",
   855  			})
   856  			_, _ = rw.Write(b)
   857  			return
   858  		}
   859  
   860  		if req.URL.Path == "/deeper/arrayProp.json" {
   861  			rw.Header().Set("Content-Type", "application/json")
   862  			b, _ := json.Marshal(map[string]interface{}{
   863  				"type": "array",
   864  				"items": map[string]interface{}{
   865  					"type": "file",
   866  				},
   867  			})
   868  			_, _ = rw.Write(b)
   869  			return
   870  		}
   871  
   872  		rw.WriteHeader(http.StatusNotFound)
   873  	}))
   874  	servedAt = server.URL
   875  	return server
   876  }
   877  
   878  func TestExpand_RemoteRefWithResolutionContext(t *testing.T) {
   879  	server := resolutionContextServer()
   880  	defer server.Close()
   881  
   882  	tgt := RefSchema(server.URL + "/resolution.json#/definitions/bool")
   883  	require.NoError(t, ExpandSchema(tgt, nil, nil))
   884  
   885  	assert.Equal(t, StringOrArray([]string{"boolean"}), tgt.Type)
   886  	assert.Empty(t, tgt.Ref)
   887  }
   888  
   889  func TestExpandRemoteRef_WithNestedResolutionContext(t *testing.T) {
   890  	server := resolutionContextServer()
   891  	defer server.Close()
   892  
   893  	tgt := RefSchema(server.URL + "/resolution.json#/items")
   894  	require.NoError(t, ExpandSchema(tgt, nil, nil))
   895  
   896  	require.Empty(t, tgt.Ref)
   897  	require.NotNil(t, tgt.Items)
   898  	require.NotNil(t, tgt.Schema)
   899  	assert.Equal(t, "deeper/", tgt.ID) // schema id is preserved
   900  
   901  	assert.Equal(t, StringOrArray([]string{"string"}), tgt.Items.Schema.Type)
   902  	assert.Empty(t, tgt.Items.Schema.Ref)
   903  }
   904  
   905  /*
   906     This next test will have to wait until we do full $ID analysis for every subschema on every file that is referenced
   907  // For now, TestExpandRemoteRef_WithNestedResolutionContext replaces this next test
   908  func TestExpandRemoteRef_WithNestedResolutionContext_WithParentID(t *testing.T) {
   909   	server := resolutionContextServer()
   910   	defer server.Close()
   911  
   912  	tgt := RefSchema(server.URL + "/resolution.json#/items/items")
   913  	require.NoError(t, ExpandSchema(tgt, nil, nil))
   914  	t.Logf("%s", asJSON(t, tgt))
   915  
   916  	assert.Equal(t, StringOrArray([]string{"string"}), tgt.Type)
   917  }
   918  */
   919  
   920  func TestExpand_RemoteRefWithNestedResolutionContextWithFragment(t *testing.T) {
   921  	server := resolutionContextServer()
   922  	defer server.Close()
   923  
   924  	tgt := RefSchema(server.URL + "/resolution2.json#/items")
   925  	require.NoError(t, ExpandSchema(tgt, nil, nil))
   926  
   927  	require.Empty(t, tgt.Ref)
   928  	require.NotNil(t, tgt.Items)
   929  	require.NotNil(t, tgt.Schema)
   930  	assert.Equal(t, "deeper/", tgt.ID) // schema id is preserved
   931  
   932  	assert.Equal(t, StringOrArray([]string{"file"}), tgt.Items.Schema.Type)
   933  	assert.Empty(t, tgt.Items.Schema.Ref)
   934  }
   935  
   936  func TestExpand_TransitiveRefs(t *testing.T) {
   937  	basePath := filepath.Join(specs, "todos.json")
   938  
   939  	rawSpec, err := os.ReadFile(basePath)
   940  	require.NoError(t, err)
   941  
   942  	var spec *Swagger
   943  	require.NoError(t, json.Unmarshal(rawSpec, &spec))
   944  
   945  	opts := &ExpandOptions{
   946  		RelativeBase: basePath,
   947  	}
   948  
   949  	require.NoError(t, ExpandSpec(spec, opts))
   950  
   951  	assert.Equal(t, "todos.stoplight.io", spec.Host) // i.e. not empty
   952  	jazon := asJSON(t, spec)
   953  
   954  	// verify that the spec has been fully expanded
   955  	assertNoRef(t, jazon)
   956  }
   957  
   958  func TestExpand_SchemaWithRoot(t *testing.T) {
   959  	root := new(Swagger)
   960  	require.NoError(t, json.Unmarshal(PetStoreJSONMessage, root))
   961  
   962  	// 1. remove ID from root definition
   963  	origPet := root.Definitions["Pet"]
   964  	newPet := origPet
   965  	newPet.ID = ""
   966  	root.Definitions["Pet"] = newPet
   967  	expandRootWithID(t, root, withoutSchemaID)
   968  
   969  	// 2. put back ID in Pet definition
   970  	// nested $ref should fail
   971  	root.Definitions["Pet"] = origPet
   972  	expandRootWithID(t, root, withSchemaID)
   973  }
   974  
   975  func expandRootWithID(t testing.TB, root *Swagger, testcase string) {
   976  	t.Logf("case: expanding $ref to schema without ID, with nested $ref with %s ID", testcase)
   977  	sch := RefSchema("#/definitions/newPet")
   978  	err := ExpandSchema(sch, root, nil)
   979  
   980  	if testcase == withSchemaID {
   981  		require.Errorf(t, err, "expected %s NOT to expand properly because of the ID in the parent schema", sch.Ref.String())
   982  	} else {
   983  		require.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
   984  
   985  		// verify that the spec has been fully expanded
   986  		jazon := asJSON(t, sch)
   987  		assertNoRef(t, jazon)
   988  	}
   989  
   990  	t.Log("case: expanding $ref to schema without nested $ref")
   991  	sch = RefSchema("#/definitions/Category")
   992  	require.NoErrorf(t, ExpandSchema(sch, root, nil), "expected %s to expand properly", sch.Ref.String())
   993  
   994  	t.Logf("case: expanding $ref to schema with %s ID and nested $ref", testcase)
   995  	sch = RefSchema("#/definitions/Pet")
   996  	err = ExpandSchema(sch, root, nil)
   997  
   998  	if testcase == withSchemaID {
   999  		require.Errorf(t, err, "expected %s NOT to expand properly because of the ID in the parent schema", sch.Ref.String())
  1000  	} else {
  1001  		require.NoErrorf(t, err, "expected %s to expand properly", sch.Ref.String())
  1002  
  1003  		// verify that the spec has been fully expanded
  1004  		jazon := asJSON(t, sch)
  1005  		assertNoRef(t, jazon)
  1006  	}
  1007  }
  1008  
  1009  func TestExpand_PathItem(t *testing.T) {
  1010  	jazon, _ := expandThisOrDieTrying(t, pathItemsFixture)
  1011  	assert.JSONEq(t, `{
  1012           "swagger": "2.0",
  1013           "info": {
  1014            "title": "PathItems refs",
  1015            "version": "1.0"
  1016           },
  1017           "paths": {
  1018            "/todos": {
  1019             "get": {
  1020              "responses": {
  1021               "200": {
  1022                "description": "List Todos",
  1023                "schema": {
  1024                 "type": "array",
  1025                 "items": {
  1026                  "type": "string"
  1027                 }
  1028                }
  1029               },
  1030               "404": {
  1031                "description": "error"
  1032               }
  1033              }
  1034             }
  1035            }
  1036           }
  1037  			 }`, jazon)
  1038  }
  1039  
  1040  func TestExpand_ExtraItems(t *testing.T) {
  1041  	jazon, _ := expandThisOrDieTrying(t, extraRefFixture)
  1042  	assert.JSONEq(t, `{
  1043           "schemes": [
  1044            "http"
  1045           ],
  1046           "swagger": "2.0",
  1047           "info": {
  1048            "title": "Supported, but non Swagger 20 compliant $ref constructs",
  1049            "version": "2.1.0"
  1050           },
  1051           "host": "item.com",
  1052           "basePath": "/extraRefs",
  1053           "paths": {
  1054            "/employees": {
  1055             "get": {
  1056              "summary": "List Employee Types",
  1057              "operationId": "LIST-Employees",
  1058              "parameters": [
  1059               {
  1060  							"description": "unsupported $ref in simple param",
  1061                "type": "array",
  1062                "items": {
  1063                 "$ref": "#/definitions/arrayType"
  1064                },
  1065                "name": "myQueryParam",
  1066                "in": "query"
  1067               }
  1068              ],
  1069              "responses": {
  1070               "200": {
  1071  							"description": "unsupported $ref in header",
  1072                "schema": {
  1073                 "type": "string"
  1074                },
  1075                "headers": {
  1076                 "X-header": {
  1077                    "type": "array",
  1078                    "items": {
  1079                      "$ref": "#/definitions/headerType"
  1080                    }
  1081  							  }
  1082                }
  1083               }
  1084              }
  1085             }
  1086            }
  1087           },
  1088           "definitions": {
  1089            "arrayType": {
  1090             "type": "integer",
  1091             "format": "int32"
  1092            },
  1093            "headerType": {
  1094             "type": "string",
  1095             "format": "uuid"
  1096            }
  1097           }
  1098  			 }`, jazon)
  1099  }
  1100  
  1101  func TestExpand_Issue145(t *testing.T) {
  1102  	cwd, err := os.Getwd()
  1103  	require.NoError(t, err)
  1104  	pseudoRoot := normalizeBase(filepath.Join(cwd, rootBase))
  1105  
  1106  	// assert the internal behavior of baseForRoot()
  1107  	t.Run("with nil root, empty cache", func(t *testing.T) {
  1108  		cache := defaultResolutionCache()
  1109  		require.Equal(t, pseudoRoot, baseForRoot(nil, cache))
  1110  
  1111  		t.Run("empty root is cached", func(t *testing.T) {
  1112  			value, ok := cache.Get(pseudoRoot)
  1113  			require.True(t, ok) // found in cache
  1114  			asMap, ok := value.(map[string]interface{})
  1115  			require.True(t, ok)
  1116  			require.Empty(t, asMap)
  1117  		})
  1118  	})
  1119  
  1120  	t.Run("with non-nil root, empty cache", func(t *testing.T) {
  1121  		cache := defaultResolutionCache()
  1122  		require.Equal(t, pseudoRoot, baseForRoot(map[string]interface{}{"key": "arbitrary"}, cache))
  1123  
  1124  		t.Run("non-empty root is cached", func(t *testing.T) {
  1125  			value, ok := cache.Get(pseudoRoot)
  1126  			require.True(t, ok) // found in cache
  1127  			asMap, ok := value.(map[string]interface{})
  1128  			require.True(t, ok)
  1129  			require.Contains(t, asMap, "key")
  1130  			require.Equal(t, "arbitrary", asMap["key"])
  1131  		})
  1132  
  1133  		t.Run("with nil root, non-empty cache", func(t *testing.T) {
  1134  			require.Equal(t, pseudoRoot, baseForRoot(nil, cache))
  1135  
  1136  			t.Run("non-empty root is kept", func(t *testing.T) {
  1137  				value, ok := cache.Get(pseudoRoot)
  1138  				require.True(t, ok) // found in cache
  1139  				asMap, ok := value.(map[string]interface{})
  1140  				require.True(t, ok)
  1141  				require.Contains(t, asMap, "key")
  1142  				require.Equal(t, "arbitrary", asMap["key"])
  1143  			})
  1144  		})
  1145  	})
  1146  }
  1147  
  1148  // PetStore20 json doc for swagger 2.0 pet store
  1149  const PetStore20 = `{
  1150    "swagger": "2.0",
  1151    "info": {
  1152      "version": "1.0.0",
  1153      "title": "Swagger Petstore",
  1154      "contact": {
  1155        "name": "Wordnik API Team",
  1156        "url": "http://developer.wordnik.com"
  1157      },
  1158      "license": {
  1159        "name": "Creative Commons 4.0 International",
  1160        "url": "http://creativecommons.org/licenses/by/4.0/"
  1161      }
  1162    },
  1163    "host": "petstore.swagger.wordnik.com",
  1164    "basePath": "/api",
  1165    "schemes": [
  1166      "http"
  1167    ],
  1168    "paths": {
  1169      "/pets": {
  1170        "get": {
  1171          "security": [
  1172            {
  1173              "basic": []
  1174            }
  1175          ],
  1176          "tags": [ "Pet Operations" ],
  1177          "operationId": "getAllPets",
  1178          "parameters": [
  1179            {
  1180              "name": "status",
  1181              "in": "query",
  1182              "description": "The status to filter by",
  1183              "type": "string"
  1184            },
  1185            {
  1186              "name": "limit",
  1187              "in": "query",
  1188              "description": "The maximum number of results to return",
  1189              "type": "integer",
  1190  						"format": "int64"
  1191            }
  1192          ],
  1193          "summary": "Finds all pets in the system",
  1194          "responses": {
  1195            "200": {
  1196              "description": "Pet response",
  1197              "schema": {
  1198                "type": "array",
  1199                "items": {
  1200                  "$ref": "#/definitions/Pet"
  1201                }
  1202              }
  1203            },
  1204            "default": {
  1205              "description": "Unexpected error",
  1206              "schema": {
  1207                "$ref": "#/definitions/Error"
  1208              }
  1209            }
  1210          }
  1211        },
  1212        "post": {
  1213          "security": [
  1214            {
  1215              "basic": []
  1216            }
  1217          ],
  1218          "tags": [ "Pet Operations" ],
  1219          "operationId": "createPet",
  1220          "summary": "Creates a new pet",
  1221          "consumes": ["application/x-yaml"],
  1222          "produces": ["application/x-yaml"],
  1223          "parameters": [
  1224            {
  1225              "name": "pet",
  1226              "in": "body",
  1227              "description": "The Pet to create",
  1228              "required": true,
  1229              "schema": {
  1230                "$ref": "#/definitions/newPet"
  1231              }
  1232            }
  1233          ],
  1234          "responses": {
  1235            "200": {
  1236              "description": "Created Pet response",
  1237              "schema": {
  1238                "$ref": "#/definitions/Pet"
  1239              }
  1240            },
  1241            "default": {
  1242              "description": "Unexpected error",
  1243              "schema": {
  1244                "$ref": "#/definitions/Error"
  1245              }
  1246            }
  1247          }
  1248        }
  1249      },
  1250      "/pets/{id}": {
  1251        "delete": {
  1252          "security": [
  1253            {
  1254              "apiKey": []
  1255            }
  1256          ],
  1257          "description": "Deletes the Pet by id",
  1258          "operationId": "deletePet",
  1259          "parameters": [
  1260            {
  1261              "name": "id",
  1262              "in": "path",
  1263              "description": "ID of pet to delete",
  1264              "required": true,
  1265              "type": "integer",
  1266              "format": "int64"
  1267            }
  1268          ],
  1269          "responses": {
  1270            "204": {
  1271              "description": "pet deleted"
  1272            },
  1273            "default": {
  1274              "description": "unexpected error",
  1275              "schema": {
  1276                "$ref": "#/definitions/Error"
  1277              }
  1278            }
  1279          }
  1280        },
  1281        "get": {
  1282          "tags": [ "Pet Operations" ],
  1283          "operationId": "getPetById",
  1284          "summary": "Finds the pet by id",
  1285          "responses": {
  1286            "200": {
  1287              "description": "Pet response",
  1288              "schema": {
  1289                "$ref": "#/definitions/Pet"
  1290              }
  1291            },
  1292            "default": {
  1293              "description": "Unexpected error",
  1294              "schema": {
  1295                "$ref": "#/definitions/Error"
  1296              }
  1297            }
  1298          }
  1299        },
  1300        "parameters": [
  1301          {
  1302            "name": "id",
  1303            "in": "path",
  1304            "description": "ID of pet",
  1305            "required": true,
  1306            "type": "integer",
  1307            "format": "int64"
  1308          }
  1309        ]
  1310      }
  1311    },
  1312    "definitions": {
  1313      "Category": {
  1314        "id": "Category",
  1315        "properties": {
  1316          "id": {
  1317            "format": "int64",
  1318            "type": "integer"
  1319          },
  1320          "name": {
  1321            "type": "string"
  1322          }
  1323        }
  1324      },
  1325      "Pet": {
  1326        "id": "Pet",
  1327        "properties": {
  1328          "category": {
  1329            "$ref": "#/definitions/Category"
  1330          },
  1331          "id": {
  1332            "description": "unique identifier for the pet",
  1333            "format": "int64",
  1334            "maximum": 100.0,
  1335            "minimum": 0.0,
  1336            "type": "integer"
  1337          },
  1338          "name": {
  1339            "type": "string"
  1340          },
  1341          "photoUrls": {
  1342            "items": {
  1343              "type": "string"
  1344            },
  1345            "type": "array"
  1346          },
  1347          "status": {
  1348            "description": "pet status in the store",
  1349            "enum": [
  1350              "available",
  1351              "pending",
  1352              "sold"
  1353            ],
  1354            "type": "string"
  1355          },
  1356          "tags": {
  1357            "items": {
  1358              "$ref": "#/definitions/Tag"
  1359            },
  1360            "type": "array"
  1361          }
  1362        },
  1363        "required": [
  1364          "id",
  1365          "name"
  1366        ]
  1367      },
  1368      "newPet": {
  1369        "anyOf": [
  1370          {
  1371            "$ref": "#/definitions/Pet"
  1372          },
  1373          {
  1374            "required": [
  1375              "name"
  1376            ]
  1377          }
  1378        ]
  1379      },
  1380      "Tag": {
  1381        "id": "Tag",
  1382        "properties": {
  1383          "id": {
  1384            "format": "int64",
  1385            "type": "integer"
  1386          },
  1387          "name": {
  1388            "type": "string"
  1389          }
  1390        }
  1391      },
  1392      "Error": {
  1393        "required": [
  1394          "code",
  1395          "message"
  1396        ],
  1397        "properties": {
  1398          "code": {
  1399            "type": "integer",
  1400            "format": "int32"
  1401          },
  1402          "message": {
  1403            "type": "string"
  1404          }
  1405        }
  1406      }
  1407    },
  1408    "consumes": [
  1409      "application/json",
  1410      "application/xml"
  1411    ],
  1412    "produces": [
  1413      "application/json",
  1414      "application/xml",
  1415      "text/plain",
  1416      "text/html"
  1417    ],
  1418    "securityDefinitions": {
  1419      "basic": {
  1420        "type": "basic"
  1421      },
  1422      "apiKey": {
  1423        "type": "apiKey",
  1424        "in": "header",
  1425        "name": "X-API-KEY"
  1426      }
  1427    }
  1428  }
  1429  `
  1430  

View as plain text