mirror of
https://github.com/LukeHagar/libopenapi.git
synced 2025-12-06 04:20:11 +00:00
1214 lines
35 KiB
Go
1214 lines
35 KiB
Go
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
|
||
// SPDX-License-Identifier: MIT
|
||
package libopenapi
|
||
|
||
import (
|
||
"fmt"
|
||
"log/slog"
|
||
"os"
|
||
"strconv"
|
||
"strings"
|
||
"testing"
|
||
|
||
"github.com/pb33f/libopenapi/datamodel"
|
||
"github.com/pb33f/libopenapi/datamodel/high/base"
|
||
v3high "github.com/pb33f/libopenapi/datamodel/high/v3"
|
||
"github.com/pb33f/libopenapi/orderedmap"
|
||
"github.com/pb33f/libopenapi/utils"
|
||
"github.com/pb33f/libopenapi/what-changed/model"
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
)
|
||
|
||
func TestLoadDocument_Simple_V2(t *testing.T) {
|
||
yml := `swagger: 2.0.1`
|
||
doc, err := NewDocument([]byte(yml))
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, "2.0.1", doc.GetVersion())
|
||
|
||
v2Doc, docErr := doc.BuildV2Model()
|
||
assert.Len(t, docErr, 0)
|
||
assert.NotNil(t, v2Doc)
|
||
assert.NotNil(t, doc.GetSpecInfo())
|
||
|
||
fmt.Print()
|
||
}
|
||
|
||
func TestLoadDocument_Simple_V2_Error(t *testing.T) {
|
||
yml := `swagger: 2.0`
|
||
doc, err := NewDocument([]byte(yml))
|
||
assert.NoError(t, err)
|
||
|
||
v2Doc, docErr := doc.BuildV3Model()
|
||
assert.Len(t, docErr, 1)
|
||
assert.Nil(t, v2Doc)
|
||
}
|
||
|
||
func TestLoadDocument_Simple_V2_Error_BadSpec(t *testing.T) {
|
||
yml := `swagger: 2.0
|
||
definitions:
|
||
thing:
|
||
$ref: bork`
|
||
doc, err := NewDocument([]byte(yml))
|
||
assert.NoError(t, err)
|
||
|
||
v2Doc, docErr := doc.BuildV2Model()
|
||
assert.Len(t, docErr, 3)
|
||
assert.Nil(t, v2Doc)
|
||
}
|
||
|
||
func TestLoadDocument_Simple_V3_Error(t *testing.T) {
|
||
yml := `openapi: 3.0.1`
|
||
doc, err := NewDocument([]byte(yml))
|
||
assert.NoError(t, err)
|
||
|
||
v2Doc, docErr := doc.BuildV2Model()
|
||
assert.Len(t, docErr, 1)
|
||
assert.Nil(t, v2Doc)
|
||
}
|
||
|
||
func TestLoadDocument_Error_V2NoSpec(t *testing.T) {
|
||
doc := new(document) // not how this should be instantiated.
|
||
_, err := doc.BuildV2Model()
|
||
assert.Len(t, err, 1)
|
||
}
|
||
|
||
func TestLoadDocument_Error_V3NoSpec(t *testing.T) {
|
||
doc := new(document) // not how this should be instantiated.
|
||
_, err := doc.BuildV3Model()
|
||
assert.Len(t, err, 1)
|
||
}
|
||
|
||
func TestLoadDocument_Empty(t *testing.T) {
|
||
yml := ``
|
||
_, err := NewDocument([]byte(yml))
|
||
assert.Error(t, err)
|
||
}
|
||
|
||
func TestLoadDocument_Simple_V3(t *testing.T) {
|
||
yml := `openapi: 3.0.1`
|
||
doc, err := NewDocument([]byte(yml))
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, "3.0.1", doc.GetVersion())
|
||
|
||
v3Doc, docErr := doc.BuildV3Model()
|
||
assert.Len(t, docErr, 0)
|
||
assert.NotNil(t, v3Doc)
|
||
}
|
||
|
||
func TestLoadDocument_Simple_V3_Error_BadSpec_BuildModel(t *testing.T) {
|
||
yml := `openapi: 3.0
|
||
paths:
|
||
"/some":
|
||
$ref: bork`
|
||
doc, err := NewDocument([]byte(yml))
|
||
assert.NoError(t, err)
|
||
|
||
doc.BuildV3Model()
|
||
rolo := doc.GetRolodex()
|
||
assert.Len(t, rolo.GetCaughtErrors(), 1)
|
||
}
|
||
|
||
func TestDocument_Serialize_Error(t *testing.T) {
|
||
doc := new(document) // not how this should be instantiated.
|
||
_, err := doc.Serialize()
|
||
assert.Error(t, err)
|
||
}
|
||
|
||
func TestDocument_Serialize(t *testing.T) {
|
||
yml := `openapi: 3.0
|
||
info:
|
||
title: The magic API
|
||
`
|
||
doc, _ := NewDocument([]byte(yml))
|
||
serial, err := doc.Serialize()
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, yml, string(serial))
|
||
}
|
||
|
||
func TestDocument_Serialize_Modified(t *testing.T) {
|
||
yml := `openapi: 3.0
|
||
info:
|
||
title: The magic API
|
||
`
|
||
|
||
ymlModified := `openapi: 3.0
|
||
info:
|
||
title: The magic API - but now, altered!
|
||
`
|
||
doc, _ := NewDocument([]byte(yml))
|
||
|
||
v3Doc, _ := doc.BuildV3Model()
|
||
|
||
v3Doc.Model.Info.GoLow().Title.Mutate("The magic API - but now, altered!")
|
||
|
||
serial, err := doc.Serialize()
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, ymlModified, string(serial))
|
||
}
|
||
|
||
func TestDocument_RoundTrip(t *testing.T) {
|
||
orig := `openapi: 3.1.0
|
||
info:
|
||
title: "The magic API"
|
||
description: |
|
||
A multi-line description
|
||
of the API. That should be retained.
|
||
tags:
|
||
- name: "Burgers"
|
||
security:
|
||
- oauth2: []
|
||
paths:
|
||
"/test":
|
||
parameters:
|
||
- $ref: "#/components/parameters/completed_since"
|
||
post:
|
||
tags:
|
||
- "Burgers"
|
||
operationId: "test"
|
||
requestBody:
|
||
description: Callback payload
|
||
content:
|
||
'application/json':
|
||
schema:
|
||
type: string
|
||
responses:
|
||
"200":
|
||
description: "OK"
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: object
|
||
properties:
|
||
data:
|
||
$ref: "#/components/schemas/test"
|
||
arr:
|
||
type: array
|
||
items:
|
||
$ref: "#/components/schemas/test"
|
||
callbacks:
|
||
BurgerCallback:
|
||
x-break-everything: please
|
||
"{$request.query.queryUrl}":
|
||
post:
|
||
requestBody:
|
||
description: Callback payload
|
||
content:
|
||
application/json:
|
||
schema:
|
||
type: string
|
||
responses:
|
||
'200':
|
||
description: callback successfully processes
|
||
components:
|
||
schemas:
|
||
test:
|
||
type: string
|
||
parameters:
|
||
completed_since:
|
||
in: query
|
||
name: completed_since
|
||
required: false
|
||
explode: false
|
||
schema:
|
||
example: 2012-02-22T02:06:58.158Z
|
||
format: date-time
|
||
type: string
|
||
links:
|
||
LocateBurger:
|
||
operationId: locateBurger
|
||
parameters:
|
||
burgerId: '$response.body#/id'
|
||
description: Go and get a tasty burger
|
||
securitySchemes:
|
||
oauth2:
|
||
description: |-
|
||
We require that applications designed to access the Asana API on behalf of multiple users implement OAuth 2.0.
|
||
Asana supports the Authorization Code Grant flow.
|
||
flows:
|
||
authorizationCode:
|
||
authorizationUrl: https://app.asana.com/-/oauth_authorize
|
||
refreshUrl: https://app.asana.com/-/oauth_token
|
||
scopes:
|
||
default: Provides access to all endpoints documented in our API reference. If no scopes are requested, this scope is assumed by default.
|
||
email: Provides access to the user’s email through the OpenID Connect user info endpoint.
|
||
openid: Provides access to OpenID Connect ID tokens and the OpenID Connect user info endpoint.
|
||
profile: Provides access to the user’s name and profile photo through the OpenID Connect user info endpoint.
|
||
tokenUrl: https://app.asana.com/-/oauth_token
|
||
type: oauth2
|
||
`
|
||
|
||
doc, err := NewDocument([]byte(orig))
|
||
require.NoError(t, err)
|
||
|
||
_, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
|
||
out, err := doc.Render()
|
||
require.NoError(t, err)
|
||
|
||
assert.Equal(t, orig, string(out))
|
||
}
|
||
|
||
func TestDocument_RenderAndReload_ChangeCheck_Burgershop(t *testing.T) {
|
||
bs, _ := os.ReadFile("test_specs/burgershop.openapi.yaml")
|
||
doc, _ := NewDocument(bs)
|
||
doc.BuildV3Model()
|
||
|
||
rend, newDoc, _, _ := doc.RenderAndReload()
|
||
|
||
// compare documents
|
||
compReport, errs := CompareDocuments(doc, newDoc)
|
||
|
||
// should noth be nil.
|
||
assert.Nil(t, errs)
|
||
assert.NotNil(t, rend)
|
||
assert.Nil(t, compReport)
|
||
}
|
||
|
||
func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) {
|
||
bs, _ := os.ReadFile("test_specs/stripe.yaml")
|
||
doc, _ := NewDocument(bs)
|
||
doc.BuildV3Model()
|
||
|
||
_, newDoc, _, _ := doc.RenderAndReload()
|
||
|
||
// compare documents
|
||
compReport, errs := CompareDocuments(doc, newDoc)
|
||
|
||
// get flat list of changes.
|
||
flatChanges := compReport.GetAllChanges()
|
||
|
||
// remove everything that is a description change (stripe has a lot of those from having 519 empty descriptions)
|
||
var filtered []*model.Change
|
||
for i := range flatChanges {
|
||
if flatChanges[i].Property != "description" {
|
||
filtered = append(filtered, flatChanges[i])
|
||
}
|
||
}
|
||
|
||
assert.Nil(t, errs)
|
||
tc := compReport.TotalChanges()
|
||
bc := compReport.TotalBreakingChanges()
|
||
assert.Equal(t, 0, bc)
|
||
assert.Equal(t, 519, tc)
|
||
|
||
// there should be no other changes than the 519 descriptions.
|
||
assert.Equal(t, 0, len(filtered))
|
||
}
|
||
|
||
func TestDocument_RenderAndReload_ChangeCheck_Asana(t *testing.T) {
|
||
bs, _ := os.ReadFile("test_specs/asana.yaml")
|
||
doc, _ := NewDocument(bs)
|
||
doc.BuildV3Model()
|
||
|
||
dat, newDoc, _, _ := doc.RenderAndReload()
|
||
assert.NotNil(t, dat)
|
||
|
||
assert.Equal(t, string(bs), string(dat))
|
||
|
||
// compare documents
|
||
compReport, errs := CompareDocuments(doc, newDoc)
|
||
|
||
// get flat list of changes.
|
||
flatChanges := compReport.GetAllChanges()
|
||
|
||
assert.Nil(t, errs)
|
||
tc := compReport.TotalChanges()
|
||
assert.Equal(t, 0, tc)
|
||
|
||
// there are some properties re-rendered that trigger changes.
|
||
assert.Equal(t, 0, len(flatChanges))
|
||
}
|
||
|
||
func TestDocument_RenderAndReload(t *testing.T) {
|
||
// load an OpenAPI 3 specification from bytes
|
||
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||
|
||
// create a new document from specification bytes
|
||
doc, err := NewDocument(petstore)
|
||
// if anything went wrong, an error is thrown
|
||
if err != nil {
|
||
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||
}
|
||
|
||
// because we know this is a v3 spec, we can build a ready to go model from it.
|
||
m, _ := doc.BuildV3Model()
|
||
|
||
// mutate the model
|
||
h := m.Model
|
||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId = "findACakeInABakery"
|
||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes.GetOrZero("400").Description = "a nice bucket of mice"
|
||
h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags = append(h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, "gurgle", "giggle")
|
||
|
||
h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security = append(h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security,
|
||
&base.SecurityRequirement{Requirements: orderedmap.ToOrderedMap(map[string][]string{
|
||
"pizza-and-cake": {"read:abook", "write:asong"},
|
||
})},
|
||
)
|
||
|
||
h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example = utils.CreateStringNode("I am a teapot, filled with love.")
|
||
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
|
||
|
||
bytes, _, newDocModel, e := doc.RenderAndReload()
|
||
assert.Nil(t, e)
|
||
assert.NotNil(t, bytes)
|
||
|
||
h = newDocModel.Model
|
||
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId)
|
||
assert.Equal(t, "a nice bucket of mice",
|
||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes.GetOrZero("400").Description)
|
||
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
|
||
|
||
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
|
||
yu := h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security
|
||
assert.Equal(t, "read:abook", yu[len(yu)-1].Requirements.GetOrZero("pizza-and-cake")[0])
|
||
|
||
var example string
|
||
_ = h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example.Decode(&example)
|
||
assert.Equal(t, "I am a teapot, filled with love.", example)
|
||
|
||
assert.Equal(t, "https://pb33f.io",
|
||
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl)
|
||
}
|
||
|
||
func TestDocument_Render(t *testing.T) {
|
||
// load an OpenAPI 3 specification from bytes
|
||
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||
|
||
// create a new document from specification bytes
|
||
doc, err := NewDocument(petstore)
|
||
// if anything went wrong, an error is thrown
|
||
if err != nil {
|
||
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||
}
|
||
|
||
// because we know this is a v3 spec, we can build a ready to go model from it.
|
||
m, _ := doc.BuildV3Model()
|
||
|
||
// mutate the model
|
||
h := m.Model
|
||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId = "findACakeInABakery"
|
||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").
|
||
Get.Responses.Codes.GetOrZero("400").Description = "a nice bucket of mice"
|
||
h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags = append(h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, "gurgle", "giggle")
|
||
|
||
h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security = append(h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security,
|
||
&base.SecurityRequirement{Requirements: orderedmap.ToOrderedMap(map[string][]string{
|
||
"pizza-and-cake": {"read:abook", "write:asong"},
|
||
})},
|
||
)
|
||
|
||
h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example = utils.CreateStringNode("I am a teapot, filled with love.")
|
||
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl = "https://pb33f.io"
|
||
|
||
bytes, e := doc.Render()
|
||
assert.NoError(t, e)
|
||
assert.NotNil(t, bytes)
|
||
|
||
newDoc, docErr := NewDocument(bytes)
|
||
|
||
assert.NoError(t, docErr)
|
||
|
||
newDocModel, docErrs := newDoc.BuildV3Model()
|
||
assert.Len(t, docErrs, 0)
|
||
|
||
h = newDocModel.Model
|
||
assert.Equal(t, "findACakeInABakery", h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.OperationId)
|
||
assert.Equal(t, "a nice bucket of mice",
|
||
h.Paths.PathItems.GetOrZero("/pet/findByStatus").Get.Responses.Codes.GetOrZero("400").Description)
|
||
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
|
||
|
||
assert.Len(t, h.Paths.PathItems.GetOrZero("/pet/findByTags").Get.Tags, 3)
|
||
yu := h.Paths.PathItems.GetOrZero("/pet/{petId}").Delete.Security
|
||
assert.Equal(t, "read:abook", yu[len(yu)-1].Requirements.GetOrZero("pizza-and-cake")[0])
|
||
|
||
var example string
|
||
_ = h.Components.Schemas.GetOrZero("Order").Schema().Properties.GetOrZero("status").Schema().Example.Decode(&example)
|
||
assert.Equal(t, "I am a teapot, filled with love.", example)
|
||
|
||
assert.Equal(t, "https://pb33f.io",
|
||
h.Components.SecuritySchemes.GetOrZero("petstore_auth").Flows.Implicit.AuthorizationUrl)
|
||
}
|
||
|
||
func TestDocument_RenderWithLargeIndention(t *testing.T) {
|
||
json := `{
|
||
"openapi": "3.0"
|
||
}`
|
||
doc, _ := NewDocument([]byte(json))
|
||
|
||
doc.BuildV3Model()
|
||
bytes, _ := doc.Render()
|
||
assert.Equal(t, json, string(bytes))
|
||
}
|
||
|
||
func TestDocument_Render_ChangeCheck_Burgershop(t *testing.T) {
|
||
bs, _ := os.ReadFile("test_specs/burgershop.openapi.yaml")
|
||
doc, _ := NewDocument(bs)
|
||
doc.BuildV3Model()
|
||
|
||
rend, _ := doc.Render()
|
||
|
||
newDoc, _ := NewDocument(rend)
|
||
|
||
// compare documents
|
||
compReport, errs := CompareDocuments(doc, newDoc)
|
||
|
||
// should noth be nil.
|
||
assert.Nil(t, errs)
|
||
assert.NotNil(t, rend)
|
||
assert.Nil(t, compReport)
|
||
}
|
||
|
||
func TestDocument_RenderAndReload_Swagger(t *testing.T) {
|
||
petstore, _ := os.ReadFile("test_specs/petstorev2.json")
|
||
doc, _ := NewDocument(petstore)
|
||
doc.BuildV2Model()
|
||
doc.BuildV2Model()
|
||
_, _, _, e := doc.RenderAndReload()
|
||
assert.Len(t, e, 1)
|
||
assert.Equal(t, "this method only supports OpenAPI 3 documents, not Swagger", e[0].Error())
|
||
}
|
||
|
||
func TestDocument_Render_Swagger(t *testing.T) {
|
||
petstore, _ := os.ReadFile("test_specs/petstorev2.json")
|
||
doc, _ := NewDocument(petstore)
|
||
doc.BuildV2Model()
|
||
doc.BuildV2Model()
|
||
_, e := doc.Render()
|
||
assert.Error(t, e)
|
||
assert.Equal(t, "this method only supports OpenAPI 3 documents, not Swagger", e.Error())
|
||
}
|
||
|
||
func TestDocument_BuildModelPreBuild(t *testing.T) {
|
||
petstore, _ := os.ReadFile("test_specs/petstorev3.json")
|
||
doc, e := NewDocument(petstore)
|
||
assert.NoError(t, e)
|
||
doc.BuildV3Model()
|
||
doc.BuildV3Model()
|
||
_, _, _, er := doc.RenderAndReload()
|
||
assert.Len(t, er, 0)
|
||
}
|
||
|
||
func TestDocument_AnyDoc(t *testing.T) {
|
||
anything := []byte(`{"chickens": "3.0.0", "burgers": {"title": "hello"}}`)
|
||
_, e := NewDocumentWithTypeCheck(anything, true)
|
||
assert.NoError(t, e)
|
||
}
|
||
|
||
func TestDocument_AnyDocWithConfig(t *testing.T) {
|
||
anything := []byte(`{"chickens": "3.0.0", "burgers": {"title": "hello"}}`)
|
||
_, e := NewDocumentWithConfiguration(anything, &datamodel.DocumentConfiguration{
|
||
BypassDocumentCheck: true,
|
||
})
|
||
assert.NoError(t, e)
|
||
}
|
||
|
||
func TestDocument_BuildModelCircular(t *testing.T) {
|
||
petstore, _ := os.ReadFile("test_specs/circular-tests.yaml")
|
||
doc, _ := NewDocument(petstore)
|
||
doc.BuildV3Model()
|
||
assert.Len(t, doc.GetRolodex().GetCaughtErrors(), 3)
|
||
}
|
||
|
||
func TestDocument_BuildModelBad(t *testing.T) {
|
||
petstore, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||
doc, _ := NewDocument(petstore)
|
||
doc.BuildV3Model()
|
||
assert.Len(t, doc.GetRolodex().GetCaughtErrors(), 6)
|
||
}
|
||
|
||
func TestDocument_Serialize_JSON_Modified(t *testing.T) {
|
||
json := `{ 'openapi': '3.0',
|
||
'info': {
|
||
'title': 'The magic API'
|
||
}
|
||
}
|
||
`
|
||
jsonModified := `{"info":{"title":"The magic API - but now, altered!"},"openapi":"3.0"}`
|
||
doc, _ := NewDocument([]byte(json))
|
||
|
||
v3Doc, _ := doc.BuildV3Model()
|
||
|
||
// eventually this will be encapsulated up high.
|
||
// mutation does not replace low model, eventually pointers will be used.
|
||
newTitle := v3Doc.Model.Info.GoLow().Title.Mutate("The magic API - but now, altered!")
|
||
v3Doc.Model.Info.GoLow().Title = newTitle
|
||
|
||
assert.Equal(t, "The magic API - but now, altered!", v3Doc.Model.Info.GoLow().Title.GetValue())
|
||
|
||
serial, err := doc.Serialize()
|
||
assert.NoError(t, err)
|
||
assert.Equal(t, jsonModified, string(serial))
|
||
}
|
||
|
||
func TestExtractReference(t *testing.T) {
|
||
data := `
|
||
openapi: "3.1"
|
||
components:
|
||
parameters:
|
||
Param1:
|
||
description: "I am a param"
|
||
paths:
|
||
/something:
|
||
get:
|
||
parameters:
|
||
- $ref: '#/components/parameters/Param1'`
|
||
|
||
doc, err := NewDocument([]byte(data))
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
result, errs := doc.BuildV3Model()
|
||
if len(errs) > 0 {
|
||
panic(errs)
|
||
}
|
||
|
||
// extract operation.
|
||
operation := result.Model.Paths.PathItems.GetOrZero("/something").Get
|
||
|
||
// print it out.
|
||
fmt.Printf("param1: %s, is reference? %t, original reference %s",
|
||
operation.Parameters[0].Description, operation.GoLow().Parameters.Value[0].IsReference(),
|
||
operation.GoLow().Parameters.Value[0].GetReference())
|
||
}
|
||
|
||
func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) {
|
||
burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||
changes, errors := CompareDocuments(originalDoc, updatedDoc)
|
||
assert.Len(t, errors, 6)
|
||
assert.Nil(t, changes)
|
||
}
|
||
|
||
func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) {
|
||
burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml")
|
||
burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml")
|
||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||
assert.Len(t, errors, 6)
|
||
assert.Nil(t, changes)
|
||
}
|
||
|
||
func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) {
|
||
burgerShopOriginal, _ := os.ReadFile("test_specs/petstorev2-badref.json")
|
||
burgerShopUpdated, _ := os.ReadFile("test_specs/petstorev2-badref.json")
|
||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||
assert.Len(t, errors, 14)
|
||
assert.Nil(t, changes)
|
||
}
|
||
|
||
func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) {
|
||
burgerShopOriginal, _ := os.ReadFile("test_specs/petstorev2.json")
|
||
burgerShopUpdated, _ := os.ReadFile("test_specs/petstorev3.json")
|
||
originalDoc, _ := NewDocument(burgerShopOriginal)
|
||
updatedDoc, _ := NewDocument(burgerShopUpdated)
|
||
changes, errors := CompareDocuments(updatedDoc, originalDoc)
|
||
assert.Len(t, errors, 1)
|
||
assert.Nil(t, changes)
|
||
}
|
||
|
||
func TestSchemaRefIsFollowed(t *testing.T) {
|
||
petstore, _ := os.ReadFile("test_specs/ref-followed.yaml")
|
||
|
||
// create a new document from specification bytes
|
||
document, err := NewDocument(petstore)
|
||
// if anything went wrong, an error is thrown
|
||
if err != nil {
|
||
panic(fmt.Sprintf("cannot create new document: %e", err))
|
||
}
|
||
|
||
// because we know this is a v3 spec, we can build a ready to go model from it.
|
||
v3Model, errors := document.BuildV3Model()
|
||
|
||
// if anything went wrong when building the v3 model, a slice of errors will be returned
|
||
if len(errors) > 0 {
|
||
for i := range errors {
|
||
fmt.Printf("error: %e\n", errors[i])
|
||
}
|
||
panic(fmt.Sprintf("cannot create v3 model from document: %d errors reported", len(errors)))
|
||
}
|
||
|
||
// get a count of the number of paths and schemas.
|
||
schemas := v3Model.Model.Components.Schemas
|
||
assert.Equal(t, 4, orderedmap.Len(schemas))
|
||
|
||
fp := schemas.GetOrZero("FP")
|
||
fbsref := schemas.GetOrZero("FBSRef")
|
||
|
||
assert.Equal(t, fp.Schema().Pattern, fbsref.Schema().Pattern)
|
||
assert.Equal(t, fp.Schema().Example, fbsref.Schema().Example)
|
||
|
||
byte := schemas.GetOrZero("Byte")
|
||
uint64 := schemas.GetOrZero("UInt64")
|
||
|
||
assert.Equal(t, uint64.Schema().Format, byte.Schema().Format)
|
||
assert.Equal(t, uint64.Schema().Type, byte.Schema().Type)
|
||
assert.Equal(t, uint64.Schema().Nullable, byte.Schema().Nullable)
|
||
assert.Equal(t, uint64.Schema().Example, byte.Schema().Example)
|
||
assert.Equal(t, uint64.Schema().Minimum, byte.Schema().Minimum)
|
||
}
|
||
|
||
func TestDocument_ParamsAndRefsRender(t *testing.T) {
|
||
d := `openapi: "3.1"
|
||
components:
|
||
parameters:
|
||
limit:
|
||
description: I am a param
|
||
offset:
|
||
description: I am a param
|
||
paths:
|
||
/webhooks:
|
||
get:
|
||
description: Get the compact representation of all webhooks your app has registered for the authenticated user in the given workspace.
|
||
operationId: getWebhooks
|
||
parameters:
|
||
- $ref: '#/components/parameters/limit'
|
||
- $ref: '#/components/parameters/offset'
|
||
- description: The workspace to query for webhooks in.
|
||
example: "1331"
|
||
in: query
|
||
name: workspace
|
||
required: true
|
||
schema:
|
||
type: string
|
||
- description: Only return webhooks for the given resource.
|
||
example: "51648"
|
||
in: query
|
||
name: resource
|
||
schema:
|
||
type: string`
|
||
|
||
doc, err := NewDocument([]byte(d))
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
result, errs := doc.BuildV3Model()
|
||
if len(errs) > 0 {
|
||
panic(errs)
|
||
}
|
||
|
||
// render the document.
|
||
rend, _ := result.Model.Render()
|
||
|
||
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||
}
|
||
|
||
// disabled for now as the host is timing out
|
||
//func TestDocument_RemoteWithoutBaseURL(t *testing.T) {
|
||
//
|
||
// // This test will push the index to do try and locate remote references that use relative references
|
||
// spec := `openapi: 3.0.2
|
||
//info:
|
||
// title: Test
|
||
// version: 1.0.0
|
||
//paths:
|
||
// /test:
|
||
// get:
|
||
// parameters:
|
||
// - $ref: "https://schemas.opengis.net/ogcapi/features/part2/1.0/openapi/ogcapi-features-2.yaml#/components/parameters/crs"`
|
||
//
|
||
// config := datamodel.NewDocumentConfiguration()
|
||
//
|
||
// doc, err := NewDocumentWithConfiguration([]byte(spec), config)
|
||
// if err != nil {
|
||
// panic(err)
|
||
// }
|
||
//
|
||
// result, errs := doc.BuildV3Model()
|
||
// if len(errs) > 0 {
|
||
// panic(errs)
|
||
// }
|
||
//
|
||
// assert.Equal(t, "crs", result.Model.Paths.PathItems.GetOrZero("/test").Get.Parameters[0].Name)
|
||
//}
|
||
|
||
func TestDocument_ExampleMap(t *testing.T) {
|
||
d := `openapi: "3.1"
|
||
components:
|
||
schemas:
|
||
ProjectRequest:
|
||
allOf:
|
||
- properties:
|
||
custom_fields:
|
||
additionalProperties:
|
||
description: '"{custom_field_gid}" => Value (Can be text, number, etc.)'
|
||
type: string
|
||
description: An object where each key is a Custom Field gid and each value is an enum gid, string, or number.
|
||
example:
|
||
"4578152156": Not Started
|
||
"5678904321": On Hold
|
||
type: object
|
||
`
|
||
|
||
doc, err := NewDocument([]byte(d))
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
result, errs := doc.BuildV3Model()
|
||
if len(errs) > 0 {
|
||
panic(errs)
|
||
}
|
||
|
||
// render the document.
|
||
rend, _ := result.Model.Render()
|
||
|
||
assert.Len(t, rend, len(d))
|
||
}
|
||
|
||
func TestDocument_OperationsAsRefs(t *testing.T) {
|
||
ae := `operationId: thisIsAnOperationId
|
||
summary: a test thing
|
||
description: this is a test, that does a test.`
|
||
|
||
_ = os.WriteFile("test-operation.yaml", []byte(ae), 0o644)
|
||
defer os.Remove("test-operation.yaml")
|
||
|
||
d := `openapi: "3.1"
|
||
paths:
|
||
/an/operation:
|
||
get:
|
||
$ref: test-operation.yaml`
|
||
|
||
cf := datamodel.NewDocumentConfiguration()
|
||
cf.BasePath = "."
|
||
cf.FileFilter = []string{"test-operation.yaml"}
|
||
|
||
doc, err := NewDocumentWithConfiguration([]byte(d), cf)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
assert.NotNil(t, doc.GetConfiguration())
|
||
assert.Equal(t, doc.GetConfiguration(), cf)
|
||
|
||
result, errs := doc.BuildV3Model()
|
||
if len(errs) > 0 {
|
||
panic(errs)
|
||
}
|
||
|
||
// render the document.
|
||
rend, _ := result.Model.Render()
|
||
|
||
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||
}
|
||
|
||
func TestDocument_InputAsJSON(t *testing.T) {
|
||
d := `{
|
||
"openapi": "3.1",
|
||
"paths": {
|
||
"/an/operation": {
|
||
"get": {
|
||
"operationId": "thisIsAnOperationId"
|
||
}
|
||
}
|
||
}
|
||
}`
|
||
|
||
doc, err := NewDocumentWithConfiguration([]byte(d), datamodel.NewDocumentConfiguration())
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
_, _ = doc.BuildV3Model()
|
||
|
||
// render the document.
|
||
rend, _, _, _ := doc.RenderAndReload()
|
||
|
||
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||
}
|
||
|
||
func TestDocument_InputAsJSON_LargeIndent(t *testing.T) {
|
||
d := `{
|
||
"openapi": "3.1",
|
||
"paths": {
|
||
"/an/operation": {
|
||
"get": {
|
||
"operationId": "thisIsAnOperationId"
|
||
}
|
||
}
|
||
}
|
||
}`
|
||
|
||
doc, err := NewDocumentWithConfiguration([]byte(d), datamodel.NewDocumentConfiguration())
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
_, _ = doc.BuildV3Model()
|
||
|
||
// render the document.
|
||
rend, _, _, _ := doc.RenderAndReload()
|
||
|
||
assert.Equal(t, d, strings.TrimSpace(string(rend)))
|
||
}
|
||
|
||
func TestDocument_RenderWithIndention(t *testing.T) {
|
||
spec := `openapi: "3.1.0"
|
||
info:
|
||
title: Test
|
||
version: 1.0.0
|
||
paths:
|
||
/test:
|
||
get:
|
||
operationId: 'test'`
|
||
|
||
config := datamodel.NewDocumentConfiguration()
|
||
|
||
doc, err := NewDocumentWithConfiguration([]byte(spec), config)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
_, _ = doc.BuildV3Model()
|
||
|
||
rend, _, _, _ := doc.RenderAndReload()
|
||
|
||
assert.Equal(t, spec, strings.TrimSpace(string(rend)))
|
||
}
|
||
|
||
func TestDocument_IgnorePolymorphicCircularReferences(t *testing.T) {
|
||
d := `openapi: 3.1.0
|
||
components:
|
||
schemas:
|
||
ProductCategory:
|
||
type: "object"
|
||
properties:
|
||
name:
|
||
type: "string"
|
||
children:
|
||
type: "object"
|
||
anyOf:
|
||
- $ref: "#/components/schemas/ProductCategory"
|
||
description: "Array of sub-categories in the same format."
|
||
required:
|
||
- "name"
|
||
- "children"`
|
||
|
||
config := datamodel.NewDocumentConfiguration()
|
||
config.IgnorePolymorphicCircularReferences = true
|
||
|
||
doc, err := NewDocumentWithConfiguration([]byte(d), config)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
m, errs := doc.BuildV3Model()
|
||
|
||
assert.Len(t, errs, 0)
|
||
assert.Len(t, m.Index.GetCircularReferences(), 0)
|
||
assert.Len(t, m.Index.GetResolver().GetIgnoredCircularPolyReferences(), 1)
|
||
}
|
||
|
||
func TestDocument_IgnoreArrayCircularReferences(t *testing.T) {
|
||
d := `openapi: 3.1.0
|
||
components:
|
||
schemas:
|
||
ProductCategory:
|
||
type: "object"
|
||
properties:
|
||
name:
|
||
type: "string"
|
||
children:
|
||
type: "array"
|
||
items:
|
||
$ref: "#/components/schemas/ProductCategory"
|
||
description: "Array of sub-categories in the same format."
|
||
required:
|
||
- "name"
|
||
- "children"`
|
||
|
||
config := datamodel.NewDocumentConfiguration()
|
||
config.IgnoreArrayCircularReferences = true
|
||
|
||
doc, err := NewDocumentWithConfiguration([]byte(d), config)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
|
||
m, errs := doc.BuildV3Model()
|
||
|
||
assert.Len(t, errs, 0)
|
||
assert.Len(t, m.Index.GetCircularReferences(), 0)
|
||
assert.Len(t, m.Index.GetResolver().GetIgnoredCircularArrayReferences(), 1)
|
||
}
|
||
|
||
func TestDocument_TestMixedReferenceOrigin(t *testing.T) {
|
||
bs, _ := os.ReadFile("test_specs/mixedref-burgershop.openapi.yaml")
|
||
|
||
config := datamodel.NewDocumentConfiguration()
|
||
config.AllowRemoteReferences = true
|
||
config.AllowFileReferences = true
|
||
config.SkipCircularReferenceCheck = true
|
||
config.BasePath = "test_specs"
|
||
|
||
config.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
|
||
Level: slog.LevelDebug,
|
||
}))
|
||
|
||
doc, _ := NewDocumentWithConfiguration(bs, config)
|
||
m, _ := doc.BuildV3Model()
|
||
|
||
// extract something that can only exist after being located by the rolodex.
|
||
mediaType := m.Model.Paths.PathItems.GetOrZero("/burgers/{burgerId}/dressings").
|
||
Get.Responses.Codes.GetOrZero("200").Content.GetOrZero("application/json").Schema.Schema().Items
|
||
|
||
items := mediaType.A.Schema()
|
||
|
||
origin := items.ParentProxy.GetReferenceOrigin()
|
||
assert.NotNil(t, origin)
|
||
assert.True(t, strings.HasSuffix(origin.AbsoluteLocation, "test_specs/burgershop.openapi.yaml"))
|
||
}
|
||
|
||
func BenchmarkReferenceOrigin(b *testing.B) {
|
||
b.ResetTimer()
|
||
for i := 0; i < b.N; i++ {
|
||
|
||
bs, _ := os.ReadFile("test_specs/mixedref-burgershop.openapi.yaml")
|
||
|
||
config := datamodel.NewDocumentConfiguration()
|
||
config.AllowRemoteReferences = true
|
||
config.AllowFileReferences = true
|
||
config.BasePath = "test_specs"
|
||
config.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug}))
|
||
|
||
doc, _ := NewDocumentWithConfiguration(bs, config)
|
||
m, _ := doc.BuildV3Model()
|
||
|
||
// extract something that can only exist after being located by the rolodex.
|
||
mediaType := m.Model.Paths.PathItems.GetOrZero("/burgers/{burgerId}/dressings").
|
||
Get.Responses.Codes.GetOrZero("200").Content.GetOrZero("application/json").Schema.Schema().Items
|
||
|
||
items := mediaType.A.Schema()
|
||
|
||
origin := items.ParentProxy.GetReferenceOrigin()
|
||
if origin == nil {
|
||
// fmt.Println("nil origin")
|
||
} else {
|
||
// fmt.Println(origin.AbsoluteLocation)
|
||
}
|
||
assert.NotNil(b, origin)
|
||
assert.True(b, strings.HasSuffix(origin.AbsoluteLocation, "test_specs/burgershop.openapi.yaml"))
|
||
}
|
||
}
|
||
|
||
// Ensure document ordering is preserved after building, rendering, and reloading.
|
||
func TestDocument_Render_PreserveOrder(t *testing.T) {
|
||
t.Run("Paths", func(t *testing.T) {
|
||
const itemCount = 100
|
||
doc, err := NewDocument([]byte(`openapi: 3.1.0`))
|
||
require.NoError(t, err)
|
||
model, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
pathItems := orderedmap.New[string, *v3high.PathItem]()
|
||
model.Model.Paths = &v3high.Paths{
|
||
PathItems: pathItems,
|
||
}
|
||
for i := 0; i < itemCount; i++ {
|
||
pathItem := &v3high.PathItem{
|
||
Get: &v3high.Operation{
|
||
Parameters: make([]*v3high.Parameter, 0),
|
||
},
|
||
}
|
||
pathName := fmt.Sprintf("/foobar/%d", i)
|
||
pathItems.Set(pathName, pathItem)
|
||
}
|
||
|
||
checkOrder := func(t *testing.T, doc Document) {
|
||
model, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
pathItems := model.Model.Paths.PathItems
|
||
require.Equal(t, itemCount, orderedmap.Len(pathItems))
|
||
|
||
var i int
|
||
for pair := orderedmap.First(model.Model.Paths.PathItems); pair != nil; pair = pair.Next() {
|
||
pathName := fmt.Sprintf("/foobar/%d", i)
|
||
assert.Equal(t, pathName, pair.Key())
|
||
i++
|
||
}
|
||
assert.Equal(t, itemCount, i)
|
||
}
|
||
|
||
t.Run("Check order before rendering", func(t *testing.T) {
|
||
checkOrder(t, doc)
|
||
})
|
||
|
||
yamlBytes, doc, _, errs := doc.RenderAndReload()
|
||
require.Empty(t, errs)
|
||
|
||
// Reload YAML into new Document, verify ordering.
|
||
t.Run("Unmarshalled YAML ordering", func(t *testing.T) {
|
||
doc2, err := NewDocument(yamlBytes)
|
||
require.NoError(t, err)
|
||
checkOrder(t, doc2)
|
||
})
|
||
|
||
// Verify ordering of reloaded document after call to RenderAndReload().
|
||
t.Run("Reloaded document ordering", func(t *testing.T) {
|
||
checkOrder(t, doc)
|
||
})
|
||
})
|
||
|
||
t.Run("Responses", func(t *testing.T) {
|
||
t.Run("Codes", func(t *testing.T) {
|
||
const itemCount = 100
|
||
doc, err := NewDocument([]byte(`openapi: 3.1.0`))
|
||
require.NoError(t, err)
|
||
model, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
pathItems := orderedmap.New[string, *v3high.PathItem]()
|
||
model.Model.Paths = &v3high.Paths{
|
||
PathItems: pathItems,
|
||
}
|
||
pathItem := &v3high.PathItem{
|
||
Get: &v3high.Operation{
|
||
Parameters: make([]*v3high.Parameter, 0),
|
||
},
|
||
}
|
||
pathName := "/foobar"
|
||
pathItems.Set(pathName, pathItem)
|
||
responses := &v3high.Responses{
|
||
Codes: orderedmap.New[string, *v3high.Response](),
|
||
}
|
||
pathItem.Get.Responses = responses
|
||
|
||
for i := 0; i < itemCount; i++ {
|
||
code := strconv.Itoa(200 + i)
|
||
resp := &v3high.Response{}
|
||
responses.Codes.Set(code, resp)
|
||
}
|
||
|
||
checkOrder := func(t *testing.T, doc Document) {
|
||
model, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
pathItem := model.Model.Paths.PathItems.GetOrZero(pathName)
|
||
responses := pathItem.Get.Responses
|
||
|
||
var i int
|
||
for pair := orderedmap.First(responses.Codes); pair != nil; pair = pair.Next() {
|
||
expectedCode := strconv.Itoa(200 + i)
|
||
assert.Equal(t, expectedCode, pair.Key())
|
||
i++
|
||
}
|
||
assert.Equal(t, itemCount, i)
|
||
}
|
||
|
||
t.Run("Check order before rendering", func(t *testing.T) {
|
||
checkOrder(t, doc)
|
||
})
|
||
|
||
yamlBytes, doc, _, errs := doc.RenderAndReload()
|
||
require.Empty(t, errs)
|
||
|
||
// Reload YAML into new Document, verify ordering.
|
||
t.Run("Unmarshalled YAML ordering", func(t *testing.T) {
|
||
doc2, err := NewDocument(yamlBytes)
|
||
require.NoError(t, err)
|
||
checkOrder(t, doc2)
|
||
})
|
||
|
||
// Verify ordering of reloaded document after call to RenderAndReload().
|
||
t.Run("Reloaded document ordering", func(t *testing.T) {
|
||
checkOrder(t, doc)
|
||
})
|
||
})
|
||
|
||
t.Run("Examples", func(t *testing.T) {
|
||
const itemCount = 3
|
||
doc, err := NewDocument([]byte(`openapi: 3.1.0`))
|
||
require.NoError(t, err)
|
||
model, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
pathItems := orderedmap.New[string, *v3high.PathItem]()
|
||
model.Model.Paths = &v3high.Paths{
|
||
PathItems: pathItems,
|
||
}
|
||
pathItem := &v3high.PathItem{
|
||
Get: &v3high.Operation{
|
||
Parameters: make([]*v3high.Parameter, 0),
|
||
},
|
||
}
|
||
const pathName = "/foobar"
|
||
pathItems.Set(pathName, pathItem)
|
||
responses := &v3high.Responses{
|
||
Codes: orderedmap.New[string, *v3high.Response](),
|
||
}
|
||
pathItem.Get.Responses = responses
|
||
response := &v3high.Response{
|
||
Content: orderedmap.New[string, *v3high.MediaType](),
|
||
}
|
||
const respCode = "200"
|
||
responses.Codes.Set(respCode, response)
|
||
const mediaType = "application/json"
|
||
mediaTypeResp := &v3high.MediaType{
|
||
Examples: orderedmap.New[string, *base.Example](),
|
||
}
|
||
response.Content.Set(mediaType, mediaTypeResp)
|
||
type testExampleDomain struct {
|
||
ID string `json:"id"`
|
||
Name string `json:"name"`
|
||
Type string `json:"type"`
|
||
}
|
||
type testExampleDetails struct {
|
||
Message string `json:"message"`
|
||
Domain testExampleDomain `json:"domain"`
|
||
}
|
||
|
||
for i := 0; i < itemCount; i++ {
|
||
example := &base.Example{
|
||
Summary: fmt.Sprintf("Summary example %d", i),
|
||
Description: "Description example",
|
||
Value: utils.CreateYamlNode(testExampleDetails{
|
||
Message: "Foobar message",
|
||
Domain: testExampleDomain{
|
||
ID: "12345",
|
||
Name: "example.com",
|
||
Type: "Foobar type",
|
||
},
|
||
}),
|
||
}
|
||
exampleName := fmt.Sprintf("FoobarExample%d", i)
|
||
mediaTypeResp.Examples.Set(exampleName, example)
|
||
}
|
||
|
||
checkOrder := func(t *testing.T, doc Document) {
|
||
model, errs := doc.BuildV3Model()
|
||
require.Empty(t, errs)
|
||
pathItem := model.Model.Paths.PathItems.GetOrZero(pathName)
|
||
responses := pathItem.Get.Responses
|
||
respCode := responses.Codes.GetOrZero(respCode)
|
||
mediaTypeResp := respCode.Content.GetOrZero(mediaType)
|
||
|
||
var i int
|
||
for pair := orderedmap.First(mediaTypeResp.Examples); pair != nil; pair = pair.Next() {
|
||
assert.Equal(t, fmt.Sprintf("FoobarExample%d", i), pair.Key())
|
||
example := pair.Value()
|
||
assert.Equal(t, fmt.Sprintf("Summary example %d", i), example.Summary)
|
||
i++
|
||
}
|
||
assert.Equal(t, itemCount, i)
|
||
}
|
||
|
||
t.Run("Check order before rendering", func(t *testing.T) {
|
||
checkOrder(t, doc)
|
||
})
|
||
|
||
_, _, _, errs = doc.RenderAndReload()
|
||
require.Empty(t, errs)
|
||
|
||
// Cannot test order of reloaded or unmarshalled examples.
|
||
// The data type of `Example.Value` is `any`, and `yaml` package
|
||
// will unmarshall associative array data to `map` objects, which
|
||
// will lose consistent order.
|
||
})
|
||
})
|
||
}
|