diff --git a/.github/dependabot.yaml b/.github/dependabot.yaml new file mode 100644 index 00000000..a0ba689b --- /dev/null +++ b/.github/dependabot.yaml @@ -0,0 +1,15 @@ +version: 2 +updates: +- package-ecosystem: gomod + directory: / + schedule: + interval: weekly + reviewers: + - "daveshanley" + +- package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + reviewers: + - "daveshanley" \ No newline at end of file diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d8b47544..a5e70c58 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,49 +1,84 @@ -name: Build +name: Build & Test on: push: - branches: - - main + branches: [ "main" ] + paths: + - '**.go' + - '**.yaml' + - '**.yml' + - '**.toml' + - '**.json' + - 'go.mod' + - 'go.sum' pull_request: - branches: - - main + # The branches below must be a subset of the branches above + branches: [ "main" ] + paths: + - '**.go' + - '**.yaml' + - '**.yml' + - '**.toml' + - '**.json' + - 'go.mod' + - 'go.sum' jobs: build: - name: Build - runs-on: ubuntu-latest + name: Build & Test + strategy: + matrix: + go-version: ['stable', 'oldstable'] + platform: ['ubuntu-latest'] + runs-on: ${{ matrix.platform }} + permissions: + # required for security related workflows + security-events: write + # only required for workflows in private repositories + actions: read + contents: read steps: - - name: Set up Go 1.x - uses: actions/setup-go@v3 - with: - go-version: 1.19 - id: go - - name: Checkout code uses: actions/checkout@v3 - - name: Get dependencies - run: | - go get -v -t -d ./... - if [ -f Gopkg.toml ]; then - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh - dep ensure - fi - - name: Test - run: go test ./... - - name: Coverage - run: | - go get github.com/axw/gocov/gocov - go get github.com/AlekSi/gocov-xml - go install github.com/axw/gocov/gocov - go install github.com/AlekSi/gocov-xml - - run: | - go test -v -coverprofile cover.out ./... - gocov convert cover.out | gocov-xml > coverage.xml - - uses: codecov/codecov-action@v3 + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version: ${{ matrix.go-version }} + + - name: Vet + run: go vet ./... + + - name: Install govulncheck + run: go install golang.org/x/vuln/cmd/govulncheck@latest + + - name: Run govulncheck + run: govulncheck ./... + + - name: Run Gosec Security Scanner + uses: securego/gosec@master + with: + # we let the report trigger content trigger a failure using the GitHub Security features. + args: '-no-fail -fmt sarif -out results.sarif ./...' + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v2 + with: + # Path to SARIF file relative to the root of the repository + sarif_file: results.sarif + + + - name: Test & Code Coverage + # https://go.dev/blog/cover + # -covermode=set (if a statement was run = default) + # -covermode=count (how many times a statement was run) + # -covermode=atomic (how many times a statement was run, even in parallel) + run: go test -v -coverprofile=coverage.out ./... + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml - flags: unittests + files: ./coverage.out fail_ci_if_error: false verbose: true + diff --git a/.gitignore b/.gitignore index 069611e7..63406912 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ test-operation.yaml .idea/ -*.iml \ No newline at end of file +*.iml +__debug_bin* +coverage.out diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..180cfdad --- /dev/null +++ b/Makefile @@ -0,0 +1,12 @@ + +build: + go build ./... + +test: + go test -v -timeout 30m -count=1 ./... + +race: + go test -v -timeout 30m -count=1 -race ./... + +coverage: + go test -v -timeout 30m -count=1 -covermode=atomic -coverprofile=coverage.out ./... \ No newline at end of file diff --git a/datamodel/document_config_test.go b/datamodel/document_config_test.go index 8d18f38e..3cafdab7 100644 --- a/datamodel/document_config_test.go +++ b/datamodel/document_config_test.go @@ -4,17 +4,20 @@ package datamodel import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestNewClosedDocumentConfiguration(t *testing.T) { + t.Parallel() cfg := NewClosedDocumentConfiguration() assert.False(t, cfg.AllowRemoteReferences) assert.False(t, cfg.AllowFileReferences) } func TestNewOpenDocumentConfiguration(t *testing.T) { + t.Parallel() cfg := NewOpenDocumentConfiguration() assert.True(t, cfg.AllowRemoteReferences) assert.True(t, cfg.AllowFileReferences) diff --git a/datamodel/high/base/contact_test.go b/datamodel/high/base/contact_test.go index b854eb5c..8aa1526f 100644 --- a/datamodel/high/base/contact_test.go +++ b/datamodel/high/base/contact_test.go @@ -5,15 +5,16 @@ package base import ( "fmt" + "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestNewContact(t *testing.T) { - + t.Parallel() var cNode yaml.Node yml := `name: pizza @@ -58,7 +59,7 @@ email: buckaroo@pb33f.io` } func TestContact_MarshalYAML(t *testing.T) { - + t.Parallel() yml := `name: Buckaroo url: https://pb33f.io email: buckaroo@pb33f.io diff --git a/datamodel/high/base/discriminator_test.go b/datamodel/high/base/discriminator_test.go index 85dde25b..e0187d3c 100644 --- a/datamodel/high/base/discriminator_test.go +++ b/datamodel/high/base/discriminator_test.go @@ -5,16 +5,17 @@ package base import ( "fmt" + "strings" + "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestNewDiscriminator(t *testing.T) { - + t.Parallel() var cNode yaml.Node yml := `propertyName: coffee diff --git a/datamodel/high/base/dynamic_value_test.go b/datamodel/high/base/dynamic_value_test.go index bbd05e3a..fccc682a 100644 --- a/datamodel/high/base/dynamic_value_test.go +++ b/datamodel/high/base/dynamic_value_test.go @@ -4,58 +4,67 @@ package base import ( + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestDynamicValue_Render_A(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, int]{N: 0, A: "hello"} dvb, _ := dv.Render() assert.Equal(t, "hello", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_B(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, int]{N: 1, B: 12345} dvb, _ := dv.Render() assert.Equal(t, "12345", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_Bool(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, bool]{N: 1, B: true} dvb, _ := dv.Render() assert.Equal(t, "true", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_Int64(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, int64]{N: 1, B: 12345567810} dvb, _ := dv.Render() assert.Equal(t, "12345567810", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_Int32(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, int32]{N: 1, B: 1234567891} dvb, _ := dv.Render() assert.Equal(t, "1234567891", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_Float32(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, float32]{N: 1, B: 23456.123} dvb, _ := dv.Render() assert.Equal(t, "23456.123", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_Float64(t *testing.T) { + t.Parallel() dv := &DynamicValue[string, float64]{N: 1, B: 23456.1233456778} dvb, _ := dv.Render() assert.Equal(t, "23456.1233456778", strings.TrimSpace(string(dvb))) } func TestDynamicValue_Render_Ptr(t *testing.T) { + t.Parallel() type cake struct { Cake string @@ -67,7 +76,7 @@ func TestDynamicValue_Render_Ptr(t *testing.T) { } func TestDynamicValue_Render_PtrRenderable(t *testing.T) { - + t.Parallel() tag := &Tag{ Name: "cake", } @@ -78,7 +87,7 @@ func TestDynamicValue_Render_PtrRenderable(t *testing.T) { } func TestDynamicValue_RenderInline(t *testing.T) { - + t.Parallel() tag := &Tag{ Name: "cake", } @@ -89,7 +98,7 @@ func TestDynamicValue_RenderInline(t *testing.T) { } func TestDynamicValue_MarshalYAMLInline(t *testing.T) { - + t.Parallel() const ymlComponents = `components: schemas: rice: @@ -133,7 +142,7 @@ func TestDynamicValue_MarshalYAMLInline(t *testing.T) { } func TestDynamicValue_MarshalYAMLInline_Error(t *testing.T) { - + t.Parallel() const ymlComponents = `components: schemas: rice: diff --git a/datamodel/high/base/example_test.go b/datamodel/high/base/example_test.go index a783c0a2..7149cb76 100644 --- a/datamodel/high/base/example_test.go +++ b/datamodel/high/base/example_test.go @@ -5,16 +5,17 @@ package base import ( "fmt" + "strings" + "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestNewExample(t *testing.T) { - + t.Parallel() var cNode yaml.Node yml := `summary: an example @@ -49,6 +50,7 @@ x-hack: code` } func TestExtractExamples(t *testing.T) { + t.Parallel() var cNode yaml.Node yml := `summary: herbs` diff --git a/datamodel/high/base/external_doc_test.go b/datamodel/high/base/external_doc_test.go index e4fd2048..b501120c 100644 --- a/datamodel/high/base/external_doc_test.go +++ b/datamodel/high/base/external_doc_test.go @@ -5,16 +5,17 @@ package base import ( "fmt" + "strings" + "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestNewExternalDoc(t *testing.T) { - + t.Parallel() var cNode yaml.Node yml := `description: hack code diff --git a/datamodel/high/base/info_test.go b/datamodel/high/base/info_test.go index 33e31b95..2a91882d 100644 --- a/datamodel/high/base/info_test.go +++ b/datamodel/high/base/info_test.go @@ -14,6 +14,7 @@ import ( ) func TestNewInfo(t *testing.T) { + t.Parallel() var cNode yaml.Node yml := `title: chicken @@ -108,6 +109,7 @@ url: https://opensource.org/licenses/MIT` } func TestInfo_Render(t *testing.T) { + t.Parallel() ext := make(map[string]any) ext["x-pizza"] = "pepperoni" diff --git a/datamodel/high/base/licence_test.go b/datamodel/high/base/licence_test.go index 16582988..7fc9ffec 100644 --- a/datamodel/high/base/licence_test.go +++ b/datamodel/high/base/licence_test.go @@ -4,14 +4,16 @@ package base import ( + "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestLicense_Render(t *testing.T) { + t.Parallel() highL := &License{Name: "MIT", URL: "https://pb33f.io"} dat, _ := highL.Render() @@ -33,6 +35,7 @@ func TestLicense_Render(t *testing.T) { } func TestLicense_RenderEqual(t *testing.T) { + t.Parallel() yml := `name: MIT url: https://pb33f.io/not-real @@ -59,6 +62,7 @@ url: https://pb33f.io/not-real } func TestLicense_Render_Identifier(t *testing.T) { + t.Parallel() highL := &License{Name: "MIT", Identifier: "MIT"} dat, _ := highL.Render() @@ -80,6 +84,7 @@ func TestLicense_Render_Identifier(t *testing.T) { } func TestLicense_Render_IdentifierAndURL_Error(t *testing.T) { + t.Parallel() // this should fail because you can't have both an identifier and a URL highL := &License{Name: "MIT", Identifier: "MIT", URL: "https://pb33f.io"} diff --git a/datamodel/high/base/schema.go b/datamodel/high/base/schema.go index 305327d4..8e32b9d0 100644 --- a/datamodel/high/base/schema.go +++ b/datamodel/high/base/schema.go @@ -435,7 +435,7 @@ func NewSchema(schema *base.Schema) *Schema { completeChildren := 0 if children > 0 { allDone: - for true { + for { select { case <-polyCompletedChan: completeChildren++ diff --git a/datamodel/high/base/schema_proxy_test.go b/datamodel/high/base/schema_proxy_test.go index 38e98fd7..7e6cdedb 100644 --- a/datamodel/high/base/schema_proxy_test.go +++ b/datamodel/high/base/schema_proxy_test.go @@ -4,16 +4,18 @@ package base import ( + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestSchemaProxy_MarshalYAML(t *testing.T) { + t.Parallel() const ymlComponents = `components: schemas: rice: @@ -55,12 +57,14 @@ func TestSchemaProxy_MarshalYAML(t *testing.T) { } func TestCreateSchemaProxy(t *testing.T) { + t.Parallel() sp := CreateSchemaProxy(&Schema{Description: "iAmASchema"}) assert.Equal(t, "iAmASchema", sp.rendered.Description) assert.False(t, sp.IsReference()) } func TestCreateSchemaProxyRef(t *testing.T) { + t.Parallel() sp := CreateSchemaProxyRef("#/components/schemas/MySchema") assert.Equal(t, "#/components/schemas/MySchema", sp.GetReference()) assert.True(t, sp.IsReference()) diff --git a/datamodel/high/base/schema_test.go b/datamodel/high/base/schema_test.go index c9609d42..cf2f3bac 100644 --- a/datamodel/high/base/schema_test.go +++ b/datamodel/high/base/schema_test.go @@ -17,12 +17,14 @@ import ( ) func TestDynamicValue_IsA(t *testing.T) { + t.Parallel() dv := &DynamicValue[int, bool]{N: 0, A: 23} assert.True(t, dv.IsA()) assert.False(t, dv.IsB()) } func TestNewSchemaProxy(t *testing.T) { + t.Parallel() // check proxy yml := `components: schemas: @@ -71,6 +73,7 @@ func TestNewSchemaProxy(t *testing.T) { } func TestNewSchemaProxyRender(t *testing.T) { + t.Parallel() // check proxy yml := `components: schemas: @@ -115,6 +118,7 @@ func TestNewSchemaProxyRender(t *testing.T) { } func TestNewSchemaProxy_WithObject(t *testing.T) { + t.Parallel() testSpec := `type: object description: something object if: @@ -324,6 +328,7 @@ $anchor: anchor` } func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) { + t.Parallel() testSpec := test_get_allOf_schema_blob() var compNode yaml.Node @@ -374,6 +379,7 @@ func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) { } func TestNewSchemaProxy_WithObject_FinishPoly(t *testing.T) { + t.Parallel() testSpec := `type: object description: something object discriminator: @@ -515,6 +521,7 @@ required: [cake, fish]` } func TestSchemaProxy_GoLow(t *testing.T) { + t.Parallel() const ymlComponents = `components: schemas: rice: @@ -574,6 +581,7 @@ func getHighSchema(t *testing.T, yml string) *Schema { } func TestSchemaNumberNoValidation(t *testing.T) { + t.Parallel() yml := ` type: number ` @@ -587,6 +595,7 @@ type: number } func TestSchemaNumberMultipleOfInt(t *testing.T) { + t.Parallel() yml := ` type: number multipleOf: 5 @@ -598,6 +607,7 @@ multipleOf: 5 } func TestSchemaNumberMultipleOfFloat(t *testing.T) { + t.Parallel() yml := ` type: number multipleOf: 0.5 @@ -609,6 +619,7 @@ multipleOf: 0.5 } func TestSchemaNumberMinimumInt(t *testing.T) { + t.Parallel() yml := ` type: number minimum: 5 @@ -620,6 +631,7 @@ minimum: 5 } func TestSchemaNumberMinimumFloat(t *testing.T) { + t.Parallel() yml := ` type: number minimum: 0.5 @@ -631,6 +643,7 @@ minimum: 0.5 } func TestSchemaNumberMinimumZero(t *testing.T) { + t.Parallel() yml := ` type: number minimum: 0 @@ -642,6 +655,7 @@ minimum: 0 } func TestSchemaNumberExclusiveMinimum(t *testing.T) { + t.Parallel() yml := ` type: number exclusiveMinimum: 5 @@ -654,6 +668,7 @@ exclusiveMinimum: 5 } func TestSchemaNumberMaximum(t *testing.T) { + t.Parallel() yml := ` type: number maximum: 5 @@ -665,6 +680,7 @@ maximum: 5 } func TestSchemaNumberMaximumZero(t *testing.T) { + t.Parallel() yml := ` type: number maximum: 0 @@ -676,6 +692,7 @@ maximum: 0 } func TestSchemaNumberExclusiveMaximum(t *testing.T) { + t.Parallel() yml := ` type: number exclusiveMaximum: 5 @@ -688,6 +705,7 @@ exclusiveMaximum: 5 } func TestSchema_Items_Boolean(t *testing.T) { + t.Parallel() yml := ` type: number items: true @@ -698,6 +716,7 @@ items: true } func TestSchemaExamples(t *testing.T) { + t.Parallel() yml := ` type: number examples: @@ -792,6 +811,7 @@ properties: } func TestNewSchemaProxy_RenderSchema(t *testing.T) { + t.Parallel() testSpec := `type: object description: something object discriminator: @@ -840,6 +860,7 @@ allOf: } func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) { + t.Parallel() testSpec := `type: object description: something object oneOf: @@ -903,6 +924,7 @@ items: } func TestNewSchemaProxy_RenderSchemaEnsurePropertyOrdering(t *testing.T) { + t.Parallel() testSpec := `properties: somethingBee: type: number @@ -961,6 +983,7 @@ xml: } func TestNewSchemaProxy_RenderSchemaCheckDiscriminatorMappingOrder(t *testing.T) { + t.Parallel() testSpec := `discriminator: mapping: log: cat @@ -990,6 +1013,7 @@ func TestNewSchemaProxy_RenderSchemaCheckDiscriminatorMappingOrder(t *testing.T) } func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSlice(t *testing.T) { + t.Parallel() testSpec := `additionalProperties: - one - two @@ -1018,6 +1042,7 @@ func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSlice(t *testing.T) } func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSliceMap(t *testing.T) { + t.Parallel() testSpec := `additionalProperties: - nice: cake - yummy: beer @@ -1044,6 +1069,7 @@ func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSliceMap(t *testing } func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) { + t.Parallel() testSpec := `default: false` var compNode yaml.Node @@ -1067,6 +1093,7 @@ func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) { } func TestNewSchemaProxy_RenderAdditionalPropertiesFalse(t *testing.T) { + t.Parallel() testSpec := `additionalProperties: false` var compNode yaml.Node @@ -1090,6 +1117,7 @@ func TestNewSchemaProxy_RenderAdditionalPropertiesFalse(t *testing.T) { } func TestNewSchemaProxy_RenderMultiplePoly(t *testing.T) { + t.Parallel() idxYaml := `openapi: 3.1.0 components: schemas: @@ -1134,6 +1162,7 @@ components: } func TestNewSchemaProxy_RenderInline(t *testing.T) { + t.Parallel() idxYaml := `openapi: 3.1.0 components: schemas: @@ -1186,6 +1215,7 @@ components: } func TestUnevaluatedPropertiesBoolean_True(t *testing.T) { + t.Parallel() yml := ` type: number unevaluatedProperties: true @@ -1197,6 +1227,7 @@ unevaluatedProperties: true } func TestUnevaluatedPropertiesBoolean_False(t *testing.T) { + t.Parallel() yml := ` type: number unevaluatedProperties: false diff --git a/datamodel/high/base/security_requirement_test.go b/datamodel/high/base/security_requirement_test.go index b2a4f4e5..2f98e2e7 100644 --- a/datamodel/high/base/security_requirement_test.go +++ b/datamodel/high/base/security_requirement_test.go @@ -4,16 +4,17 @@ package base import ( + "strings" + "testing" + lowmodel "github.com/pb33f/libopenapi/datamodel/low" lowbase "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestNewSecurityRequirement(t *testing.T) { - + t.Parallel() var cNode yaml.Node yml := `pizza: diff --git a/datamodel/high/base/tag_test.go b/datamodel/high/base/tag_test.go index bf3b1b33..e83e7053 100644 --- a/datamodel/high/base/tag_test.go +++ b/datamodel/high/base/tag_test.go @@ -15,7 +15,7 @@ import ( ) func TestNewTag(t *testing.T) { - + t.Parallel() var cNode yaml.Node yml := `name: chicken @@ -48,7 +48,7 @@ x-hack: code` } func TestTag_RenderInline(t *testing.T) { - + t.Parallel() tag := &Tag{ Name: "cake", } diff --git a/datamodel/high/node_builder.go b/datamodel/high/node_builder.go index 0a71b089..1737fb0d 100644 --- a/datamodel/high/node_builder.go +++ b/datamodel/high/node_builder.go @@ -5,14 +5,15 @@ package high import ( "fmt" - "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/utils" - "gopkg.in/yaml.v3" "reflect" "sort" "strconv" "strings" "unicode" + + "github.com/pb33f/libopenapi/datamodel/low" + "github.com/pb33f/libopenapi/utils" + "gopkg.in/yaml.v3" ) // NodeEntry represents a single node used by NodeBuilder. @@ -197,7 +198,6 @@ func (n *NodeBuilder) add(key string, i int) { }) nodeEntry.Line = lines[0].line // pick the lowest line number so this key is sorted in order. nodeEntry.Style = lines[0].style - break case reflect.Map: l := value.Len() line := make([]int, l) @@ -303,8 +303,6 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod valueNode = utils.CreateStringNode(val) valueNode.Line = line valueNode.Style = entry.Style - break - case reflect.Bool: val := value.(bool) if !val { @@ -313,26 +311,18 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod valueNode = utils.CreateBoolNode("true") } valueNode.Line = line - break - case reflect.Int: val := strconv.Itoa(value.(int)) valueNode = utils.CreateIntNode(val) valueNode.Line = line - break - case reflect.Int64: val := strconv.FormatInt(value.(int64), 10) valueNode = utils.CreateIntNode(val) valueNode.Line = line - break - case reflect.Float32: val := strconv.FormatFloat(float64(value.(float32)), 'f', 2, 64) valueNode = utils.CreateFloatNode(val) valueNode.Line = line - break - case reflect.Float64: precision := -1 if entry.StringValue != "" && strings.Contains(entry.StringValue, ".") { @@ -341,8 +331,6 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod val := strconv.FormatFloat(value.(float64), 'f', precision, 64) valueNode = utils.CreateFloatNode(val) valueNode.Line = line - break - case reflect.Map: // the keys will be rendered randomly, if we don't find out the original line @@ -373,7 +361,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod fg := reflect.ValueOf(pr.GetValueUntyped()) found := false found, orderedCollection = n.extractLowMapKeys(fg, x, found, orderedCollection, m, k) - if found != true { + if !found { // this is something new, add it. orderedCollection = append(orderedCollection, &NodeEntry{ Tag: x, @@ -456,13 +444,13 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, entry *NodeEntry) *yaml.Nod if er, ko := sqi.(Renderable); ko { var rend interface{} if !n.Resolve { - rend, _ = er.(Renderable).MarshalYAML() + rend, _ = er.MarshalYAML() } else { // try and render inline, if we can, otherwise treat as normal. if _, ko := er.(RenderableInline); ko { rend, _ = er.(RenderableInline).MarshalYAMLInline() } else { - rend, _ = er.(Renderable).MarshalYAML() + rend, _ = er.MarshalYAML() } } // check if this is a pointer or not. diff --git a/datamodel/high/node_builder_test.go b/datamodel/high/node_builder_test.go index 80589caa..83660871 100644 --- a/datamodel/high/node_builder_test.go +++ b/datamodel/high/node_builder_test.go @@ -4,13 +4,14 @@ package high import ( + "reflect" + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/utils" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "reflect" - "strings" - "testing" ) type key struct { @@ -238,7 +239,7 @@ x-pizza: time` } func TestNewNodeBuilder_Type(t *testing.T) { - + t.Parallel() t1 := test1{ Type: []string{"chicken", "soup"}, } @@ -256,7 +257,7 @@ func TestNewNodeBuilder_Type(t *testing.T) { } func TestNewNodeBuilder_IsReferenced(t *testing.T) { - + t.Parallel() t1 := key{ Name: "cotton", ref: true, @@ -275,7 +276,7 @@ func TestNewNodeBuilder_IsReferenced(t *testing.T) { } func TestNewNodeBuilder_Extensions(t *testing.T) { - + t.Parallel() t1 := test1{ Thing: "ding", Extensions: map[string]any{ @@ -293,7 +294,7 @@ func TestNewNodeBuilder_Extensions(t *testing.T) { } func TestNewNodeBuilder_LowValueNode(t *testing.T) { - + t.Parallel() t1 := test1{ Thing: "ding", Extensions: map[string]any{ @@ -312,7 +313,7 @@ func TestNewNodeBuilder_LowValueNode(t *testing.T) { } func TestNewNodeBuilder_NoValue(t *testing.T) { - + t.Parallel() t1 := test1{ Thing: "", } @@ -324,6 +325,7 @@ func TestNewNodeBuilder_NoValue(t *testing.T) { } func TestNewNodeBuilder_EmptyString(t *testing.T) { + t.Parallel() t1 := new(test1) nodeEnty := NodeEntry{} nb := NewNodeBuilder(t1, t1) @@ -332,6 +334,7 @@ func TestNewNodeBuilder_EmptyString(t *testing.T) { } func TestNewNodeBuilder_EmptyStringRenderZero(t *testing.T) { + t.Parallel() t1 := new(test1) nodeEnty := NodeEntry{RenderZero: true, Value: ""} nb := NewNodeBuilder(t1, t1) @@ -341,6 +344,7 @@ func TestNewNodeBuilder_EmptyStringRenderZero(t *testing.T) { } func TestNewNodeBuilder_Bool(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) nodeEnty := NodeEntry{} @@ -349,6 +353,7 @@ func TestNewNodeBuilder_Bool(t *testing.T) { } func TestNewNodeBuilder_BoolRenderZero(t *testing.T) { + t.Parallel() type yui struct { Thrit bool `yaml:"thrit,renderZero"` } @@ -360,6 +365,7 @@ func TestNewNodeBuilder_BoolRenderZero(t *testing.T) { } func TestNewNodeBuilder_Int(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) p := utils.CreateEmptyMapNode() @@ -371,6 +377,7 @@ func TestNewNodeBuilder_Int(t *testing.T) { } func TestNewNodeBuilder_Int64(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) p := utils.CreateEmptyMapNode() @@ -382,6 +389,7 @@ func TestNewNodeBuilder_Int64(t *testing.T) { } func TestNewNodeBuilder_Float32(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) p := utils.CreateEmptyMapNode() @@ -393,6 +401,7 @@ func TestNewNodeBuilder_Float32(t *testing.T) { } func TestNewNodeBuilder_Float64(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) p := utils.CreateEmptyMapNode() @@ -404,6 +413,7 @@ func TestNewNodeBuilder_Float64(t *testing.T) { } func TestNewNodeBuilder_EmptyNode(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) nb.Nodes = nil @@ -413,6 +423,7 @@ func TestNewNodeBuilder_EmptyNode(t *testing.T) { } func TestNewNodeBuilder_MapKeyHasValue(t *testing.T) { + t.Parallel() t1 := test1{ Thrug: map[string]string{ @@ -446,6 +457,7 @@ func TestNewNodeBuilder_MapKeyHasValue(t *testing.T) { } func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) { + t.Parallel() t1 := test1{ Thomp: map[key]string{ @@ -485,6 +497,7 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) { } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch(t *testing.T) { + t.Parallel() t1 := test1{ Thomp: map[key]string{ @@ -519,6 +532,7 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch(t *testing.T) { } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatchKeyNode(t *testing.T) { + t.Parallel() t1 := test1{ Thomp: map[key]string{ @@ -553,6 +567,7 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatchKeyNode(t *testing.T) { } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch_NoWrap(t *testing.T) { + t.Parallel() t1 := test1{ Thomp: map[key]string{ @@ -583,6 +598,7 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch_NoWrap(t *testing.T) { } func TestNewNodeBuilder_MissingLabel(t *testing.T) { + t.Parallel() t1 := new(test1) nb := NewNodeBuilder(t1, t1) @@ -594,6 +610,7 @@ func TestNewNodeBuilder_MissingLabel(t *testing.T) { } func TestNewNodeBuilder_ExtensionMap(t *testing.T) { + t.Parallel() t1 := test1{ Thing: "ding", @@ -615,6 +632,7 @@ func TestNewNodeBuilder_ExtensionMap(t *testing.T) { } func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) { + t.Parallel() t1 := test1{ Extensions: map[string]any{ @@ -640,6 +658,7 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) { } func TestNewNodeBuilder_SliceRef(t *testing.T) { + t.Parallel() c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"} ty := []*key{&c} @@ -659,6 +678,7 @@ func TestNewNodeBuilder_SliceRef(t *testing.T) { } func TestNewNodeBuilder_SliceRef_Inline(t *testing.T) { + t.Parallel() c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"} ty := []*key{&c} @@ -693,6 +713,7 @@ func (t testRenderRawNode) MarshalYAML() (interface{}, error) { } func TestNewNodeBuilder_SliceRef_Inline_NotCompatible(t *testing.T) { + t.Parallel() ty := []interface{}{testRender{}} t1 := test1{ @@ -712,6 +733,7 @@ func TestNewNodeBuilder_SliceRef_Inline_NotCompatible(t *testing.T) { } func TestNewNodeBuilder_SliceRef_Inline_NotCompatible_NotPointer(t *testing.T) { + t.Parallel() ty := []interface{}{testRenderRawNode{}} t1 := test1{ @@ -731,6 +753,7 @@ func TestNewNodeBuilder_SliceRef_Inline_NotCompatible_NotPointer(t *testing.T) { } func TestNewNodeBuilder_PointerRef_Inline_NotCompatible_RawNode(t *testing.T) { + t.Parallel() ty := testRenderRawNode{} t1 := test1{ @@ -749,6 +772,7 @@ func TestNewNodeBuilder_PointerRef_Inline_NotCompatible_RawNode(t *testing.T) { } func TestNewNodeBuilder_PointerRef_Inline_NotCompatible(t *testing.T) { + t.Parallel() ty := key{} t1 := test1{ @@ -767,6 +791,7 @@ func TestNewNodeBuilder_PointerRef_Inline_NotCompatible(t *testing.T) { } func TestNewNodeBuilder_SliceNoRef(t *testing.T) { + t.Parallel() c := key{ref: false, Name: "milky"} ty := []*key{&c} @@ -786,6 +811,7 @@ func TestNewNodeBuilder_SliceNoRef(t *testing.T) { } func TestNewNodeBuilder_TestStructAny(t *testing.T) { + t.Parallel() t1 := test1{ Thurm: low.ValueReference[any]{ @@ -803,6 +829,7 @@ func TestNewNodeBuilder_TestStructAny(t *testing.T) { assert.Equal(t, desired, strings.TrimSpace(string(data))) } func TestNewNodeBuilder_TestStructString(t *testing.T) { + t.Parallel() t1 := test1{ Thurm: low.ValueReference[string]{ @@ -821,6 +848,7 @@ func TestNewNodeBuilder_TestStructString(t *testing.T) { } func TestNewNodeBuilder_TestStructPointer(t *testing.T) { + t.Parallel() t1 := test1{ Thrim: &key{ @@ -842,6 +870,7 @@ func TestNewNodeBuilder_TestStructPointer(t *testing.T) { } func TestNewNodeBuilder_TestStructRef(t *testing.T) { + t.Parallel() fkn := utils.CreateStringNode("pizzaBurgers") fkn.Line = 22 @@ -866,6 +895,7 @@ func TestNewNodeBuilder_TestStructRef(t *testing.T) { } func TestNewNodeBuilder_TestStructDefaultEncode(t *testing.T) { + t.Parallel() f := 1 t1 := test1{ @@ -883,6 +913,7 @@ func TestNewNodeBuilder_TestStructDefaultEncode(t *testing.T) { } func TestNewNodeBuilder_TestSliceMapSliceStruct(t *testing.T) { + t.Parallel() a := []map[string][]string{ {"pizza": {"beer", "wine"}}, @@ -906,6 +937,7 @@ func TestNewNodeBuilder_TestSliceMapSliceStruct(t *testing.T) { } func TestNewNodeBuilder_TestRenderZero(t *testing.T) { + t.Parallel() f := false t1 := test1{ @@ -923,6 +955,7 @@ func TestNewNodeBuilder_TestRenderZero(t *testing.T) { } func TestNewNodeBuilder_TestRenderServerVariableSimulation(t *testing.T) { + t.Parallel() t1 := test1{ Thrig: map[string]*plug{ @@ -945,6 +978,7 @@ func TestNewNodeBuilder_TestRenderServerVariableSimulation(t *testing.T) { } func TestNewNodeBuilder_ShouldHaveNotDoneTestsLikeThisOhWell(t *testing.T) { + t.Parallel() m := make(map[low.KeyReference[string]]low.ValueReference[*key]) diff --git a/datamodel/high/shared_test.go b/datamodel/high/shared_test.go index cf03e548..188b6848 100644 --- a/datamodel/high/shared_test.go +++ b/datamodel/high/shared_test.go @@ -4,13 +4,15 @@ package high import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestExtractExtensions(t *testing.T) { + t.Parallel() n := make(map[low.KeyReference[string]]low.ValueReference[any]) n[low.KeyReference[string]{ Value: "pb33f", @@ -43,6 +45,7 @@ func (c *child) GetExtensions() map[low.KeyReference[string]]low.ValueReference[ } func TestUnpackExtensions(t *testing.T) { + t.Parallel() var resultA, resultB yaml.Node @@ -88,6 +91,7 @@ power: 2` } func TestUnpackExtensions_Fail(t *testing.T) { + t.Parallel() var resultA, resultB yaml.Node diff --git a/datamodel/high/v2/path_item_test.go b/datamodel/high/v2/path_item_test.go index 752d0b8c..1f373c2c 100644 --- a/datamodel/high/v2/path_item_test.go +++ b/datamodel/high/v2/path_item_test.go @@ -4,16 +4,17 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" v2 "github.com/pb33f/libopenapi/datamodel/low/v2" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestPathItem_GetOperations(t *testing.T) { - + t.Parallel() yml := `get: description: get put: diff --git a/datamodel/high/v2/swagger_test.go b/datamodel/high/v2/swagger_test.go index e21eed4e..654a10f5 100644 --- a/datamodel/high/v2/swagger_test.go +++ b/datamodel/high/v2/swagger_test.go @@ -4,20 +4,21 @@ package v2 import ( + "os" + "github.com/pb33f/libopenapi/datamodel" v2 "github.com/pb33f/libopenapi/datamodel/low/v2" "github.com/stretchr/testify/assert" - "io/ioutil" "testing" ) var doc *v2.Swagger func initTest() { - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + data, _ := os.ReadFile("../../../test_specs/petstorev2-complete.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error doc, err = v2.CreateDocument(info) if err != nil { panic("broken something") diff --git a/datamodel/high/v3/callback_test.go b/datamodel/high/v3/callback_test.go index b6a748b1..bbc13670 100644 --- a/datamodel/high/v3/callback_test.go +++ b/datamodel/high/v3/callback_test.go @@ -4,17 +4,18 @@ package v3 import ( + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestCallback_MarshalYAML(t *testing.T) { - + t.Parallel() cb := &Callback{ Expression: map[string]*PathItem{ "https://pb33f.io": { diff --git a/datamodel/high/v3/components.go b/datamodel/high/v3/components.go index afdc52af..5365d64c 100644 --- a/datamodel/high/v3/components.go +++ b/datamodel/high/v3/components.go @@ -165,8 +165,7 @@ func buildComponent[N any, O any](comp int, key string, orig O, c chan component // build out a schema func buildSchema(key lowmodel.KeyReference[string], orig lowmodel.ValueReference[*base.SchemaProxy], c chan componentResult[*highbase.SchemaProxy]) { - var sch *highbase.SchemaProxy - sch = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ + var sch *highbase.SchemaProxy = highbase.NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{ Value: orig.Value, ValueNode: orig.ValueNode, }) diff --git a/datamodel/high/v3/components_test.go b/datamodel/high/v3/components_test.go index 5be13d4b..50959185 100644 --- a/datamodel/high/v3/components_test.go +++ b/datamodel/high/v3/components_test.go @@ -4,17 +4,18 @@ package v3 import ( + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestComponents_MarshalYAML(t *testing.T) { - + t.Parallel() comp := &Components{ Responses: map[string]*Response{ "200": { diff --git a/datamodel/high/v3/document_test.go b/datamodel/high/v3/document_test.go index 5c67cd80..e1612806 100644 --- a/datamodel/high/v3/document_test.go +++ b/datamodel/high/v3/document_test.go @@ -4,7 +4,6 @@ package v3 import ( - "fmt" "net/url" "os" "strings" @@ -14,7 +13,9 @@ import ( v2 "github.com/pb33f/libopenapi/datamodel/high/v2" lowv2 "github.com/pb33f/libopenapi/datamodel/low/v2" lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var lowDoc *lowv3.Document @@ -22,7 +23,7 @@ var lowDoc *lowv3.Document func initTest() { data, _ := os.ReadFile("../../../test_specs/burgershop.openapi.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: true, AllowRemoteReferences: true, @@ -381,29 +382,32 @@ func testBurgerShop(t *testing.T, h *Document, checkLines bool) { } func TestStripeAsDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/stripe.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) - assert.Len(t, err, 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) d := NewDocument(lowDoc) assert.NotNil(t, d) } func TestK8sAsDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/k8s.json") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowSwag, err := lowv2.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) d := v2.NewSwaggerDocument(lowSwag) - assert.Len(t, err, 0) + assert.Len(t, errorutils.ShallowUnwrap(err), 0) assert.NotNil(t, d) } func TestAsanaAsDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/asana.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) if err != nil { panic("broken something") @@ -414,9 +418,10 @@ func TestAsanaAsDoc(t *testing.T) { } func TestDigitalOceanAsDocFromSHA(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/digitalocean.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error baseURL, _ := url.Parse("https://raw.githubusercontent.com/digitalocean/openapi/82e1d558e15a59edc1d47d2c5544e7138f5b3cbf/specification") config := datamodel.DocumentConfiguration{ @@ -426,12 +431,7 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) { } lowDoc, err = lowv3.CreateDocumentFromConfig(info, &config) - if err != nil { - for e := range err { - fmt.Println(err[e]) - } - panic("broken something") - } + require.NoError(t, err) d := NewDocument(lowDoc) assert.NotNil(t, d) assert.Equal(t, 183, len(d.Paths.PathItems)) @@ -439,9 +439,10 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) { } func TestPetstoreAsDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/petstorev3.json") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) if err != nil { panic("broken something") @@ -452,11 +453,12 @@ func TestPetstoreAsDoc(t *testing.T) { } func TestCircularReferencesDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/circular-tests.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) - assert.Len(t, err, 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) d := NewDocument(lowDoc) assert.Len(t, d.Components.Schemas, 9) assert.Len(t, d.Index.GetCircularReferences(), 3) @@ -481,6 +483,7 @@ func TestDocument_MarshalYAML(t *testing.T) { } func TestDocument_MarshalIndention(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/single-definition.yaml") info, _ := datamodel.ExtractSpecInfo(data) @@ -499,6 +502,7 @@ func TestDocument_MarshalIndention(t *testing.T) { } func TestDocument_MarshalIndention_Error(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/single-definition.yaml") info, _ := datamodel.ExtractSpecInfo(data) @@ -517,6 +521,7 @@ func TestDocument_MarshalIndention_Error(t *testing.T) { } func TestDocument_MarshalJSON(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/petstorev3.json") info, _ := datamodel.ExtractSpecInfo(data) @@ -555,6 +560,7 @@ func TestDocument_MarshalYAMLInline(t *testing.T) { } func TestDocument_MarshalYAML_TestRefs(t *testing.T) { + t.Parallel() // create a new document yml := `openapi: 3.1.0 @@ -616,7 +622,7 @@ components: numPatties: 1` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: true, AllowRemoteReferences: true, @@ -633,6 +639,7 @@ components: } func TestDocument_MarshalYAML_TestParamRefs(t *testing.T) { + t.Parallel() // create a new document yml := `openapi: 3.1.0 @@ -670,7 +677,7 @@ components: required: true` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: true, AllowRemoteReferences: true, @@ -686,6 +693,7 @@ components: } func TestDocument_MarshalYAML_TestModifySchemas(t *testing.T) { + t.Parallel() // create a new document yml := `openapi: 3.1.0 @@ -699,7 +707,7 @@ components: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: true, AllowRemoteReferences: true, diff --git a/datamodel/high/v3/encoding_test.go b/datamodel/high/v3/encoding_test.go index 56c26989..db82eb84 100644 --- a/datamodel/high/v3/encoding_test.go +++ b/datamodel/high/v3/encoding_test.go @@ -4,13 +4,14 @@ package v3 import ( - "github.com/stretchr/testify/assert" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestEncoding_MarshalYAML(t *testing.T) { - + t.Parallel() explode := true encoding := &Encoding{ ContentType: "application/json", diff --git a/datamodel/high/v3/header_test.go b/datamodel/high/v3/header_test.go index 5cb4c6a0..a98e3caf 100644 --- a/datamodel/high/v3/header_test.go +++ b/datamodel/high/v3/header_test.go @@ -4,14 +4,15 @@ package v3 import ( - "github.com/pb33f/libopenapi/datamodel/high/base" - "github.com/stretchr/testify/assert" "strings" "testing" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/stretchr/testify/assert" ) func TestHeader_MarshalYAML(t *testing.T) { - + t.Parallel() header := &Header{ Description: "A header", Required: true, diff --git a/datamodel/high/v3/link_test.go b/datamodel/high/v3/link_test.go index 111db3e6..9843c5cd 100644 --- a/datamodel/high/v3/link_test.go +++ b/datamodel/high/v3/link_test.go @@ -4,12 +4,14 @@ package v3 import ( - "github.com/stretchr/testify/assert" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestLink_MarshalYAML(t *testing.T) { + t.Parallel() link := Link{ OperationRef: "somewhere", OperationId: "somewhereOutThere", diff --git a/datamodel/high/v3/media_type_test.go b/datamodel/high/v3/media_type_test.go index 6574ef8d..bc7f1f8a 100644 --- a/datamodel/high/v3/media_type_test.go +++ b/datamodel/high/v3/media_type_test.go @@ -4,7 +4,7 @@ package v3 import ( - "io/ioutil" + "os" "strings" "testing" @@ -17,10 +17,11 @@ import ( ) func TestMediaType_MarshalYAMLInline(t *testing.T) { + t.Parallel() // load the petstore spec - data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + data, _ := os.ReadFile("../../../test_specs/petstorev3.json") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = v3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{}) if err != nil { panic("broken something") @@ -107,10 +108,11 @@ example: testing a nice mutation` } func TestMediaType_MarshalYAML(t *testing.T) { + t.Parallel() // load the petstore spec - data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + data, _ := os.ReadFile("../../../test_specs/petstorev3.json") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error lowDoc, err = v3.CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{}) if err != nil { panic("broken something") @@ -143,6 +145,7 @@ example: testing a nice mutation` } func TestMediaType_Examples(t *testing.T) { + t.Parallel() yml := `examples: pbjBurger: summary: A horrible, nutty, sticky mess. diff --git a/datamodel/high/v3/oauth_flow_test.go b/datamodel/high/v3/oauth_flow_test.go index f81a00f1..537ce070 100644 --- a/datamodel/high/v3/oauth_flow_test.go +++ b/datamodel/high/v3/oauth_flow_test.go @@ -4,13 +4,15 @@ package v3 import ( - "github.com/stretchr/testify/assert" "strings" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestOAuthFlow_MarshalYAML(t *testing.T) { - + t.Parallel() oflow := &OAuthFlow{ AuthorizationUrl: "https://pb33f.io", TokenUrl: "https://pb33f.io/token", @@ -18,7 +20,9 @@ func TestOAuthFlow_MarshalYAML(t *testing.T) { Scopes: map[string]string{"chicken": "nuggets", "beefy": "soup"}, } - rend, _ := oflow.Render() + rend, err := oflow.Render() + require.NoError(t, err) + assert.NotEmpty(t, rend) desired := `authorizationUrl: https://pb33f.io tokenUrl: https://pb33f.io/token @@ -39,7 +43,8 @@ tokenUrl: https://pb33f.io/token refreshUrl: https://pb33f.io/refresh x-burgers: why not?` - rend, _ = oflow.Render() + rend, err = oflow.Render() + require.NoError(t, err) assert.Equal(t, desired, strings.TrimSpace(string(rend))) } diff --git a/datamodel/high/v3/oauth_flows_test.go b/datamodel/high/v3/oauth_flows_test.go index 42f52292..5fdb90d5 100644 --- a/datamodel/high/v3/oauth_flows_test.go +++ b/datamodel/high/v3/oauth_flows_test.go @@ -4,17 +4,18 @@ package v3 import ( + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestNewOAuthFlows(t *testing.T) { - + t.Parallel() yml := `implicit: authorizationUrl: https://pb33f.io/oauth/implicit scopes: diff --git a/datamodel/high/v3/operation_test.go b/datamodel/high/v3/operation_test.go index 55a68d4c..87ff2769 100644 --- a/datamodel/high/v3/operation_test.go +++ b/datamodel/high/v3/operation_test.go @@ -22,6 +22,7 @@ import ( // create pointless test changes. So here is a standalone test. you know... for science. func TestOperation(t *testing.T) { + t.Parallel() yml := `externalDocs: url: https://pb33f.io callbacks: @@ -55,7 +56,7 @@ callbacks: } func TestOperation_MarshalYAML(t *testing.T) { - + t.Parallel() op := &Operation{ Tags: []string{"test"}, Summary: "nice", @@ -93,7 +94,7 @@ requestBody: } func TestOperation_MarshalYAMLInline(t *testing.T) { - + t.Parallel() op := &Operation{ Tags: []string{"test"}, Summary: "nice", @@ -131,6 +132,7 @@ requestBody: } func TestOperation_EmptySecurity(t *testing.T) { + t.Parallel() yml := ` security: []` @@ -150,6 +152,7 @@ security: []` } func TestOperation_NoSecurity(t *testing.T) { + t.Parallel() yml := `operationId: test` var idxNode yaml.Node diff --git a/datamodel/high/v3/package_test.go b/datamodel/high/v3/package_test.go index cc1e6e39..02de5e8f 100644 --- a/datamodel/high/v3/package_test.go +++ b/datamodel/high/v3/package_test.go @@ -5,29 +5,28 @@ package v3 import ( "fmt" + "os" + "github.com/pb33f/libopenapi/datamodel" lowv3 "github.com/pb33f/libopenapi/datamodel/low/v3" - "io/ioutil" ) // An example of how to create a new high-level OpenAPI 3+ document from an OpenAPI specification. func Example_createHighLevelOpenAPIDocument() { // Load in an OpenAPI 3+ specification as a byte slice. - data, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + data, _ := os.ReadFile("../../../test_specs/petstorev3.json") // Create a new *datamodel.SpecInfo from bytes. info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error // Create a new low-level Document, capture any errors thrown during creation. lowDoc, err = lowv3.CreateDocument(info) // Get upset if any errors were thrown. - if len(err) > 0 { - for i := range err { - fmt.Printf("error: %e", err[i]) - } + if err != nil { + fmt.Printf("error: %v\n", err) panic("something went wrong") } diff --git a/datamodel/high/v3/parameter_test.go b/datamodel/high/v3/parameter_test.go index a79f3e0d..aeb4fb1e 100644 --- a/datamodel/high/v3/parameter_test.go +++ b/datamodel/high/v3/parameter_test.go @@ -12,7 +12,7 @@ import ( ) func TestParameter_MarshalYAML(t *testing.T) { - + t.Parallel() explode := true param := Parameter{ Name: "chicken", @@ -46,7 +46,7 @@ x-burgers: why not?` } func TestParameter_MarshalYAMLInline(t *testing.T) { - + t.Parallel() explode := true param := Parameter{ Name: "chicken", @@ -80,7 +80,7 @@ x-burgers: why not?` } func TestParameter_IsExploded(t *testing.T) { - + t.Parallel() explode := true param := Parameter{ Explode: &explode, @@ -101,7 +101,7 @@ func TestParameter_IsExploded(t *testing.T) { } func TestParameter_IsDefaultFormEncoding(t *testing.T) { - + t.Parallel() param := Parameter{} assert.True(t, param.IsDefaultFormEncoding()) @@ -128,7 +128,7 @@ func TestParameter_IsDefaultFormEncoding(t *testing.T) { } func TestParameter_IsDefaultHeaderEncoding(t *testing.T) { - + t.Parallel() param := Parameter{} assert.True(t, param.IsDefaultHeaderEncoding()) @@ -158,7 +158,7 @@ func TestParameter_IsDefaultHeaderEncoding(t *testing.T) { } func TestParameter_IsDefaultPathEncoding(t *testing.T) { - + t.Parallel() param := Parameter{} assert.True(t, param.IsDefaultPathEncoding()) diff --git a/datamodel/high/v3/path_item_test.go b/datamodel/high/v3/path_item_test.go index db032540..15259132 100644 --- a/datamodel/high/v3/path_item_test.go +++ b/datamodel/high/v3/path_item_test.go @@ -19,6 +19,7 @@ import ( // with hard coded line and column numbers in them, changing the spec above the bottom will // create pointless test changes. So here is a standalone test. you know... for science. func TestPathItem(t *testing.T) { + t.Parallel() yml := `servers: - description: so many options for things in places.` @@ -38,6 +39,7 @@ func TestPathItem(t *testing.T) { } func TestPathItem_GetOperations(t *testing.T) { + t.Parallel() yml := `get: description: get put: @@ -70,6 +72,7 @@ trace: } func TestPathItem_MarshalYAML(t *testing.T) { + t.Parallel() pi := &PathItem{ Description: "a path item", @@ -111,6 +114,7 @@ parameters: } func TestPathItem_MarshalYAMLInline(t *testing.T) { + t.Parallel() pi := &PathItem{ Description: "a path item", diff --git a/datamodel/high/v3/paths_test.go b/datamodel/high/v3/paths_test.go index a2a847fc..d48d93f8 100644 --- a/datamodel/high/v3/paths_test.go +++ b/datamodel/high/v3/paths_test.go @@ -15,6 +15,7 @@ import ( ) func TestPaths_MarshalYAML(t *testing.T) { + t.Parallel() yml := `/foo/bar/bizzle: get: @@ -67,6 +68,7 @@ func TestPaths_MarshalYAML(t *testing.T) { } func TestPaths_MarshalYAMLInline(t *testing.T) { + t.Parallel() yml := `/foo/bar/bizzle: get: diff --git a/datamodel/high/v3/request_body_test.go b/datamodel/high/v3/request_body_test.go index d9fb8f48..fa1dbc59 100644 --- a/datamodel/high/v3/request_body_test.go +++ b/datamodel/high/v3/request_body_test.go @@ -11,7 +11,7 @@ import ( ) func TestRequestBody_MarshalYAML(t *testing.T) { - + t.Parallel() rb := true req := &RequestBody{ Description: "beer", @@ -30,7 +30,7 @@ x-high-gravity: why not?` } func TestRequestBody_MarshalYAMLInline(t *testing.T) { - + t.Parallel() rb := true req := &RequestBody{ Description: "beer", @@ -49,6 +49,7 @@ x-high-gravity: why not?` } func TestRequestBody_MarshalNoRequired(t *testing.T) { + t.Parallel() rb := false req := &RequestBody{ Description: "beer", @@ -67,6 +68,7 @@ x-high-gravity: why not?` } func TestRequestBody_MarshalRequiredNil(t *testing.T) { + t.Parallel() req := &RequestBody{ Description: "beer", diff --git a/datamodel/high/v3/response_test.go b/datamodel/high/v3/response_test.go index 749c6aaf..d973ea31 100644 --- a/datamodel/high/v3/response_test.go +++ b/datamodel/high/v3/response_test.go @@ -19,7 +19,7 @@ import ( // with hard coded line and column numbers in them, changing the spec above the bottom will // create pointless test changes. So here is a standalone test. you know... for science. func TestNewResponse(t *testing.T) { - + t.Parallel() yml := `description: this is a response headers: someHeader: @@ -51,6 +51,7 @@ links: } func TestResponse_MarshalYAML(t *testing.T) { + t.Parallel() yml := `description: this is a response headers: @@ -79,6 +80,7 @@ links: } func TestResponse_MarshalYAMLInline(t *testing.T) { + t.Parallel() yml := `description: this is a response headers: diff --git a/datamodel/high/v3/responses_test.go b/datamodel/high/v3/responses_test.go index 8bee83c2..ba9e1733 100644 --- a/datamodel/high/v3/responses_test.go +++ b/datamodel/high/v3/responses_test.go @@ -20,6 +20,7 @@ import ( // create pointless test changes. So here is a standalone test. you know... for science. func TestNewResponses(t *testing.T) { + t.Parallel() yml := `default: description: default response` @@ -40,6 +41,7 @@ func TestNewResponses(t *testing.T) { } func TestResponses_MarshalYAML(t *testing.T) { + t.Parallel() yml := `"201": description: this is a response @@ -70,6 +72,7 @@ func TestResponses_MarshalYAML(t *testing.T) { } func TestResponses_MarshalYAMLInline(t *testing.T) { + t.Parallel() yml := `"201": description: this is a response diff --git a/datamodel/high/v3/security_scheme_test.go b/datamodel/high/v3/security_scheme_test.go index 18b35d4f..66b353cd 100644 --- a/datamodel/high/v3/security_scheme_test.go +++ b/datamodel/high/v3/security_scheme_test.go @@ -4,16 +4,18 @@ package v3 import ( + "strings" + "testing" + "github.com/pb33f/libopenapi/datamodel/low" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "strings" - "testing" ) func TestSecurityScheme_MarshalYAML(t *testing.T) { + t.Parallel() ss := &SecurityScheme{ Type: "apiKey", diff --git a/datamodel/high/v3/server_test.go b/datamodel/high/v3/server_test.go index bda480c8..99b01e4b 100644 --- a/datamodel/high/v3/server_test.go +++ b/datamodel/high/v3/server_test.go @@ -4,12 +4,14 @@ package v3 import ( - "github.com/stretchr/testify/assert" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestServer_MarshalYAML(t *testing.T) { + t.Parallel() server := &Server{ URL: "https://pb33f.io", diff --git a/datamodel/high/v3/server_variable_test.go b/datamodel/high/v3/server_variable_test.go index 54919b05..1d9df6f4 100644 --- a/datamodel/high/v3/server_variable_test.go +++ b/datamodel/high/v3/server_variable_test.go @@ -4,12 +4,14 @@ package v3 import ( - "github.com/stretchr/testify/assert" "strings" "testing" + + "github.com/stretchr/testify/assert" ) func TestServerVariable_MarshalYAML(t *testing.T) { + t.Parallel() svar := &ServerVariable{ Enum: []string{"one", "two", "three"}, diff --git a/datamodel/low/base/contact_test.go b/datamodel/low/base/contact_test.go index 2236398e..e8acb87b 100644 --- a/datamodel/low/base/contact_test.go +++ b/datamodel/low/base/contact_test.go @@ -4,14 +4,15 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestContact_Hash(t *testing.T) { - + t.Parallel() left := `url: https://pb33f.io description: the ranch email: buckaroo@pb33f.io` diff --git a/datamodel/low/base/discriminator_test.go b/datamodel/low/base/discriminator_test.go index f6c145c0..496565cf 100644 --- a/datamodel/low/base/discriminator_test.go +++ b/datamodel/low/base/discriminator_test.go @@ -4,13 +4,15 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestDiscriminator_FindMappingValue(t *testing.T) { + t.Parallel() yml := `propertyName: freshCakes mapping: something: nothing` @@ -28,7 +30,7 @@ mapping: } func TestDiscriminator_Hash(t *testing.T) { - + t.Parallel() left := `propertyName: freshCakes mapping: something: nothing` diff --git a/datamodel/low/base/example_test.go b/datamodel/low/base/example_test.go index d19bee14..01f9d54b 100644 --- a/datamodel/low/base/example_test.go +++ b/datamodel/low/base/example_test.go @@ -4,15 +4,16 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestExample_Build_Success_NoValue(t *testing.T) { - + t.Parallel() yml := `summary: hot description: cakes x-cake: hot` @@ -37,7 +38,7 @@ x-cake: hot` } func TestExample_Build_Success_Simple(t *testing.T) { - + t.Parallel() yml := `summary: hot description: cakes value: a string example @@ -63,7 +64,7 @@ x-cake: hot` } func TestExample_Build_Success_Object(t *testing.T) { - + t.Parallel() yml := `summary: hot description: cakes value: @@ -94,7 +95,7 @@ value: } func TestExample_Build_Success_Array(t *testing.T) { - + t.Parallel() yml := `summary: hot description: cakes value: @@ -124,7 +125,7 @@ value: } func TestExample_Build_Success_MergeNode(t *testing.T) { - + t.Parallel() yml := `x-things: &things summary: hot description: cakes @@ -157,7 +158,7 @@ func TestExample_Build_Success_MergeNode(t *testing.T) { } func TestExample_ExtractExampleValue_Map(t *testing.T) { - + t.Parallel() yml := `hot: summer: nights pizza: oven` @@ -179,7 +180,7 @@ func TestExample_ExtractExampleValue_Map(t *testing.T) { } func TestExample_ExtractExampleValue_Slice(t *testing.T) { - + t.Parallel() yml := `- hot: summer: nights - hotter: @@ -211,7 +212,7 @@ func TestExample_ExtractExampleValue_Slice(t *testing.T) { } func TestExample_Hash(t *testing.T) { - + t.Parallel() left := `summary: hot description: cakes x-burger: nice @@ -245,9 +246,9 @@ x-burger: nice` } func TestExtractExampleValue(t *testing.T) { + t.Parallel() assert.True(t, ExtractExampleValue(&yaml.Node{Tag: "!!bool", Value: "true"}).(bool)) assert.Equal(t, int64(10), ExtractExampleValue(&yaml.Node{Tag: "!!int", Value: "10"}).(int64)) assert.Equal(t, 33.2, ExtractExampleValue(&yaml.Node{Tag: "!!float", Value: "33.2"}).(float64)) assert.Equal(t, "WHAT A NICE COW", ExtractExampleValue(&yaml.Node{Tag: "!!str", Value: "WHAT A NICE COW"})) - } diff --git a/datamodel/low/base/external_doc_test.go b/datamodel/low/base/external_doc_test.go index fadd21ac..051c2d0b 100644 --- a/datamodel/low/base/external_doc_test.go +++ b/datamodel/low/base/external_doc_test.go @@ -4,15 +4,16 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestExternalDoc_FindExtension(t *testing.T) { - + t.Parallel() yml := `x-fish: cake` var idxNode yaml.Node @@ -30,7 +31,7 @@ func TestExternalDoc_FindExtension(t *testing.T) { } func TestExternalDoc_Build(t *testing.T) { - + t.Parallel() yml := `url: https://pb33f.io description: the ranch x-b33f: princess` @@ -55,7 +56,7 @@ x-b33f: princess` } func TestExternalDoc_Hash(t *testing.T) { - + t.Parallel() left := `url: https://pb33f.io description: the ranch x-b33f: princess` diff --git a/datamodel/low/base/info_test.go b/datamodel/low/base/info_test.go index 286a4dc9..adc7378f 100644 --- a/datamodel/low/base/info_test.go +++ b/datamodel/low/base/info_test.go @@ -34,7 +34,7 @@ x-cli-name: pizza cli` err := low.BuildModel(idxNode.Content[0], &n) assert.NoError(t, err) - err = n.Build(nil,idxNode.Content[0], idx) + err = n.Build(nil, idxNode.Content[0], idx) assert.NoError(t, err) assert.Equal(t, "pizza", n.Title.Value) diff --git a/datamodel/low/base/license_test.go b/datamodel/low/base/license_test.go index 9d293266..8a208440 100644 --- a/datamodel/low/base/license_test.go +++ b/datamodel/low/base/license_test.go @@ -4,14 +4,15 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestLicense_Hash(t *testing.T) { - + t.Parallel() left := `url: https://pb33f.io description: the ranch` @@ -33,7 +34,7 @@ description: the ranch` } func TestLicense_WithIdentifier_Hash(t *testing.T) { - + t.Parallel() left := `identifier: MIT description: the ranch` @@ -58,7 +59,7 @@ description: the ranch` } func TestLicense_WithIdentifierAndURL_Error(t *testing.T) { - + t.Parallel() left := `identifier: MIT url: https://pb33f.io description: the ranch` @@ -69,6 +70,7 @@ description: the ranch` // create low level objects var lDoc License err := low.BuildModel(lNode.Content[0], &lDoc) + assert.NoError(t, err) err = lDoc.Build(nil, lNode.Content[0], nil) diff --git a/datamodel/low/base/schema_proxy_test.go b/datamodel/low/base/schema_proxy_test.go index 1f460463..342b2526 100644 --- a/datamodel/low/base/schema_proxy_test.go +++ b/datamodel/low/base/schema_proxy_test.go @@ -4,14 +4,15 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestSchemaProxy_Build(t *testing.T) { - + t.Parallel() yml := `x-windows: washed description: something` @@ -44,7 +45,7 @@ description: something` } func TestSchemaProxy_Build_CheckRef(t *testing.T) { - + t.Parallel() yml := `$ref: wat` var sch SchemaProxy @@ -60,7 +61,7 @@ func TestSchemaProxy_Build_CheckRef(t *testing.T) { } func TestSchemaProxy_Build_HashInline(t *testing.T) { - + t.Parallel() yml := `type: int` var sch SchemaProxy @@ -76,7 +77,7 @@ func TestSchemaProxy_Build_HashInline(t *testing.T) { } func TestSchemaProxy_Build_UsingMergeNodes(t *testing.T) { - + t.Parallel() yml := ` x-common-definitions: life_cycle_types: &life_cycle_types_def diff --git a/datamodel/low/base/schema_test.go b/datamodel/low/base/schema_test.go index ab1382dc..244b752d 100644 --- a/datamodel/low/base/schema_test.go +++ b/datamodel/low/base/schema_test.go @@ -156,6 +156,7 @@ $anchor: anchor` } func Test_Schema(t *testing.T) { + t.Parallel() testSpec := test_get_schema_blob() var rootNode yaml.Node @@ -314,6 +315,7 @@ func Test_Schema(t *testing.T) { } func TestSchemaAllOfSequenceOrder(t *testing.T) { + t.Parallel() testSpec := test_get_allOf_schema_blob() var rootNode yaml.Node @@ -356,6 +358,7 @@ func TestSchemaAllOfSequenceOrder(t *testing.T) { } func TestSchema_Hash(t *testing.T) { + t.Parallel() // create two versions testSpec := test_get_schema_blob() var sc1n yaml.Node @@ -394,6 +397,7 @@ func BenchmarkSchema_Hash(b *testing.B) { } func Test_Schema_31(t *testing.T) { + t.Parallel() testSpec := `$schema: https://something type: - object @@ -415,26 +419,27 @@ examples: mbErr := low.BuildModel(rootNode.Content[0], &sch) assert.NoError(t, mbErr) - schErr := sch.Build(rootNode.Content[0], nil) - assert.NoError(t, schErr) - assert.Equal(t, "something object", sch.Description.Value) - assert.Len(t, sch.Type.Value.B, 2) - assert.True(t, sch.Type.Value.IsB()) - assert.Equal(t, "object", sch.Type.Value.B[0].Value) - assert.True(t, sch.ExclusiveMinimum.Value.IsB()) - assert.False(t, sch.ExclusiveMinimum.Value.IsA()) - assert.True(t, sch.ExclusiveMaximum.Value.IsB()) - assert.Equal(t, float64(12), sch.ExclusiveMinimum.Value.B) - assert.Equal(t, float64(13), sch.ExclusiveMaximum.Value.B) - assert.Len(t, sch.Examples.Value, 1) - assert.Equal(t, "testing", sch.Examples.Value[0].Value) - assert.Equal(t, "fish64", sch.ContentEncoding.Value) - assert.Equal(t, "fish/paste", sch.ContentMediaType.Value) - assert.True(t, sch.Items.Value.IsB()) - assert.True(t, sch.Items.Value.B) + schErr := sch.Build(rootNode.Content[0], nil) + assert.NoError(t, schErr) + assert.Equal(t, "something object", sch.Description.Value) + assert.Len(t, sch.Type.Value.B, 2) + assert.True(t, sch.Type.Value.IsB()) + assert.Equal(t, "object", sch.Type.Value.B[0].Value) + assert.True(t, sch.ExclusiveMinimum.Value.IsB()) + assert.False(t, sch.ExclusiveMinimum.Value.IsA()) + assert.True(t, sch.ExclusiveMaximum.Value.IsB()) + assert.Equal(t, float64(12), sch.ExclusiveMinimum.Value.B) + assert.Equal(t, float64(13), sch.ExclusiveMaximum.Value.B) + assert.Len(t, sch.Examples.Value, 1) + assert.Equal(t, "testing", sch.Examples.Value[0].Value) + assert.Equal(t, "fish64", sch.ContentEncoding.Value) + assert.Equal(t, "fish/paste", sch.ContentMediaType.Value) + assert.True(t, sch.Items.Value.IsB()) + assert.True(t, sch.Items.Value.B) } func TestSchema_Build_PropsLookup(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -461,6 +466,7 @@ properties: } func TestSchema_Build_PropsLookup_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -486,6 +492,7 @@ properties: } func TestSchema_Build_DependentSchemas_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -511,6 +518,7 @@ dependentSchemas: } func TestSchema_Build_PatternProperties_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -536,6 +544,7 @@ patternProperties: } func Test_Schema_Polymorphism_Array_Ref(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -583,6 +592,7 @@ items: } func Test_Schema_Polymorphism_Array_Ref_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -623,6 +633,7 @@ items: } func Test_Schema_Polymorphism_Map_Ref(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -670,6 +681,7 @@ items: } func Test_Schema_Polymorphism_Map_Ref_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -710,6 +722,7 @@ items: } func Test_Schema_Polymorphism_BorkParent(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -736,6 +749,7 @@ allOf: } func Test_Schema_Polymorphism_BorkChild(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -762,6 +776,7 @@ allOf: } func Test_Schema_Polymorphism_BorkChild_Array(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -792,6 +807,7 @@ allOf: } func Test_Schema_Polymorphism_RefMadness(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -823,6 +839,7 @@ allOf: } func Test_Schema_Polymorphism_RefMadnessBork(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -851,8 +868,8 @@ allOf: } func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) { + t.Parallel() // this does not work, but it won't error out. - yml := `components: schemas: Something: @@ -879,8 +896,8 @@ func Test_Schema_Polymorphism_RefMadnessIllegal(t *testing.T) { } func Test_Schema_RefMadnessIllegal_Circular(t *testing.T) { + t.Parallel() // this does not work, but it won't error out. - yml := `components: schemas: Something: @@ -911,8 +928,8 @@ func Test_Schema_RefMadnessIllegal_Circular(t *testing.T) { } func Test_Schema_RefMadnessIllegal_Nonexist(t *testing.T) { + t.Parallel() // this does not work, but it won't error out. - yml := `components: schemas: Something: @@ -943,6 +960,7 @@ func Test_Schema_RefMadnessIllegal_Nonexist(t *testing.T) { } func TestExtractSchema(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -971,6 +989,7 @@ func TestExtractSchema(t *testing.T) { } func TestExtractSchema_DefaultPrimitive(t *testing.T) { + t.Parallel() yml := ` schema: type: object @@ -987,6 +1006,7 @@ schema: } func TestExtractSchema_Ref(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1011,6 +1031,7 @@ func TestExtractSchema_Ref(t *testing.T) { } func TestExtractSchema_Ref_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1033,6 +1054,7 @@ func TestExtractSchema_Ref_Fail(t *testing.T) { } func TestExtractSchema_CheckChildPropCircular(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1072,6 +1094,7 @@ func TestExtractSchema_CheckChildPropCircular(t *testing.T) { } func TestExtractSchema_RefRoot(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1095,6 +1118,7 @@ func TestExtractSchema_RefRoot(t *testing.T) { } func TestExtractSchema_RefRoot_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1116,6 +1140,7 @@ func TestExtractSchema_RefRoot_Fail(t *testing.T) { } func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1136,6 +1161,7 @@ func TestExtractSchema_RefRoot_Child_Fail(t *testing.T) { } func TestExtractSchema_AdditionalPropertiesAsSchema(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1160,6 +1186,7 @@ func TestExtractSchema_AdditionalPropertiesAsSchema(t *testing.T) { } func TestExtractSchema_AdditionalPropertiesAsSchemaSlice(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1184,6 +1211,7 @@ func TestExtractSchema_AdditionalPropertiesAsSchemaSlice(t *testing.T) { } func TestExtractSchema_DoNothing(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1205,6 +1233,7 @@ func TestExtractSchema_DoNothing(t *testing.T) { } func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) { + t.Parallel() yml := `components: schemas: Nothing: @@ -1233,6 +1262,7 @@ func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) { } func TestExtractSchema_OneOfRef(t *testing.T) { + t.Parallel() yml := `components: schemas: Error: @@ -1348,6 +1378,7 @@ func TestExtractSchema_OneOfRef(t *testing.T) { } func TestSchema_Hash_Equal(t *testing.T) { + t.Parallel() left := `schema: $schema: https://athing.com multipleOf: 1 @@ -1447,6 +1478,7 @@ func TestSchema_Hash_Equal(t *testing.T) { } func TestSchema_Hash_AdditionalPropsSlice(t *testing.T) { + t.Parallel() left := `schema: additionalProperties: - type: string` @@ -1472,6 +1504,7 @@ func TestSchema_Hash_AdditionalPropsSlice(t *testing.T) { } func TestSchema_Hash_AdditionalPropsSliceNoMap(t *testing.T) { + t.Parallel() left := `schema: additionalProperties: - hello` @@ -1497,6 +1530,7 @@ func TestSchema_Hash_AdditionalPropsSliceNoMap(t *testing.T) { } func TestSchema_Hash_NotEqual(t *testing.T) { + t.Parallel() left := `schema: title: an OK message - but different items: true @@ -1528,6 +1562,7 @@ func TestSchema_Hash_NotEqual(t *testing.T) { } func TestSchema_Hash_EqualJumbled(t *testing.T) { + t.Parallel() left := `schema: title: an OK message description: a nice thing. @@ -1577,6 +1612,7 @@ allOf: } func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsTrue(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1603,6 +1639,7 @@ func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsTrue(t *testing.T) { } func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsFalse(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -1626,6 +1663,7 @@ func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsFalse(t *testing.T) { } func TestSchema_UnevaluatedPropertiesAsBool_Undefined(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: diff --git a/datamodel/low/base/security_requirement_test.go b/datamodel/low/base/security_requirement_test.go index 05bee6e3..b1e86422 100644 --- a/datamodel/low/base/security_requirement_test.go +++ b/datamodel/low/base/security_requirement_test.go @@ -4,13 +4,14 @@ package base import ( + "testing" + "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestSecurityRequirement_Build(t *testing.T) { - + t.Parallel() yml := `one: - two - three diff --git a/datamodel/low/base/tag_test.go b/datamodel/low/base/tag_test.go index f765f00f..351473b3 100644 --- a/datamodel/low/base/tag_test.go +++ b/datamodel/low/base/tag_test.go @@ -4,15 +4,16 @@ package base import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestTag_Build(t *testing.T) { - + t.Parallel() yml := `name: a tag description: a description externalDocs: @@ -38,7 +39,7 @@ x-coffee: tasty` } func TestTag_Build_Error(t *testing.T) { - + t.Parallel() yml := `name: a tag description: a description externalDocs: @@ -57,7 +58,7 @@ externalDocs: } func TestTag_Hash(t *testing.T) { - + t.Parallel() left := `name: melody description: my princess externalDocs: diff --git a/datamodel/low/extraction_functions_test.go b/datamodel/low/extraction_functions_test.go index 926773d1..339fa50a 100644 --- a/datamodel/low/extraction_functions_test.go +++ b/datamodel/low/extraction_functions_test.go @@ -17,6 +17,7 @@ import ( ) func TestFindItemInMap(t *testing.T) { + t.Parallel() v := make(map[KeyReference[string]]ValueReference[string]) v[KeyReference[string]{ Value: "pizza", @@ -27,6 +28,7 @@ func TestFindItemInMap(t *testing.T) { } func TestFindItemInMap_WrongCase(t *testing.T) { + t.Parallel() v := make(map[KeyReference[string]]ValueReference[string]) v[KeyReference[string]{ Value: "pizza", @@ -37,6 +39,7 @@ func TestFindItemInMap_WrongCase(t *testing.T) { } func TestFindItemInMap_Error(t *testing.T) { + t.Parallel() v := make(map[KeyReference[string]]ValueReference[string]) v[KeyReference[string]{ Value: "pizza", @@ -47,7 +50,7 @@ func TestFindItemInMap_Error(t *testing.T) { } func TestLocateRefNode(t *testing.T) { - + t.Parallel() yml := `components: schemas: cake: @@ -69,7 +72,7 @@ func TestLocateRefNode(t *testing.T) { } func TestLocateRefNode_BadNode(t *testing.T) { - + t.Parallel() yml := `components: schemas: cake: @@ -94,7 +97,7 @@ func TestLocateRefNode_BadNode(t *testing.T) { } func TestLocateRefNode_Path(t *testing.T) { - + t.Parallel() yml := `paths: /burger/time: description: hello` @@ -115,7 +118,7 @@ func TestLocateRefNode_Path(t *testing.T) { } func TestLocateRefNode_Path_NotFound(t *testing.T) { - + t.Parallel() yml := `paths: /burger/time: description: hello` @@ -145,7 +148,7 @@ func (p *pizza) Build(_, _ *yaml.Node, _ *index.SpecIndex) error { } func TestExtractObject(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -169,7 +172,7 @@ func TestExtractObject(t *testing.T) { } func TestExtractObject_Ref(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -193,7 +196,7 @@ func TestExtractObject_Ref(t *testing.T) { } func TestExtractObject_DoubleRef(t *testing.T) { - + t.Parallel() yml := `components: schemas: cake: @@ -219,6 +222,7 @@ func TestExtractObject_DoubleRef(t *testing.T) { } func TestExtractObject_DoubleRef_Circular(t *testing.T) { + t.Parallel() yml := `components: schemas: loopy: @@ -249,6 +253,7 @@ func TestExtractObject_DoubleRef_Circular(t *testing.T) { } func TestExtractObject_DoubleRef_Circular_Fail(t *testing.T) { + t.Parallel() yml := `components: schemas: loopy: @@ -278,7 +283,7 @@ func TestExtractObject_DoubleRef_Circular_Fail(t *testing.T) { } func TestExtractObject_DoubleRef_Circular_Direct(t *testing.T) { - + t.Parallel() yml := `components: schemas: loopy: @@ -308,7 +313,7 @@ func TestExtractObject_DoubleRef_Circular_Direct(t *testing.T) { } func TestExtractObject_DoubleRef_Circular_Direct_Fail(t *testing.T) { - + t.Parallel() yml := `components: schemas: loopy: @@ -337,6 +342,7 @@ func TestExtractObject_DoubleRef_Circular_Direct_Fail(t *testing.T) { } +// TODO: why is this type unused? type test_borked struct { DontWork int } @@ -370,7 +376,7 @@ func (t *test_Good) Build(_, root *yaml.Node, idx *index.SpecIndex) error { } func TestExtractObject_BadLowLevelModel(t *testing.T) { - + t.Parallel() yml := `components: schemas: hey:` @@ -391,7 +397,7 @@ func TestExtractObject_BadLowLevelModel(t *testing.T) { } func TestExtractObject_BadBuild(t *testing.T) { - + t.Parallel() yml := `components: schemas: hey:` @@ -412,7 +418,7 @@ func TestExtractObject_BadBuild(t *testing.T) { } func TestExtractObject_BadLabel(t *testing.T) { - + t.Parallel() yml := `components: schemas: hey:` @@ -434,7 +440,7 @@ func TestExtractObject_BadLabel(t *testing.T) { } func TestExtractObject_PathIsCircular(t *testing.T) { - + t.Parallel() // first we need an index. yml := `paths: '/something/here': @@ -467,7 +473,7 @@ func TestExtractObject_PathIsCircular(t *testing.T) { } func TestExtractObject_PathIsCircular_IgnoreErrors(t *testing.T) { - + t.Parallel() // first we need an index. yml := `paths: '/something/here': @@ -503,7 +509,7 @@ func TestExtractObject_PathIsCircular_IgnoreErrors(t *testing.T) { } func TestExtractObjectRaw(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -526,7 +532,7 @@ func TestExtractObjectRaw(t *testing.T) { } func TestExtractObjectRaw_With_Ref(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -551,7 +557,7 @@ func TestExtractObjectRaw_With_Ref(t *testing.T) { } func TestExtractObjectRaw_Ref_Circular(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -579,7 +585,7 @@ func TestExtractObjectRaw_Ref_Circular(t *testing.T) { } func TestExtractObjectRaw_RefBroken(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -601,7 +607,7 @@ func TestExtractObjectRaw_RefBroken(t *testing.T) { } func TestExtractObjectRaw_Ref_NonBuildable(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -622,7 +628,7 @@ func TestExtractObjectRaw_Ref_NonBuildable(t *testing.T) { } func TestExtractObjectRaw_Ref_AlmostBuildable(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -643,7 +649,7 @@ func TestExtractObjectRaw_Ref_AlmostBuildable(t *testing.T) { } func TestExtractArray(t *testing.T) { - + t.Parallel() yml := `components: schemas: pizza: @@ -671,7 +677,7 @@ func TestExtractArray(t *testing.T) { } func TestExtractArray_Ref(t *testing.T) { - + t.Parallel() yml := `components: schemas: things: @@ -698,7 +704,7 @@ func TestExtractArray_Ref(t *testing.T) { } func TestExtractArray_Ref_Unbuildable(t *testing.T) { - + t.Parallel() yml := `components: schemas: things: @@ -722,7 +728,7 @@ func TestExtractArray_Ref_Unbuildable(t *testing.T) { } func TestExtractArray_Ref_Circular(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs: @@ -750,7 +756,7 @@ func TestExtractArray_Ref_Circular(t *testing.T) { } func TestExtractArray_Ref_Bad(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs: @@ -778,7 +784,7 @@ func TestExtractArray_Ref_Bad(t *testing.T) { } func TestExtractArray_Ref_Nested(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs: @@ -807,7 +813,7 @@ func TestExtractArray_Ref_Nested(t *testing.T) { } func TestExtractArray_Ref_Nested_Circular(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs: @@ -836,7 +842,7 @@ func TestExtractArray_Ref_Nested_Circular(t *testing.T) { } func TestExtractArray_Ref_Nested_BadRef(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs: @@ -863,7 +869,7 @@ func TestExtractArray_Ref_Nested_BadRef(t *testing.T) { } func TestExtractArray_Ref_Nested_CircularFlat(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs: @@ -892,7 +898,7 @@ func TestExtractArray_Ref_Nested_CircularFlat(t *testing.T) { } func TestExtractArray_BadBuild(t *testing.T) { - + t.Parallel() yml := `components: schemas: thongs:` @@ -914,6 +920,7 @@ func TestExtractArray_BadBuild(t *testing.T) { } func TestExtractExample_String(t *testing.T) { + t.Parallel() yml := `hi` var e yaml.Node _ = yaml.Unmarshal([]byte(yml), &e) @@ -923,6 +930,7 @@ func TestExtractExample_String(t *testing.T) { assert.Equal(t, "hi", exp.Value) } func TestExtractExample_Map(t *testing.T) { + t.Parallel() yml := `one: two` var e yaml.Node _ = yaml.Unmarshal([]byte(yml), &e) @@ -937,6 +945,7 @@ func TestExtractExample_Map(t *testing.T) { } func TestExtractExample_Array(t *testing.T) { + t.Parallel() yml := `- hello` var e yaml.Node _ = yaml.Unmarshal([]byte(yml), &e) @@ -951,6 +960,7 @@ func TestExtractExample_Array(t *testing.T) { } func TestExtractMapFlatNoLookup(t *testing.T) { + t.Parallel() yml := `components:` @@ -974,6 +984,7 @@ one: } func TestExtractMap_NoLookupWithExtensions(t *testing.T) { + t.Parallel() yml := `components:` @@ -1004,6 +1015,7 @@ one: } func TestExtractMap_NoLookupWithExtensions_UsingMerge(t *testing.T) { + t.Parallel() yml := `components:` @@ -1030,6 +1042,7 @@ one: } func TestExtractMap_NoLookupWithoutExtensions(t *testing.T) { + t.Parallel() yml := `components:` @@ -1056,6 +1069,7 @@ one: } func TestExtractMap_WithExtensions(t *testing.T) { + t.Parallel() yml := `components:` @@ -1078,6 +1092,7 @@ one: } func TestExtractMap_WithoutExtensions(t *testing.T) { + t.Parallel() yml := `components:` @@ -1100,6 +1115,7 @@ one: } func TestExtractMapFlatNoLookup_Ref(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1126,6 +1142,7 @@ one: } func TestExtractMapFlatNoLookup_Ref_Bad(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1152,6 +1169,7 @@ one: } func TestExtractMapFlatNoLookup_Ref_Circular(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1184,6 +1202,7 @@ one: } func TestExtractMapFlatNoLookup_Ref_BadBuild(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1210,6 +1229,7 @@ hello: } func TestExtractMapFlatNoLookup_Ref_AlmostBuild(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1236,6 +1256,7 @@ one: } func TestExtractMapFlat(t *testing.T) { + t.Parallel() yml := `components:` @@ -1259,6 +1280,7 @@ one: } func TestExtractMapFlat_Ref(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1290,6 +1312,7 @@ one: } func TestExtractMapFlat_DoubleRef(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1319,6 +1342,7 @@ func TestExtractMapFlat_DoubleRef(t *testing.T) { } func TestExtractMapFlat_DoubleRef_Error(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1346,6 +1370,7 @@ func TestExtractMapFlat_DoubleRef_Error(t *testing.T) { } func TestExtractMapFlat_DoubleRef_Error_NotFound(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1373,6 +1398,7 @@ func TestExtractMapFlat_DoubleRef_Error_NotFound(t *testing.T) { } func TestExtractMapFlat_DoubleRef_Circles(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1405,6 +1431,7 @@ func TestExtractMapFlat_DoubleRef_Circles(t *testing.T) { } func TestExtractMapFlat_Ref_Error(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1432,6 +1459,7 @@ func TestExtractMapFlat_Ref_Error(t *testing.T) { } func TestExtractMapFlat_Ref_Circ_Error(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1461,6 +1489,7 @@ func TestExtractMapFlat_Ref_Circ_Error(t *testing.T) { } func TestExtractMapFlat_Ref_Nested_Circ_Error(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1491,6 +1520,7 @@ func TestExtractMapFlat_Ref_Nested_Circ_Error(t *testing.T) { } func TestExtractMapFlat_Ref_Nested_Error(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1517,6 +1547,7 @@ func TestExtractMapFlat_Ref_Nested_Error(t *testing.T) { } func TestExtractMapFlat_BadKey_Ref_Nested_Error(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1543,6 +1574,7 @@ func TestExtractMapFlat_BadKey_Ref_Nested_Error(t *testing.T) { } func TestExtractMapFlat_Ref_Bad(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -1572,6 +1604,7 @@ func TestExtractMapFlat_Ref_Bad(t *testing.T) { } func TestLocateRefNode_RemoteFile(t *testing.T) { + t.Parallel() ymlFile := fmt.Sprintf(`components: schemas: @@ -1604,6 +1637,7 @@ func TestLocateRefNode_RemoteFile(t *testing.T) { } func TestExtractExtensions(t *testing.T) { + t.Parallel() yml := `x-bing: ding x-bong: 1 @@ -1662,6 +1696,7 @@ func TestAreEqual(t *testing.T) { } func TestGenerateHashString(t *testing.T) { + t.Parallel() assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", GenerateHashString(test_fresh{val: "hello"})) @@ -1678,6 +1713,7 @@ func TestGenerateHashString(t *testing.T) { } func TestGenerateHashString_Pointer(t *testing.T) { + t.Parallel() val := true assert.Equal(t, "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b", @@ -1689,6 +1725,7 @@ func TestGenerateHashString_Pointer(t *testing.T) { } func TestSetReference(t *testing.T) { + t.Parallel() type testObj struct { *Reference @@ -1702,6 +1739,7 @@ func TestSetReference(t *testing.T) { } func TestSetReference_nil(t *testing.T) { + t.Parallel() type testObj struct { *Reference diff --git a/datamodel/low/model_builder_test.go b/datamodel/low/model_builder_test.go index 2dbeac90..df642d9b 100644 --- a/datamodel/low/model_builder_test.go +++ b/datamodel/low/model_builder_test.go @@ -1,10 +1,11 @@ package low import ( - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" "sync" "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) type hotdog struct { @@ -377,8 +378,9 @@ func TestHandleSlicesOfBools(t *testing.T) { } func TestSetField_Ignore(t *testing.T) { - + t.Parallel() type Complex struct { + //lint:ignore U1000 only used in test name string } type internal struct { @@ -401,7 +403,7 @@ func TestSetField_Ignore(t *testing.T) { } func TestBuildModelAsync(t *testing.T) { - + t.Parallel() type internal struct { Thing KeyReference[map[KeyReference[string]]ValueReference[string]] } @@ -426,7 +428,7 @@ func TestBuildModelAsync(t *testing.T) { } func TestBuildModelAsync_Error(t *testing.T) { - + t.Parallel() type internal struct { Thing []NodeReference[any] } diff --git a/datamodel/low/reference.go b/datamodel/low/reference.go index da45df29..19b0ef11 100644 --- a/datamodel/low/reference.go +++ b/datamodel/low/reference.go @@ -2,6 +2,7 @@ package low import ( "fmt" + "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" @@ -258,10 +259,7 @@ func (n ValueReference[T]) SetReference(ref string) { // IsReference will return true if the key node contains a $ref func (n ValueReference[T]) IsReference() bool { - if n.Reference != "" { - return true - } - return false + return n.Reference != "" } func (n ValueReference[T]) MarshalYAML() (interface{}, error) { diff --git a/datamodel/low/reference_test.go b/datamodel/low/reference_test.go index 2a33f2e4..f1ea479d 100644 --- a/datamodel/low/reference_test.go +++ b/datamodel/low/reference_test.go @@ -6,10 +6,11 @@ package low import ( "crypto/sha256" "fmt" - "github.com/pb33f/libopenapi/utils" "strings" "testing" + "github.com/pb33f/libopenapi/utils" + "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/resolver" "github.com/stretchr/testify/assert" @@ -17,11 +18,13 @@ import ( ) func TestNodeReference_IsEmpty(t *testing.T) { + t.Parallel() nr := new(NodeReference[string]) assert.True(t, nr.IsEmpty()) } func TestNodeReference_GenerateMapKey(t *testing.T) { + t.Parallel() nr := new(NodeReference[string]) nr.ValueNode = &yaml.Node{ Line: 22, @@ -31,6 +34,7 @@ func TestNodeReference_GenerateMapKey(t *testing.T) { } func TestNodeReference_Mutate(t *testing.T) { + t.Parallel() nr := new(NodeReference[string]) nr.ValueNode = &yaml.Node{ Line: 22, @@ -49,6 +53,7 @@ func TestNodeReference_Mutate(t *testing.T) { } func TestNodeReference_RefNode(t *testing.T) { + t.Parallel() nr := new(NodeReference[string]) nr.KeyNode = &yaml.Node{ Content: []*yaml.Node{{ @@ -59,6 +64,7 @@ func TestNodeReference_RefNode(t *testing.T) { } func TestValueReference_Mutate(t *testing.T) { + t.Parallel() nr := new(ValueReference[string]) nr.ValueNode = &yaml.Node{ Line: 22, @@ -70,11 +76,13 @@ func TestValueReference_Mutate(t *testing.T) { } func TestValueReference_IsEmpty(t *testing.T) { + t.Parallel() nr := new(ValueReference[string]) assert.True(t, nr.IsEmpty()) } func TestValueReference_GenerateMapKey(t *testing.T) { + t.Parallel() nr := new(ValueReference[string]) nr.ValueNode = &yaml.Node{ Line: 22, @@ -86,11 +94,13 @@ func TestValueReference_GenerateMapKey(t *testing.T) { } func TestKeyReference_IsEmpty(t *testing.T) { + t.Parallel() nr := new(KeyReference[string]) assert.True(t, nr.IsEmpty()) } func TestKeyReference_GenerateMapKey(t *testing.T) { + t.Parallel() nr := new(KeyReference[string]) nr.KeyNode = &yaml.Node{ Line: 22, @@ -100,6 +110,7 @@ func TestKeyReference_GenerateMapKey(t *testing.T) { } func TestIsCircular_LookupFromJourney(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -137,7 +148,7 @@ func TestIsCircular_LookupFromJourney(t *testing.T) { } func TestIsCircular_LookupFromJourney_Optional(t *testing.T) { - + t.Parallel() yml := `components: schemas: Something: @@ -170,6 +181,7 @@ func TestIsCircular_LookupFromJourney_Optional(t *testing.T) { } func TestIsCircular_LookupFromLoopPoint(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -206,6 +218,7 @@ func TestIsCircular_LookupFromLoopPoint(t *testing.T) { } func TestIsCircular_LookupFromLoopPoint_Optional(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -238,6 +251,7 @@ func TestIsCircular_LookupFromLoopPoint_Optional(t *testing.T) { } func TestIsCircular_FromRefLookup(t *testing.T) { + t.Parallel() yml := `components: schemas: @@ -279,6 +293,7 @@ func TestIsCircular_FromRefLookup(t *testing.T) { } func TestIsCircular_FromRefLookup_Optional(t *testing.T) { + t.Parallel() yml := `components: schemas: NotCircle: @@ -315,14 +330,17 @@ func TestIsCircular_FromRefLookup_Optional(t *testing.T) { } func TestIsCircular_NoNode(t *testing.T) { + t.Parallel() assert.False(t, IsCircular(nil, nil)) } func TestGetCircularReferenceResult_NoNode(t *testing.T) { + t.Parallel() assert.Nil(t, GetCircularReferenceResult(nil, nil)) } func TestGetCircularReferenceResult_FromJourney(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -361,6 +379,7 @@ func TestGetCircularReferenceResult_FromJourney(t *testing.T) { } func TestGetCircularReferenceResult_FromJourney_Optional(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -395,6 +414,7 @@ func TestGetCircularReferenceResult_FromJourney_Optional(t *testing.T) { } func TestGetCircularReferenceResult_FromLoopPoint(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -433,6 +453,7 @@ func TestGetCircularReferenceResult_FromLoopPoint(t *testing.T) { } func TestGetCircularReferenceResult_FromLoopPoint_Optional(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -467,6 +488,7 @@ func TestGetCircularReferenceResult_FromLoopPoint_Optional(t *testing.T) { } func TestGetCircularReferenceResult_FromMappedRef(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -503,6 +525,7 @@ func TestGetCircularReferenceResult_FromMappedRef(t *testing.T) { } func TestGetCircularReferenceResult_FromMappedRef_Optional(t *testing.T) { + t.Parallel() yml := `components: schemas: Something: @@ -535,6 +558,7 @@ func TestGetCircularReferenceResult_FromMappedRef_Optional(t *testing.T) { } func TestGetCircularReferenceResult_NothingFound(t *testing.T) { + t.Parallel() yml := `components: schemas: NotCircle: @@ -557,12 +581,14 @@ func TestGetCircularReferenceResult_NothingFound(t *testing.T) { } func TestHashToString(t *testing.T) { + t.Parallel() assert.Equal(t, "5994471abb01112afcc18159f6cc74b4f511b99806da59b3caf5a9c173cacfc5", HashToString(sha256.Sum256([]byte("12345")))) } func TestReference_IsReference(t *testing.T) { + t.Parallel() ref := Reference{ Reference: "#/components/schemas/SomeSchema", } @@ -571,6 +597,7 @@ func TestReference_IsReference(t *testing.T) { } func TestNodeReference_NodeLineNumber(t *testing.T) { + t.Parallel() n := utils.CreateStringNode("pizza") nr := NodeReference[string]{ @@ -583,6 +610,7 @@ func TestNodeReference_NodeLineNumber(t *testing.T) { } func TestNodeReference_NodeLineNumberEmpty(t *testing.T) { + t.Parallel() nr := NodeReference[string]{ Value: "pizza", @@ -591,6 +619,7 @@ func TestNodeReference_NodeLineNumberEmpty(t *testing.T) { } func TestNodeReference_GetReference(t *testing.T) { + t.Parallel() nr := NodeReference[string]{ Reference: "#/happy/sunday", @@ -599,12 +628,14 @@ func TestNodeReference_GetReference(t *testing.T) { } func TestNodeReference_SetReference(t *testing.T) { + t.Parallel() nr := NodeReference[string]{} nr.SetReference("#/happy/sunday") } func TestNodeReference_IsReference(t *testing.T) { + t.Parallel() nr := NodeReference[string]{ ReferenceNode: true, @@ -613,6 +644,7 @@ func TestNodeReference_IsReference(t *testing.T) { } func TestNodeReference_GetKeyNode(t *testing.T) { + t.Parallel() nr := NodeReference[string]{ KeyNode: utils.CreateStringNode("pizza"), @@ -622,6 +654,7 @@ func TestNodeReference_GetKeyNode(t *testing.T) { } func TestNodeReference_GetValueUntyped(t *testing.T) { + t.Parallel() type anything struct { thing string @@ -635,6 +668,7 @@ func TestNodeReference_GetValueUntyped(t *testing.T) { } func TestValueReference_NodeLineNumber(t *testing.T) { + t.Parallel() n := utils.CreateStringNode("pizza") nr := ValueReference[string]{ @@ -647,6 +681,7 @@ func TestValueReference_NodeLineNumber(t *testing.T) { } func TestValueReference_NodeLineNumber_Nil(t *testing.T) { + t.Parallel() nr := ValueReference[string]{ Value: "pizza", @@ -656,6 +691,7 @@ func TestValueReference_NodeLineNumber_Nil(t *testing.T) { } func TestValueReference_GetReference(t *testing.T) { + t.Parallel() nr := ValueReference[string]{ Reference: "#/happy/sunday", @@ -664,12 +700,14 @@ func TestValueReference_GetReference(t *testing.T) { } func TestValueReference_SetReference(t *testing.T) { + t.Parallel() nr := ValueReference[string]{} nr.SetReference("#/happy/sunday") } func TestValueReference_GetValueUntyped(t *testing.T) { + t.Parallel() type anything struct { thing string @@ -683,6 +721,7 @@ func TestValueReference_GetValueUntyped(t *testing.T) { } func TestValueReference_IsReference(t *testing.T) { + t.Parallel() nr := NodeReference[string]{ ReferenceNode: true, @@ -691,6 +730,7 @@ func TestValueReference_IsReference(t *testing.T) { } func TestValueReference_MarshalYAML_Ref(t *testing.T) { + t.Parallel() nr := ValueReference[string]{ ReferenceNode: true, @@ -703,6 +743,7 @@ func TestValueReference_MarshalYAML_Ref(t *testing.T) { } func TestValueReference_MarshalYAML(t *testing.T) { + t.Parallel() v := map[string]interface{}{ "beer": "burger", @@ -726,6 +767,7 @@ wine: cheese` } func TestKeyReference_GetValueUntyped(t *testing.T) { + t.Parallel() type anything struct { thing string @@ -739,6 +781,7 @@ func TestKeyReference_GetValueUntyped(t *testing.T) { } func TestKeyReference_GetKeyNode(t *testing.T) { + t.Parallel() kn := utils.CreateStringNode("pizza") kn.Line = 3 diff --git a/datamodel/low/v2/definitions_test.go b/datamodel/low/v2/definitions_test.go index dedec28e..d2a87ae1 100644 --- a/datamodel/low/v2/definitions_test.go +++ b/datamodel/low/v2/definitions_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestDefinitions_Schemas_Build_Error(t *testing.T) { - + t.Parallel() yml := `gonna: $ref: break` @@ -31,7 +32,7 @@ func TestDefinitions_Schemas_Build_Error(t *testing.T) { } func TestDefinitions_Parameters_Build_Error(t *testing.T) { - + t.Parallel() yml := `gonna: $ref: break` @@ -50,7 +51,7 @@ func TestDefinitions_Parameters_Build_Error(t *testing.T) { } func TestDefinitions_Hash(t *testing.T) { - + t.Parallel() yml := `nice: description: rice` @@ -70,7 +71,7 @@ func TestDefinitions_Hash(t *testing.T) { } func TestDefinitions_Responses_Build_Error(t *testing.T) { - + t.Parallel() yml := `gonna: $ref: break` @@ -89,7 +90,7 @@ func TestDefinitions_Responses_Build_Error(t *testing.T) { } func TestDefinitions_Security_Build_Error(t *testing.T) { - + t.Parallel() yml := `gonna: $ref: break` diff --git a/datamodel/low/v2/examples_test.go b/datamodel/low/v2/examples_test.go index 28eb350b..9e630347 100644 --- a/datamodel/low/v2/examples_test.go +++ b/datamodel/low/v2/examples_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestExamples_Hash(t *testing.T) { - + t.Parallel() yml := `something: string yes: - more diff --git a/datamodel/low/v2/header_test.go b/datamodel/low/v2/header_test.go index 36770203..5b6773a4 100644 --- a/datamodel/low/v2/header_test.go +++ b/datamodel/low/v2/header_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestHeader_Build(t *testing.T) { - + t.Parallel() yml := `items: $ref: break` @@ -31,7 +32,7 @@ func TestHeader_Build(t *testing.T) { } func TestHeader_DefaultAsSlice(t *testing.T) { - + t.Parallel() yml := `x-ext: thing default: - why @@ -52,7 +53,7 @@ default: } func TestHeader_DefaultAsObject(t *testing.T) { - + t.Parallel() yml := `default: lets: create: @@ -71,7 +72,7 @@ func TestHeader_DefaultAsObject(t *testing.T) { } func TestHeader_NoDefault(t *testing.T) { - + t.Parallel() yml := `minimum: 12` var idxNode yaml.Node @@ -86,7 +87,7 @@ func TestHeader_NoDefault(t *testing.T) { } func TestHeader_Hash_n_Grab(t *testing.T) { - + t.Parallel() yml := `description: head type: string format: left diff --git a/datamodel/low/v2/items_test.go b/datamodel/low/v2/items_test.go index c34ed4d4..f38611da 100644 --- a/datamodel/low/v2/items_test.go +++ b/datamodel/low/v2/items_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestItems_Build(t *testing.T) { - + t.Parallel() yml := `items: $ref: break` @@ -30,7 +31,7 @@ func TestItems_Build(t *testing.T) { } func TestItems_DefaultAsSlice(t *testing.T) { - + t.Parallel() yml := `x-thing: thing default: - pizza @@ -49,7 +50,7 @@ default: } func TestItems_DefaultAsMap(t *testing.T) { - + t.Parallel() yml := `default: hot: pizza tasty: beer` @@ -67,7 +68,7 @@ func TestItems_DefaultAsMap(t *testing.T) { } func TestItems_Hash_n_Grab(t *testing.T) { - + t.Parallel() yml := `type: string format: left collectionFormat: nice diff --git a/datamodel/low/v2/operation_test.go b/datamodel/low/v2/operation_test.go index f658358e..98e7b483 100644 --- a/datamodel/low/v2/operation_test.go +++ b/datamodel/low/v2/operation_test.go @@ -4,16 +4,17 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestOperation_Build_ExternalDocs(t *testing.T) { - + t.Parallel() yml := `externalDocs: $ref: break` @@ -32,7 +33,7 @@ func TestOperation_Build_ExternalDocs(t *testing.T) { } func TestOperation_Build_Params(t *testing.T) { - + t.Parallel() yml := `parameters: $ref: break` @@ -51,7 +52,7 @@ func TestOperation_Build_Params(t *testing.T) { } func TestOperation_Build_Responses(t *testing.T) { - + t.Parallel() yml := `responses: $ref: break` @@ -70,7 +71,7 @@ func TestOperation_Build_Responses(t *testing.T) { } func TestOperation_Build_Security(t *testing.T) { - + t.Parallel() yml := `security: $ref: break` @@ -89,7 +90,7 @@ func TestOperation_Build_Security(t *testing.T) { } func TestOperation_Hash_n_Grab(t *testing.T) { - + t.Parallel() yml := `tags: - nice - hat diff --git a/datamodel/low/v2/package_test.go b/datamodel/low/v2/package_test.go index 9ebb422e..2a72733d 100644 --- a/datamodel/low/v2/package_test.go +++ b/datamodel/low/v2/package_test.go @@ -5,8 +5,9 @@ package v2 import ( "fmt" + "os" + "github.com/pb33f/libopenapi/datamodel" - "io/ioutil" ) // How to create a low-level Swagger / OpenAPI 2 Document from a specification @@ -15,19 +16,17 @@ func Example_createLowLevelSwaggerDocument() { // How to create a low-level OpenAPI 2 Document // load petstore into bytes - petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev2.json") + petstoreBytes, _ := os.ReadFile("../../../test_specs/petstorev2.json") // read in specification info, _ := datamodel.ExtractSpecInfo(petstoreBytes) // build low-level document model - document, errors := CreateDocument(info) + document, err := CreateDocument(info) - // if something went wrong, a slice of errors is returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %s\n", errors[i].Error()) - } + // if something went wrong, a slice of err is returned + if err != nil { + fmt.Printf("error: %v\n", err) panic("cannot build document") } @@ -43,19 +42,17 @@ func ExampleCreateDocument() { // How to create a low-level OpenAPI 2 Document // load petstore into bytes - petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev2.json") + petstoreBytes, _ := os.ReadFile("../../../test_specs/petstorev2.json") // read in specification info, _ := datamodel.ExtractSpecInfo(petstoreBytes) // build low-level document model - document, errors := CreateDocument(info) + document, err := CreateDocument(info) - // if something went wrong, a slice of errors is returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %s\n", errors[i].Error()) - } + // if something went wrong, a slice of err is returned + if err != nil { + fmt.Printf("error: %v\n", err) panic("cannot build document") } diff --git a/datamodel/low/v2/parameter_test.go b/datamodel/low/v2/parameter_test.go index 48b9575e..78c34397 100644 --- a/datamodel/low/v2/parameter_test.go +++ b/datamodel/low/v2/parameter_test.go @@ -4,16 +4,17 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestParameter_Build(t *testing.T) { - + t.Parallel() yml := `$ref: break` var idxNode yaml.Node diff --git a/datamodel/low/v2/path_item.go b/datamodel/low/v2/path_item.go index 8bdff817..c2778c96 100644 --- a/datamodel/low/v2/path_item.go +++ b/datamodel/low/v2/path_item.go @@ -6,13 +6,14 @@ package v2 import ( "crypto/sha256" "fmt" + "sort" + "strings" + "sync" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" - "sort" - "strings" - "sync" ) // PathItem represents a low-level Swagger / OpenAPI 2 PathItem object. @@ -102,19 +103,19 @@ func (p *PathItem) Build(_, root *yaml.Node, idx *index.SpecIndex) error { // the only thing we now care about is handling operations, filter out anything that's not a verb. switch currentNode.Value { case GetLabel: - break + // valid label case PostLabel: - break + // valid label case PutLabel: - break + // valid label case PatchLabel: - break + // valid label case DeleteLabel: - break + // valid label case HeadLabel: - break + // valid label case OptionsLabel: - break + // valid label default: continue // ignore everything else. } diff --git a/datamodel/low/v2/path_item_test.go b/datamodel/low/v2/path_item_test.go index 2efff318..af54cf29 100644 --- a/datamodel/low/v2/path_item_test.go +++ b/datamodel/low/v2/path_item_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestPathItem_Build_Params(t *testing.T) { - + t.Parallel() yml := `parameters: $ref: break` @@ -31,7 +32,7 @@ func TestPathItem_Build_Params(t *testing.T) { } func TestPathItem_Build_MethodFail(t *testing.T) { - + t.Parallel() yml := `post: $ref: break` @@ -50,7 +51,7 @@ func TestPathItem_Build_MethodFail(t *testing.T) { } func TestPathItem_Hash(t *testing.T) { - + t.Parallel() yml := `get: description: get me up put: diff --git a/datamodel/low/v2/paths_test.go b/datamodel/low/v2/paths_test.go index 127b8611..b41007c0 100644 --- a/datamodel/low/v2/paths_test.go +++ b/datamodel/low/v2/paths_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestPaths_Build(t *testing.T) { - + t.Parallel() yml := `"/fresh/code": $ref: break` @@ -31,7 +32,7 @@ func TestPaths_Build(t *testing.T) { } func TestPaths_FindPathAndKey(t *testing.T) { - + t.Parallel() yml := `/no/sleep: get: description: til brooklyn @@ -54,7 +55,7 @@ func TestPaths_FindPathAndKey(t *testing.T) { } func TestPaths_Hash(t *testing.T) { - + t.Parallel() yml := `/data/dog: get: description: does data kinda, ish. diff --git a/datamodel/low/v2/response_test.go b/datamodel/low/v2/response_test.go index 4ca1146f..60a41ff9 100644 --- a/datamodel/low/v2/response_test.go +++ b/datamodel/low/v2/response_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestResponse_Build_Schema(t *testing.T) { - + t.Parallel() yml := `schema: $ref: break` @@ -31,7 +32,7 @@ func TestResponse_Build_Schema(t *testing.T) { } func TestResponse_Build_Examples(t *testing.T) { - + t.Parallel() yml := `examples: $ref: break` @@ -50,7 +51,7 @@ func TestResponse_Build_Examples(t *testing.T) { } func TestResponse_Build_Headers(t *testing.T) { - + t.Parallel() yml := `headers: $ref: break` @@ -69,7 +70,7 @@ func TestResponse_Build_Headers(t *testing.T) { } func TestResponse_Hash(t *testing.T) { - + t.Parallel() yml := `description: your thing, sir. schema: type: string diff --git a/datamodel/low/v2/responses_test.go b/datamodel/low/v2/responses_test.go index ae38e7f3..110c341a 100644 --- a/datamodel/low/v2/responses_test.go +++ b/datamodel/low/v2/responses_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestResponses_Build_Response(t *testing.T) { - + t.Parallel() yml := `- $ref: break` var idxNode yaml.Node @@ -30,7 +31,7 @@ func TestResponses_Build_Response(t *testing.T) { } func TestResponses_Build_Response_Default(t *testing.T) { - + t.Parallel() yml := `default: $ref: break` @@ -49,7 +50,7 @@ func TestResponses_Build_Response_Default(t *testing.T) { } func TestResponses_Build_WrongType(t *testing.T) { - + t.Parallel() yml := `- $ref: break` var idxNode yaml.Node @@ -67,7 +68,7 @@ func TestResponses_Build_WrongType(t *testing.T) { } func TestResponses_Hash(t *testing.T) { - + t.Parallel() yml := `default: description: I am a potato 200: diff --git a/datamodel/low/v2/scopes_test.go b/datamodel/low/v2/scopes_test.go index 8bcd33bb..b1241c8f 100644 --- a/datamodel/low/v2/scopes_test.go +++ b/datamodel/low/v2/scopes_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestScopes_Hash(t *testing.T) { - + t.Parallel() yml := `burgers: chips pizza: beans x-men: needs a reboot or a refresh` diff --git a/datamodel/low/v2/security_scheme_test.go b/datamodel/low/v2/security_scheme_test.go index 81b0551a..db4ae8f5 100644 --- a/datamodel/low/v2/security_scheme_test.go +++ b/datamodel/low/v2/security_scheme_test.go @@ -4,15 +4,16 @@ package v2 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestSecurityScheme_Build_Borked(t *testing.T) { - + t.Parallel() yml := `scopes: $ref: break` @@ -31,7 +32,7 @@ func TestSecurityScheme_Build_Borked(t *testing.T) { } func TestSecurityScheme_Build_Scopes(t *testing.T) { - + t.Parallel() yml := `scopes: some:thing: here something: there` @@ -52,7 +53,7 @@ func TestSecurityScheme_Build_Scopes(t *testing.T) { } func TestSecurityScheme_Hash(t *testing.T) { - + t.Parallel() yml := `type: secure description: a very secure thing name: securityPerson diff --git a/datamodel/low/v2/swagger.go b/datamodel/low/v2/swagger.go index 872c8dec..5af00751 100644 --- a/datamodel/low/v2/swagger.go +++ b/datamodel/low/v2/swagger.go @@ -16,6 +16,7 @@ import ( "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/pb33f/libopenapi/resolver" "gopkg.in/yaml.v3" ) @@ -123,21 +124,21 @@ func (s *Swagger) GetExtensions() map[low.KeyReference[string]]low.ValueReferenc // CreateDocumentFromConfig will create a new Swagger document from the provided SpecInfo and DocumentConfiguration. func CreateDocumentFromConfig(info *datamodel.SpecInfo, - configuration *datamodel.DocumentConfiguration) (*Swagger, []error) { + configuration *datamodel.DocumentConfiguration) (*Swagger, error) { return createDocument(info, configuration) } // CreateDocument will create a new Swagger document from the provided SpecInfo. // // Deprecated: Use CreateDocumentFromConfig instead. -func CreateDocument(info *datamodel.SpecInfo) (*Swagger, []error) { +func CreateDocument(info *datamodel.SpecInfo) (*Swagger, error) { return createDocument(info, &datamodel.DocumentConfiguration{ AllowRemoteReferences: true, AllowFileReferences: true, }) } -func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, []error) { +func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Swagger, error) { doc := Swagger{Swagger: low.ValueReference[string]{Value: info.Version, ValueNode: info.RootNode}} doc.Extensions = low.ExtractExtensions(info.RootNode.Content[0]) @@ -151,7 +152,7 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur doc.Index = idx doc.SpecInfo = info - var errors []error + var errs []error // build out swagger scalar variables. _ = low.BuildModel(info.RootNode.Content[0], &doc) @@ -159,7 +160,7 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur // extract externalDocs extDocs, err := low.ExtractObject[*base.ExternalDoc](base.ExternalDocsLabel, info.RootNode, idx) if err != nil { - errors = append(errors, err) + errs = append(errs, err) } doc.ExternalDocs = extDocs @@ -170,7 +171,7 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur if len(resolvingErrors) > 0 { for r := range resolvingErrors { - errors = append(errors, resolvingErrors[r]) + errs = append(errs, resolvingErrors[r]) } } @@ -196,11 +197,11 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur completedExtractions++ case e := <-errChan: completedExtractions++ - errors = append(errors, e) + errs = append(errs, e) } } - return &doc, errors + return &doc, errorutils.Join(errs...) } func (s *Swagger) GetExternalDocs() *low.NodeReference[any] { diff --git a/datamodel/low/v2/swagger_test.go b/datamodel/low/v2/swagger_test.go index c099194e..96eeecc1 100644 --- a/datamodel/low/v2/swagger_test.go +++ b/datamodel/low/v2/swagger_test.go @@ -5,10 +5,12 @@ package v2 import ( "fmt" + "os" + "testing" + "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" ) var doc *Swagger @@ -17,9 +19,9 @@ func initTest() { if doc != nil { return } - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + data, _ := os.ReadFile("../../../test_specs/petstorev2-complete.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -38,7 +40,7 @@ func initTest() { } func BenchmarkCreateDocument(b *testing.B) { - data, _ := ioutil.ReadFile("../../../test_specs/petstorev2-complete.yaml") + data, _ := os.ReadFile("../../../test_specs/petstorev2-complete.yaml") info, _ := datamodel.ExtractSpecInfo(data) for i := 0; i < b.N; i++ { doc, _ = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ @@ -182,7 +184,7 @@ func TestCreateDocument_ExternalDocsBad(t *testing.T) { $ref: bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -191,7 +193,7 @@ func TestCreateDocument_ExternalDocsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_TagsBad(t *testing.T) { @@ -200,7 +202,7 @@ func TestCreateDocument_TagsBad(t *testing.T) { $ref: bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -209,7 +211,7 @@ func TestCreateDocument_TagsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_PathsBad(t *testing.T) { @@ -222,7 +224,7 @@ func TestCreateDocument_PathsBad(t *testing.T) { $ref: bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -231,7 +233,7 @@ func TestCreateDocument_PathsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_SecurityBad(t *testing.T) { @@ -240,7 +242,7 @@ func TestCreateDocument_SecurityBad(t *testing.T) { $ref: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -249,7 +251,7 @@ func TestCreateDocument_SecurityBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { @@ -258,7 +260,7 @@ func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { $ref: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -267,7 +269,7 @@ func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_ResponsesBad(t *testing.T) { @@ -276,7 +278,7 @@ func TestCreateDocument_ResponsesBad(t *testing.T) { $ref: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -285,7 +287,7 @@ func TestCreateDocument_ResponsesBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_ParametersBad(t *testing.T) { @@ -294,7 +296,7 @@ func TestCreateDocument_ParametersBad(t *testing.T) { $ref: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -303,7 +305,7 @@ func TestCreateDocument_ParametersBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_DefinitionsBad(t *testing.T) { @@ -312,7 +314,7 @@ func TestCreateDocument_DefinitionsBad(t *testing.T) { $ref: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -321,7 +323,7 @@ func TestCreateDocument_DefinitionsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_InfoBad(t *testing.T) { @@ -330,7 +332,7 @@ func TestCreateDocument_InfoBad(t *testing.T) { $ref: ` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocument(info) wait := true for wait { @@ -339,15 +341,15 @@ func TestCreateDocument_InfoBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCircularReferenceError(t *testing.T) { - data, _ := ioutil.ReadFile("../../../test_specs/swagger-circular-tests.yaml") + data, _ := os.ReadFile("../../../test_specs/swagger-circular-tests.yaml") info, _ := datamodel.ExtractSpecInfo(data) circDoc, err := CreateDocument(info) assert.NotNil(t, circDoc) - assert.Len(t, err, 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) } diff --git a/datamodel/low/v3/callback_test.go b/datamodel/low/v3/callback_test.go index c98389e0..07313421 100644 --- a/datamodel/low/v3/callback_test.go +++ b/datamodel/low/v3/callback_test.go @@ -4,15 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestCallback_Build_Success(t *testing.T) { - + t.Parallel() yml := `'{$request.query.queryUrl}': post: requestBody: @@ -41,7 +42,7 @@ func TestCallback_Build_Success(t *testing.T) { } func TestCallback_Build_Error(t *testing.T) { - + t.Parallel() // first we need an index. doc := `components: schemas: @@ -71,7 +72,7 @@ func TestCallback_Build_Error(t *testing.T) { } func TestCallback_Build_Using_InlineRef(t *testing.T) { - + t.Parallel() // first we need an index. doc := `components: schemas: @@ -112,7 +113,7 @@ func TestCallback_Build_Using_InlineRef(t *testing.T) { } func TestCallback_Hash(t *testing.T) { - + t.Parallel() yml := `x-seed: grow pizza: description: cheesy diff --git a/datamodel/low/v3/components_test.go b/datamodel/low/v3/components_test.go index a18a7dac..3342d944 100644 --- a/datamodel/low/v3/components_test.go +++ b/datamodel/low/v3/components_test.go @@ -4,11 +4,12 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) var testComponentsYaml = ` @@ -64,7 +65,7 @@ var testComponentsYaml = ` description: eighteen of many` func TestComponents_Build_Success(t *testing.T) { - + t.Parallel() var idxNode yaml.Node mErr := yaml.Unmarshal([]byte(testComponentsYaml), &idxNode) assert.NoError(t, mErr) @@ -104,7 +105,7 @@ func TestComponents_Build_Success(t *testing.T) { } func TestComponents_Build_Success_Skip(t *testing.T) { - + t.Parallel() yml := `components:` var idxNode yaml.Node @@ -122,7 +123,7 @@ func TestComponents_Build_Success_Skip(t *testing.T) { } func TestComponents_Build_Fail(t *testing.T) { - + t.Parallel() yml := ` parameters: schema: @@ -143,7 +144,7 @@ func TestComponents_Build_Fail(t *testing.T) { } func TestComponents_Build_ParameterFail(t *testing.T) { - + t.Parallel() yml := ` parameters: pizza: @@ -165,7 +166,7 @@ func TestComponents_Build_ParameterFail(t *testing.T) { } func TestComponents_Build_Fail_TypeFail(t *testing.T) { - + t.Parallel() yml := ` parameters: - schema: @@ -185,7 +186,7 @@ func TestComponents_Build_Fail_TypeFail(t *testing.T) { } func TestComponents_Build_ExtensionTest(t *testing.T) { - + t.Parallel() yml := `x-curry: seagull headers: x-curry-gull: vinadloo` diff --git a/datamodel/low/v3/create_document.go b/datamodel/low/v3/create_document.go index fce4e7ee..9ccd8c24 100644 --- a/datamodel/low/v3/create_document.go +++ b/datamodel/low/v3/create_document.go @@ -9,6 +9,7 @@ import ( "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/pb33f/libopenapi/resolver" "github.com/pb33f/libopenapi/utils" ) @@ -17,7 +18,7 @@ import ( // // Deprecated: Use CreateDocumentFromConfig instead. This function will be removed in a later version, it // defaults to allowing file and remote references, and does not support relative file references. -func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) { +func CreateDocument(info *datamodel.SpecInfo) (*Document, error) { config := datamodel.DocumentConfiguration{ AllowFileReferences: true, AllowRemoteReferences: true, @@ -26,15 +27,15 @@ func CreateDocument(info *datamodel.SpecInfo) (*Document, []error) { } // CreateDocumentFromConfig Create a new document from the provided SpecInfo and DocumentConfiguration pointer. -func CreateDocumentFromConfig(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Document, []error) { +func CreateDocumentFromConfig(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Document, error) { return createDocument(info, config) } -func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Document, []error) { +func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfiguration) (*Document, error) { _, labelNode, versionNode := utils.FindKeyNodeFull(OpenAPILabel, info.RootNode.Content) var version low.NodeReference[string] if versionNode == nil { - return nil, []error{errors.New("no openapi version/tag found, cannot create document")} + return nil, errors.New("no openapi version/tag found, cannot create document") } version = low.NodeReference[string]{Value: versionNode.Value, KeyNode: labelNode, ValueNode: versionNode} doc := Document{Version: version} @@ -109,7 +110,9 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur go runExtraction(info, &doc, idx, f, &errs, &wg) } wg.Wait() - return &doc, errs + + // circular errors need to be returned with struct + return &doc, errorutils.Join(errs...) } func extractInfo(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 563ab536..0d903379 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -1,13 +1,17 @@ package v3 import ( + "errors" "fmt" "os" "testing" "github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/internal/errorutils" + "github.com/pb33f/libopenapi/resolver" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var doc *Document @@ -18,7 +22,7 @@ func initTest() { } data, _ := os.ReadFile("../../../test_specs/burgershop.openapi.yaml") info, _ := datamodel.ExtractSpecInfo(data) - var err []error + var err error // deprecated function test. doc, err = CreateDocument(info) if err != nil { @@ -45,9 +49,7 @@ func BenchmarkCreateDocument_Circular(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } @@ -62,22 +64,22 @@ func BenchmarkCreateDocument_k8s(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } func TestCircularReferenceError(t *testing.T) { - + t.Parallel() data, _ := os.ReadFile("../../../test_specs/circular-tests.yaml") info, _ := datamodel.ExtractSpecInfo(data) circDoc, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) + + // lower level packages return errors and potentially a document. assert.NotNil(t, circDoc) - assert.Len(t, err, 3) } func BenchmarkCreateDocument_Stripe(b *testing.B) { @@ -89,9 +91,7 @@ func BenchmarkCreateDocument_Stripe(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } @@ -103,14 +103,12 @@ func BenchmarkCreateDocument_Petstore(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } func TestCreateDocumentStripe(t *testing.T) { - + t.Parallel() data, _ := os.ReadFile("../../../test_specs/stripe.yaml") info, _ := datamodel.ExtractSpecInfo(data) d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ @@ -118,7 +116,7 @@ func TestCreateDocumentStripe(t *testing.T) { AllowRemoteReferences: false, BasePath: "/here", }) - assert.Len(t, err, 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) assert.Equal(t, "3.0.0", d.Version.Value) assert.Equal(t, "Stripe API", d.Info.Value.Title.Value) @@ -175,16 +173,17 @@ func TestCreateDocument_WebHooks(t *testing.T) { } func TestCreateDocument_WebHooks_Error(t *testing.T) { + t.Parallel() yml := `webhooks: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Servers(t *testing.T) { @@ -559,6 +558,7 @@ func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) { } func TestCreateDocument_Components_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 components: schemas: @@ -568,12 +568,13 @@ components: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 0) + require.NoError(t, err) + require.Len(t, errorutils.ShallowUnwrap(err), 0) ob := doc.Components.Value.FindSchema("bork").Value ob.Schema() @@ -581,21 +582,23 @@ components: } func TestCreateDocument_Webhooks_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 webhooks: aHook: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Components_Error_Extract(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 components: parameters: @@ -603,73 +606,78 @@ components: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Paths_Errors(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 paths: /p: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Tags_Errors(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 tags: - $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Security_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 security: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_ExternalDoc_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 externalDocs: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_YamlAnchor(t *testing.T) { + t.Parallel() // load petstore into bytes anchorDocument, _ := os.ReadFile("../../../test_specs/yaml-anchor.yaml") @@ -677,16 +685,14 @@ func TestCreateDocument_YamlAnchor(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(anchorDocument) // build low-level document model - document, errors := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ + document, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - // if something went wrong, a slice of errors is returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %s\n", errors[i].Error()) - } + // if something went wrong, a slice of err is returned + if err != nil { + fmt.Printf("error: %v\n", err) panic("cannot build document") } @@ -732,16 +738,14 @@ func ExampleCreateDocument() { info, _ := datamodel.ExtractSpecInfo(petstoreBytes) // build low-level document model - document, errors := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ + document, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, }) - // if something went wrong, a slice of errors is returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %s\n", errors[i].Error()) - } + // if something went wrong, a slice of err is returned + if err != nil { + fmt.Printf("error: %v\n", err) panic("cannot build document") } @@ -749,3 +753,14 @@ func ExampleCreateDocument() { fmt.Print(document.Info.Value.Contact.Value.Email.Value) // Output: apiteam@swagger.io } + +func panicOnUnknown(err error) { + for _, err := range errorutils.ShallowUnwrap(err) { + var resolvErr *resolver.ResolvingError + if errors.As(err, &resolvErr) && resolvErr.CircularReference != nil { + continue + } else if err != nil { + panic(fmt.Errorf("this should not error: %w", err)) + } + } +} diff --git a/datamodel/low/v3/encoding_test.go b/datamodel/low/v3/encoding_test.go index 1f1501d9..34e7e5a1 100644 --- a/datamodel/low/v3/encoding_test.go +++ b/datamodel/low/v3/encoding_test.go @@ -4,15 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestEncoding_Build_Success(t *testing.T) { - + t.Parallel() yml := `contentType: hot/cakes headers: ohMyStars: @@ -45,7 +46,7 @@ explode: true` } func TestEncoding_Build_Error(t *testing.T) { - + t.Parallel() yml := `contentType: hot/cakes headers: $ref: #/borked` @@ -64,7 +65,7 @@ headers: } func TestEncoding_Hash(t *testing.T) { - + t.Parallel() yml := `contentType: application/waffle headers: heady: diff --git a/datamodel/low/v3/examples_test.go b/datamodel/low/v3/examples_test.go index 8f71efaa..7d379c76 100644 --- a/datamodel/low/v3/examples_test.go +++ b/datamodel/low/v3/examples_test.go @@ -5,8 +5,9 @@ package v3 import ( "fmt" + "os" + "github.com/pb33f/libopenapi/datamodel" - "io/ioutil" ) // How to create a low-level OpenAPI 3+ Document from an OpenAPI specification @@ -14,19 +15,17 @@ func Example_createLowLevelOpenAPIDocument() { // How to create a low-level OpenAPI 3 Document // load petstore into bytes - petstoreBytes, _ := ioutil.ReadFile("../../../test_specs/petstorev3.json") + petstoreBytes, _ := os.ReadFile("../../../test_specs/petstorev3.json") // read in specification info, _ := datamodel.ExtractSpecInfo(petstoreBytes) // build low-level document model - document, errors := CreateDocument(info) + document, err := CreateDocument(info) // if something went wrong, a slice of errors is returned - if len(errors) > 0 { - for i := range errors { - fmt.Printf("error: %s\n", errors[i].Error()) - } + if err != nil { + fmt.Printf("error: %v\n", err) panic("cannot build document") } diff --git a/datamodel/low/v3/header_test.go b/datamodel/low/v3/header_test.go index 44df47bf..83d10e6a 100644 --- a/datamodel/low/v3/header_test.go +++ b/datamodel/low/v3/header_test.go @@ -4,15 +4,17 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestHeader_Build(t *testing.T) { + t.Parallel() yml := `description: michelle, meddy and maddy required: true deprecated: false @@ -86,6 +88,7 @@ content: } func TestHeader_Build_Success_Examples(t *testing.T) { + t.Parallel() yml := `examples: family: value: @@ -117,6 +120,7 @@ func TestHeader_Build_Success_Examples(t *testing.T) { } func TestHeader_Build_Fail_Examples(t *testing.T) { + t.Parallel() yml := `examples: family: $ref: I AM BORKED` @@ -134,6 +138,7 @@ func TestHeader_Build_Fail_Examples(t *testing.T) { } func TestHeader_Build_Fail_Schema(t *testing.T) { + t.Parallel() yml := `schema: $ref: I will fail.` @@ -150,6 +155,7 @@ func TestHeader_Build_Fail_Schema(t *testing.T) { } func TestHeader_Build_Fail_Content(t *testing.T) { + t.Parallel() yml := `content: ohMyStars: $ref: fail!` @@ -167,7 +173,7 @@ func TestHeader_Build_Fail_Content(t *testing.T) { } func TestEncoding_Hash_n_Grab(t *testing.T) { - + t.Parallel() yml := `description: heady required: true deprecated: true diff --git a/datamodel/low/v3/link_test.go b/datamodel/low/v3/link_test.go index cb81f4b8..24a1c5ec 100644 --- a/datamodel/low/v3/link_test.go +++ b/datamodel/low/v3/link_test.go @@ -4,15 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestLink_Build(t *testing.T) { - + t.Parallel() yml := `operationRef: '#/someref' operationId: someId parameters: @@ -56,7 +57,7 @@ x-linky: slinky } func TestLink_Build_Fail(t *testing.T) { - + t.Parallel() yml := `operationRef: '#/someref' operationId: someId parameters: @@ -81,7 +82,7 @@ server: } func TestLink_Hash(t *testing.T) { - + t.Parallel() yml := `operationRef: something operationId: someWhere parameters: diff --git a/datamodel/low/v3/media_type_test.go b/datamodel/low/v3/media_type_test.go index 27764da0..d73de44c 100644 --- a/datamodel/low/v3/media_type_test.go +++ b/datamodel/low/v3/media_type_test.go @@ -4,14 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestMediaType_Build(t *testing.T) { + t.Parallel() yml := `schema: type: string example: hello @@ -45,6 +47,7 @@ x-rock: and roll` } func TestMediaType_Build_Fail_Schema(t *testing.T) { + t.Parallel() yml := `schema: $ref: #bork` @@ -61,6 +64,7 @@ func TestMediaType_Build_Fail_Schema(t *testing.T) { } func TestMediaType_Build_Fail_Examples(t *testing.T) { + t.Parallel() yml := `examples: waff: $ref: #bork` @@ -79,6 +83,7 @@ func TestMediaType_Build_Fail_Examples(t *testing.T) { } func TestMediaType_Build_Fail_Encoding(t *testing.T) { + t.Parallel() yml := `encoding: wiff: $ref: #bork` @@ -96,6 +101,7 @@ func TestMediaType_Build_Fail_Encoding(t *testing.T) { } func TestMediaType_Hash(t *testing.T) { + t.Parallel() yml := `schema: type: string diff --git a/datamodel/low/v3/oauth_flows_test.go b/datamodel/low/v3/oauth_flows_test.go index ea5357b2..06bb38eb 100644 --- a/datamodel/low/v3/oauth_flows_test.go +++ b/datamodel/low/v3/oauth_flows_test.go @@ -4,14 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestOAuthFlows_Build(t *testing.T) { + t.Parallel() yml := `authorizationUrl: https://pb33f.io/auth tokenUrl: https://pb33f.io/token @@ -41,6 +43,7 @@ x-tasty: herbs } func TestOAuthFlow_Build_Implicit(t *testing.T) { + t.Parallel() yml := `implicit: authorizationUrl: https://pb33f.io/auth @@ -62,6 +65,7 @@ x-tasty: herbs` } func TestOAuthFlow_Build_Implicit_Fail(t *testing.T) { + t.Parallel() yml := `implicit: $ref: #bork"` @@ -79,6 +83,7 @@ func TestOAuthFlow_Build_Implicit_Fail(t *testing.T) { } func TestOAuthFlow_Build_Password(t *testing.T) { + t.Parallel() yml := `password: authorizationUrl: https://pb33f.io/auth` @@ -97,6 +102,7 @@ func TestOAuthFlow_Build_Password(t *testing.T) { } func TestOAuthFlow_Build_Password_Fail(t *testing.T) { + t.Parallel() yml := `password: $ref: #bork"` @@ -114,6 +120,7 @@ func TestOAuthFlow_Build_Password_Fail(t *testing.T) { } func TestOAuthFlow_Build_ClientCredentials(t *testing.T) { + t.Parallel() yml := `clientCredentials: authorizationUrl: https://pb33f.io/auth` @@ -132,6 +139,7 @@ func TestOAuthFlow_Build_ClientCredentials(t *testing.T) { } func TestOAuthFlow_Build_ClientCredentials_Fail(t *testing.T) { + t.Parallel() yml := `clientCredentials: $ref: #bork"` @@ -149,6 +157,7 @@ func TestOAuthFlow_Build_ClientCredentials_Fail(t *testing.T) { } func TestOAuthFlow_Build_AuthCode(t *testing.T) { + t.Parallel() yml := `authorizationCode: authorizationUrl: https://pb33f.io/auth` @@ -167,6 +176,7 @@ func TestOAuthFlow_Build_AuthCode(t *testing.T) { } func TestOAuthFlow_Build_AuthCode_Fail(t *testing.T) { + t.Parallel() yml := `authorizationCode: $ref: #bork"` @@ -184,6 +194,7 @@ func TestOAuthFlow_Build_AuthCode_Fail(t *testing.T) { } func TestOAuthFlow_Hash(t *testing.T) { + t.Parallel() yml := `authorizationUrl: https://pb33f.io/auth tokenUrl: https://pb33f.io/token @@ -221,6 +232,7 @@ scopes: } func TestOAuthFlows_Hash(t *testing.T) { + t.Parallel() yml := `implicit: authorizationUrl: https://pb33f.io/auth diff --git a/datamodel/low/v3/operation_test.go b/datamodel/low/v3/operation_test.go index 1d7663ee..64362092 100644 --- a/datamodel/low/v3/operation_test.go +++ b/datamodel/low/v3/operation_test.go @@ -4,15 +4,17 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestOperation_Build(t *testing.T) { + t.Parallel() yml := `tags: - meddy @@ -75,6 +77,7 @@ servers: } func TestOperation_Build_FailDocs(t *testing.T) { + t.Parallel() yml := `externalDocs: $ref: #borked` @@ -92,6 +95,7 @@ func TestOperation_Build_FailDocs(t *testing.T) { } func TestOperation_Build_FailParams(t *testing.T) { + t.Parallel() yml := `parameters: $ref: #borked` @@ -109,6 +113,7 @@ func TestOperation_Build_FailParams(t *testing.T) { } func TestOperation_Build_FailRequestBody(t *testing.T) { + t.Parallel() yml := `requestBody: $ref: #borked` @@ -126,6 +131,7 @@ func TestOperation_Build_FailRequestBody(t *testing.T) { } func TestOperation_Build_FailResponses(t *testing.T) { + t.Parallel() yml := `responses: $ref: #borked` @@ -143,6 +149,7 @@ func TestOperation_Build_FailResponses(t *testing.T) { } func TestOperation_Build_FailCallbacks(t *testing.T) { + t.Parallel() yml := `callbacks: $ref: #borked` @@ -160,6 +167,7 @@ func TestOperation_Build_FailCallbacks(t *testing.T) { } func TestOperation_Build_FailSecurity(t *testing.T) { + t.Parallel() yml := `security: $ref: #borked` @@ -177,6 +185,7 @@ func TestOperation_Build_FailSecurity(t *testing.T) { } func TestOperation_Build_FailServers(t *testing.T) { + t.Parallel() yml := `servers: $ref: #borked` @@ -194,6 +203,7 @@ func TestOperation_Build_FailServers(t *testing.T) { } func TestOperation_Hash_n_Grab(t *testing.T) { + t.Parallel() yml := `tags: - nice @@ -288,6 +298,7 @@ x-mint: sweet` } func TestOperation_EmptySecurity(t *testing.T) { + t.Parallel() yml := ` security: []` diff --git a/datamodel/low/v3/parameter_test.go b/datamodel/low/v3/parameter_test.go index 44c57386..2b58e767 100644 --- a/datamodel/low/v3/parameter_test.go +++ b/datamodel/low/v3/parameter_test.go @@ -4,15 +4,17 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestParameter_Build(t *testing.T) { + t.Parallel() yml := `description: michelle, meddy and maddy required: true deprecated: false @@ -90,6 +92,7 @@ content: } func TestParameter_Build_Success_Examples(t *testing.T) { + t.Parallel() yml := `examples: family: value: @@ -121,6 +124,7 @@ func TestParameter_Build_Success_Examples(t *testing.T) { } func TestParameter_Build_Fail_Examples(t *testing.T) { + t.Parallel() yml := `examples: family: $ref: I AM BORKED` @@ -138,6 +142,7 @@ func TestParameter_Build_Fail_Examples(t *testing.T) { } func TestParameter_Build_Fail_Schema(t *testing.T) { + t.Parallel() yml := `schema: $ref: I will fail.` @@ -154,6 +159,7 @@ func TestParameter_Build_Fail_Schema(t *testing.T) { } func TestParameter_Build_Fail_Content(t *testing.T) { + t.Parallel() yml := `content: ohMyStars: $ref: fail!` @@ -171,6 +177,7 @@ func TestParameter_Build_Fail_Content(t *testing.T) { } func TestParameter_Hash_n_grab(t *testing.T) { + t.Parallel() yml := `description: michelle, meddy and maddy required: true diff --git a/datamodel/low/v3/path_item.go b/datamodel/low/v3/path_item.go index 23683098..d1cac73c 100644 --- a/datamodel/low/v3/path_item.go +++ b/datamodel/low/v3/path_item.go @@ -178,21 +178,21 @@ func (p *PathItem) Build(_, root *yaml.Node, idx *index.SpecIndex) error { // the only thing we now care about is handling operations, filter out anything that's not a verb. switch currentNode.Value { case GetLabel: - break + // valid path label case PostLabel: - break + // valid path label case PutLabel: - break + // valid path label case PatchLabel: - break + // valid path label case DeleteLabel: - break + // valid path label case HeadLabel: - break + // valid path label case OptionsLabel: - break + // valid path label case TraceLabel: - break + // valid path label default: continue // ignore everything else. } diff --git a/datamodel/low/v3/path_item_test.go b/datamodel/low/v3/path_item_test.go index cd5f631c..4ba737df 100644 --- a/datamodel/low/v3/path_item_test.go +++ b/datamodel/low/v3/path_item_test.go @@ -4,15 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestPathItem_Hash(t *testing.T) { - + t.Parallel() yml := `description: a path item summary: it's another path item servers: diff --git a/datamodel/low/v3/paths_test.go b/datamodel/low/v3/paths_test.go index 18a95cc2..9f49a69d 100644 --- a/datamodel/low/v3/paths_test.go +++ b/datamodel/low/v3/paths_test.go @@ -14,7 +14,7 @@ import ( ) func TestPaths_Build(t *testing.T) { - + t.Parallel() yml := `"/some/path": get: description: get method @@ -67,7 +67,7 @@ x-milk: cold` } func TestPaths_Build_Fail(t *testing.T) { - + t.Parallel() yml := `"/some/path": $ref: $bork` @@ -85,7 +85,7 @@ func TestPaths_Build_Fail(t *testing.T) { } func TestPaths_Build_FailRef(t *testing.T) { - + t.Parallel() // this is kinda nuts, and, it's completely illegal, but you never know! yml := `"/some/path": description: this is some path @@ -121,7 +121,7 @@ func TestPaths_Build_FailRef(t *testing.T) { } func TestPaths_Build_FailRefDeadEnd(t *testing.T) { - + t.Parallel() // this is nuts. yml := `"/no/path": get: @@ -145,7 +145,7 @@ func TestPaths_Build_FailRefDeadEnd(t *testing.T) { } func TestPaths_Build_SuccessRef(t *testing.T) { - + t.Parallel() // this is kinda nuts, it's also not illegal, however the mechanics still need to work. yml := `"/some/path": description: this is some path @@ -182,7 +182,7 @@ func TestPaths_Build_SuccessRef(t *testing.T) { } func TestPaths_Build_BadParams(t *testing.T) { - + t.Parallel() yml := `"/some/path": parameters: this: shouldFail` @@ -200,7 +200,7 @@ func TestPaths_Build_BadParams(t *testing.T) { } func TestPaths_Build_BadRef(t *testing.T) { - + t.Parallel() // this is kinda nuts, it's also not illegal, however the mechanics still need to work. yml := `"/some/path": description: this is some path @@ -226,7 +226,7 @@ func TestPaths_Build_BadRef(t *testing.T) { } func TestPathItem_Build_GoodRef(t *testing.T) { - + t.Parallel() // this is kinda nuts, it's also not illegal, however the mechanics still need to work. yml := `"/some/path": description: this is some path @@ -256,7 +256,7 @@ func TestPathItem_Build_GoodRef(t *testing.T) { } func TestPathItem_Build_BadRef(t *testing.T) { - + t.Parallel() // this is kinda nuts, it's also not illegal, however the mechanics still need to work. yml := `"/some/path": description: this is some path @@ -286,7 +286,7 @@ func TestPathItem_Build_BadRef(t *testing.T) { } func TestPathNoOps(t *testing.T) { - + t.Parallel() // this is kinda nuts, it's also not illegal, however the mechanics still need to work. yml := `"/some/path": "/cakes":` @@ -304,7 +304,7 @@ func TestPathNoOps(t *testing.T) { } func TestPathItem_Build_Using_Ref(t *testing.T) { - + t.Parallel() // first we need an index. yml := `paths: '/something/here': @@ -342,7 +342,7 @@ func TestPathItem_Build_Using_Ref(t *testing.T) { } func TestPath_Build_Using_CircularRef(t *testing.T) { - + t.Parallel() // first we need an index. yml := `paths: '/something/here': @@ -378,7 +378,7 @@ func TestPath_Build_Using_CircularRef(t *testing.T) { } func TestPath_Build_Using_CircularRefWithOp(t *testing.T) { - + t.Parallel() // first we need an index. yml := `paths: '/something/here': @@ -415,7 +415,7 @@ func TestPath_Build_Using_CircularRefWithOp(t *testing.T) { } func TestPaths_Build_BrokenOp(t *testing.T) { - + t.Parallel() yml := `"/some/path": post: externalDocs: @@ -434,7 +434,7 @@ func TestPaths_Build_BrokenOp(t *testing.T) { } func TestPaths_Hash(t *testing.T) { - + t.Parallel() yml := `/french/toast: description: toast /french/hen: diff --git a/datamodel/low/v3/request_body_test.go b/datamodel/low/v3/request_body_test.go index 832e5deb..0288b1e7 100644 --- a/datamodel/low/v3/request_body_test.go +++ b/datamodel/low/v3/request_body_test.go @@ -4,15 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestRequestBody_Build(t *testing.T) { - + t.Parallel() yml := `description: a nice request required: true content: @@ -39,7 +40,7 @@ x-requesto: presto` } func TestRequestBody_Fail(t *testing.T) { - + t.Parallel() yml := `content: $ref: #illegal` @@ -56,7 +57,7 @@ func TestRequestBody_Fail(t *testing.T) { } func TestRequestBody_Hash(t *testing.T) { - + t.Parallel() yml := `description: nice toast content: jammy/toast: diff --git a/datamodel/low/v3/response_test.go b/datamodel/low/v3/response_test.go index 0424c75d..bc799a6b 100644 --- a/datamodel/low/v3/response_test.go +++ b/datamodel/low/v3/response_test.go @@ -4,15 +4,17 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" - "testing" ) func TestResponses_Build(t *testing.T) { - + t.Parallel() yml := `"200": description: some response headers: @@ -67,7 +69,7 @@ default: } func TestResponses_NoDefault(t *testing.T) { - + t.Parallel() yml := `"200": description: some response headers: @@ -90,9 +92,10 @@ x-shoes: old` var n Responses err := low.BuildModel(&idxNode, &n) - assert.NoError(t, err) + require.NoError(t, err) err = n.Build(nil, idxNode.Content[0], idx) + require.NoError(t, err) // check hash assert.Equal(t, "54ab66e6cb8bd226940f421c2387e45215b84c946182435dfe2a3036043fa07c", @@ -104,7 +107,7 @@ x-shoes: old` } func TestResponses_Build_FailCodes_WrongType(t *testing.T) { - + t.Parallel() yml := `- "200": $ref: #bork` @@ -122,7 +125,7 @@ func TestResponses_Build_FailCodes_WrongType(t *testing.T) { } func TestResponses_Build_FailCodes(t *testing.T) { - + t.Parallel() yml := `"200": $ref: #bork` @@ -140,7 +143,7 @@ func TestResponses_Build_FailCodes(t *testing.T) { } func TestResponses_Build_FailDefault(t *testing.T) { - + t.Parallel() yml := `- default` var idxNode yaml.Node @@ -157,7 +160,7 @@ func TestResponses_Build_FailDefault(t *testing.T) { } func TestResponses_Build_FailBadHeader(t *testing.T) { - + t.Parallel() yml := `"200": headers: header1: @@ -177,7 +180,7 @@ func TestResponses_Build_FailBadHeader(t *testing.T) { } func TestResponses_Build_FailBadContent(t *testing.T) { - + t.Parallel() yml := `"200": content: flim/flam: @@ -197,7 +200,7 @@ func TestResponses_Build_FailBadContent(t *testing.T) { } func TestResponses_Build_FailBadLinks(t *testing.T) { - + t.Parallel() yml := `"200": links: aLink: @@ -217,7 +220,7 @@ func TestResponses_Build_FailBadLinks(t *testing.T) { } func TestResponses_Build_AllowXPrefixHeader(t *testing.T) { - + t.Parallel() yml := `"200": headers: x-header1: @@ -241,7 +244,7 @@ func TestResponses_Build_AllowXPrefixHeader(t *testing.T) { } func TestResponse_Hash(t *testing.T) { - + t.Parallel() yml := `description: nice toast headers: heady: diff --git a/datamodel/low/v3/security_scheme_test.go b/datamodel/low/v3/security_scheme_test.go index cbdab09b..ea175096 100644 --- a/datamodel/low/v3/security_scheme_test.go +++ b/datamodel/low/v3/security_scheme_test.go @@ -4,15 +4,17 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestSecurityRequirement_Build(t *testing.T) { + t.Parallel() yml := `something: - read:me - write:me` @@ -35,6 +37,7 @@ func TestSecurityRequirement_Build(t *testing.T) { } func TestSecurityScheme_Build(t *testing.T) { + t.Parallel() yml := `type: tea description: cake name: biscuit @@ -75,6 +78,7 @@ x-milk: please` } func TestSecurityScheme_Build_Fail(t *testing.T) { + t.Parallel() yml := `flows: $ref: #bork` diff --git a/datamodel/low/v3/server_test.go b/datamodel/low/v3/server_test.go index e10712d3..371c68ee 100644 --- a/datamodel/low/v3/server_test.go +++ b/datamodel/low/v3/server_test.go @@ -4,15 +4,16 @@ package v3 import ( + "testing" + "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "testing" ) func TestServer_Build(t *testing.T) { - + t.Parallel() yml := `x-coffee: hot url: https://pb33f.io description: high quality software for developers. @@ -51,7 +52,7 @@ variables: } func TestServer_Build_NoVars(t *testing.T) { - + t.Parallel() yml := `url: https://pb33f.io description: high quality software for developers.` diff --git a/datamodel/spec_info_test.go b/datamodel/spec_info_test.go index 702ae147..6490944d 100644 --- a/datamodel/spec_info_test.go +++ b/datamodel/spec_info_test.go @@ -5,7 +5,7 @@ package datamodel import ( "fmt" - "io/ioutil" + "os" "testing" "github.com/pb33f/libopenapi/utils" @@ -24,6 +24,7 @@ const ( ) func TestSpecInfo_GetJSONParsingChannel(t *testing.T) { + t.Parallel() // dumb, but we need to ensure coverage is as high as we can make it. bchan := make(chan bool) @@ -115,39 +116,45 @@ info: version: '0.1.0'` func TestExtractSpecInfo_ValidJSON(t *testing.T) { + t.Parallel() r, e := ExtractSpecInfo([]byte(goodJSON)) assert.Greater(t, len(*r.SpecJSONBytes), 0) assert.Error(t, e) } func TestExtractSpecInfo_InvalidJSON(t *testing.T) { + t.Parallel() _, e := ExtractSpecInfo([]byte(badJSON)) assert.Error(t, e) } func TestExtractSpecInfo_Nothing(t *testing.T) { + t.Parallel() _, e := ExtractSpecInfo([]byte("")) assert.Error(t, e) } func TestExtractSpecInfo_ValidYAML(t *testing.T) { + t.Parallel() r, e := ExtractSpecInfo([]byte(goodYAML)) assert.Greater(t, len(*r.SpecJSONBytes), 0) assert.Error(t, e) } func TestExtractSpecInfo_InvalidYAML(t *testing.T) { + t.Parallel() _, e := ExtractSpecInfo([]byte(badYAML)) assert.Error(t, e) } func TestExtractSpecInfo_InvalidOpenAPIVersion(t *testing.T) { + t.Parallel() _, e := ExtractSpecInfo([]byte(OpenApiOne)) assert.Error(t, e) } func TestExtractSpecInfo_OpenAPI3(t *testing.T) { - + t.Parallel() r, e := ExtractSpecInfo([]byte(OpenApi3Spec)) assert.Nil(t, e) assert.Equal(t, utils.OpenApi3, r.SpecType) @@ -157,7 +164,7 @@ func TestExtractSpecInfo_OpenAPI3(t *testing.T) { } func TestExtractSpecInfo_OpenAPIWat(t *testing.T) { - + t.Parallel() r, e := ExtractSpecInfo([]byte(OpenApiWat)) assert.Nil(t, e) assert.Equal(t, OpenApi3, r.SpecType) @@ -165,7 +172,7 @@ func TestExtractSpecInfo_OpenAPIWat(t *testing.T) { } func TestExtractSpecInfo_OpenAPI31(t *testing.T) { - + t.Parallel() r, e := ExtractSpecInfo([]byte(OpenApi31)) assert.Nil(t, e) assert.Equal(t, OpenApi3, r.SpecType) @@ -174,7 +181,7 @@ func TestExtractSpecInfo_OpenAPI31(t *testing.T) { } func TestExtractSpecInfo_AnyDocument(t *testing.T) { - + t.Parallel() random := `something: yeah nothing: - one @@ -190,7 +197,7 @@ why: } func TestExtractSpecInfo_AnyDocument_JSON(t *testing.T) { - + t.Parallel() random := `{ "something" : "yeah"}` r, e := ExtractSpecInfoWithDocumentCheck([]byte(random), true) @@ -201,7 +208,7 @@ func TestExtractSpecInfo_AnyDocument_JSON(t *testing.T) { } func TestExtractSpecInfo_AnyDocumentFromConfig(t *testing.T) { - + t.Parallel() random := `something: yeah nothing: - one @@ -219,14 +226,14 @@ why: } func TestExtractSpecInfo_OpenAPIFalse(t *testing.T) { - + t.Parallel() spec, e := ExtractSpecInfo([]byte(OpenApiFalse)) assert.NoError(t, e) assert.Equal(t, "false", spec.Version) } func TestExtractSpecInfo_OpenAPI2(t *testing.T) { - + t.Parallel() r, e := ExtractSpecInfo([]byte(OpenApi2Spec)) assert.Nil(t, e) assert.Equal(t, OpenApi2, r.SpecType) @@ -236,7 +243,7 @@ func TestExtractSpecInfo_OpenAPI2(t *testing.T) { } func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) { - + t.Parallel() _, e := ExtractSpecInfo([]byte(OpenApi2SpecOdd)) assert.NotNil(t, e) assert.Equal(t, @@ -244,7 +251,7 @@ func TestExtractSpecInfo_OpenAPI2_OddVersion(t *testing.T) { } func TestExtractSpecInfo_AsyncAPI(t *testing.T) { - + t.Parallel() r, e := ExtractSpecInfo([]byte(AsyncAPISpec)) assert.Nil(t, e) assert.Equal(t, AsyncApi, r.SpecType) @@ -253,7 +260,7 @@ func TestExtractSpecInfo_AsyncAPI(t *testing.T) { } func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) { - + t.Parallel() _, e := ExtractSpecInfo([]byte(AsyncAPISpecOdd)) assert.NotNil(t, e) assert.Equal(t, @@ -261,7 +268,7 @@ func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) { } func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) { - + t.Parallel() yml := `openapi: should: fail` @@ -270,7 +277,7 @@ func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) { } func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) { - + t.Parallel() yml := `swagger: should: fail` @@ -279,7 +286,7 @@ func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) { } func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) { - + t.Parallel() yml := `asyncapi: should: fail` @@ -290,7 +297,7 @@ func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) { func ExampleExtractSpecInfo() { // load bytes from openapi spec file. - bytes, _ := ioutil.ReadFile("../test_specs/petstorev3.json") + bytes, _ := os.ReadFile("../test_specs/petstorev3.json") // create a new *SpecInfo instance from loaded bytes specInfo, err := ExtractSpecInfo(bytes) diff --git a/document.go b/document.go index 66e8036c..2c35fc8b 100644 --- a/document.go +++ b/document.go @@ -14,17 +14,17 @@ package libopenapi import ( - "errors" "fmt" + "net/http" "github.com/pb33f/libopenapi/index" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/pb33f/libopenapi/datamodel" v2high "github.com/pb33f/libopenapi/datamodel/high/v2" v3high "github.com/pb33f/libopenapi/datamodel/high/v3" v2low "github.com/pb33f/libopenapi/datamodel/low/v2" v3low "github.com/pb33f/libopenapi/datamodel/low/v3" - "github.com/pb33f/libopenapi/resolver" "github.com/pb33f/libopenapi/utils" what_changed "github.com/pb33f/libopenapi/what-changed" "github.com/pb33f/libopenapi/what-changed/model" @@ -42,19 +42,19 @@ type Document interface { // SetConfiguration will set the configuration for the document. This allows for finer grained control over // allowing remote or local references, as well as a BaseURL to allow for relative file references. - SetConfiguration(configuration *datamodel.DocumentConfiguration) + SetConfiguration(configuration *Configuration) // BuildV2Model will build out a Swagger (version 2) model from the specification used to create the document // If there are any issues, then no model will be returned, instead a slice of errors will explain all the // problems that occurred. This method will only support version 2 specifications and will throw an error for // any other types. - BuildV2Model() (*DocumentModel[v2high.Swagger], []error) + BuildV2Model() (*DocumentModel[v2high.Swagger], error) // BuildV3Model will build out an OpenAPI (version 3+) model from the specification used to create the document // If there are any issues, then no model will be returned, instead a slice of errors will explain all the // problems that occurred. This method will only support version 3 specifications and will throw an error for // any other types. - BuildV3Model() (*DocumentModel[v3high.Document], []error) + BuildV3Model() (*DocumentModel[v3high.Document], error) // RenderAndReload will render the high level model as it currently exists (including any mutations, additions // and removals to and from any object in the tree). It will then reload the low level model with the new bytes @@ -70,7 +70,7 @@ type Document interface { // **IMPORTANT** This method only supports OpenAPI Documents. The Swagger model will not support mutations correctly // and will not update when called. This choice has been made because we don't want to continue supporting Swagger, // it's too old, so it should be motivation to upgrade to OpenAPI 3. - RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], []error) + RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], error) // Serialize will re-render a Document back into a []byte slice. If any modifications have been made to the // underlying data model using low level APIs, then those changes will be reflected in the serialized output. @@ -85,10 +85,14 @@ type Document interface { type document struct { version string + config *Configuration info *datamodel.SpecInfo - config *datamodel.DocumentConfiguration highOpenAPI3Model *DocumentModel[v3high.Document] highSwaggerModel *DocumentModel[v2high.Swagger] + + // skip optional errors like circular reference errors. + // if configured + errorFilter []func(error) bool } // DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is @@ -111,36 +115,76 @@ type DocumentModel[T v2high.Swagger | v3high.Document] struct { // If this isn't the behavior you want, or that you feel this is a potential security risk, // then you can use the NewDocumentWithConfiguration() function instead, which allows you to set a configuration that // will allow you to control if file or remote references are allowed. -func NewDocument(specByteArray []byte) (Document, error) { - return NewDocumentWithTypeCheck(specByteArray, false) +func NewDocument(specByteArray []byte, options ...ConfigurationOption) (Document, error) { + // sane defaults directly visible to the user. + config := &Configuration{ + RemoteURLHandler: http.Get, + AllowFileReferences: false, + AllowRemoteReferences: false, + AvoidIndexBuild: false, + BypassDocumentCheck: false, + ForbidCircularReferenceResolving: false, + } + + var err error + for _, option := range options { + err = option(config) + if err != nil { + return nil, err + } + } + + return NewDocumentWithConfiguration(specByteArray, config) } -func NewDocumentWithTypeCheck(specByteArray []byte, bypassCheck bool) (Document, error) { - info, err := datamodel.ExtractSpecInfoWithDocumentCheck(specByteArray, bypassCheck) +// NewDocumentWithConfiguration is the same as NewDocument, except it's a convenience function that calls NewDocument +// under the hood and then calls SetConfiguration() on the returned Document. +func NewDocumentWithConfiguration(specByteArray []byte, config *Configuration) (Document, error) { + + if config == nil { + // use default config provided by our central constructor function + return NewDocument(specByteArray) + } + + info, err := datamodel.ExtractSpecInfoWithDocumentCheck(specByteArray, config.BypassDocumentCheck) if err != nil { return nil, err } - d := new(document) - d.version = info.Version - d.info = info + + d := &document{ + version: info.Version, + info: info, + highOpenAPI3Model: nil, + highSwaggerModel: nil, + errorFilter: nil, // set in SetConfiguration + } + + d.SetConfiguration(config) + return d, nil } -// NewDocumentWithConfiguration is the same as NewDocument, except it's a convenience function that calls NewDocument -// under the hood and then calls SetConfiguration() on the returned Document. -func NewDocumentWithConfiguration(specByteArray []byte, configuration *datamodel.DocumentConfiguration) (Document, error) { - var d Document - var err error - if configuration != nil && configuration.BypassDocumentCheck { - d, err = NewDocumentWithTypeCheck(specByteArray, true) - } else { - d, err = NewDocument(specByteArray) +func (d *document) SetConfiguration(config *Configuration) { + if config == nil { + // programming error, should panic + panic("configuration cannot be nil") } - if d != nil { - d.SetConfiguration(configuration) + if config.RemoteURLHandler == nil { + // set default handler if + config.RemoteURLHandler = http.Get } - return d, err + + d.config = config + + d.errorFilter = []func(error) bool{ + // more filters can be added here if needed + circularReferenceErrorFilter(config.ForbidCircularReferenceResolving), + } +} + +func NewDocumentWithTypeCheck(specByteArray []byte, bypassCheck bool) (Document, error) { + return NewDocument(specByteArray, WithBypassDocumentCheck(bypassCheck)) } func (d *document) GetVersion() string { @@ -151,10 +195,6 @@ func (d *document) GetSpecInfo() *datamodel.SpecInfo { return d.info } -func (d *document) SetConfiguration(configuration *datamodel.DocumentConfiguration) { - d.config = configuration -} - func (d *document) Serialize() ([]byte, error) { if d.info == nil { return nil, fmt.Errorf("unable to serialize, document has not yet been initialized") @@ -167,9 +207,9 @@ func (d *document) Serialize() ([]byte, error) { } } -func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], []error) { +func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Document], error) { if d.highSwaggerModel != nil && d.highOpenAPI3Model == nil { - return nil, nil, nil, []error{errors.New("this method only supports OpenAPI 3 documents, not Swagger")} + return nil, nil, nil, ErrOpenAPI3Operation } var newBytes []byte @@ -192,101 +232,70 @@ func (d *document) RenderAndReload() ([]byte, Document, *DocumentModel[v3high.Do newDoc, err := NewDocumentWithConfiguration(newBytes, d.config) if err != nil { - return newBytes, newDoc, nil, []error{err} + return newBytes, newDoc, nil, err } // build the model. - model, errs := newDoc.BuildV3Model() - if errs != nil { - return newBytes, newDoc, model, errs + model, err := newDoc.BuildV3Model() + if err != nil { + return newBytes, newDoc, model, err } // this document is now dead, long live the new document! return newBytes, newDoc, model, nil } -func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], []error) { +func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { if d.highSwaggerModel != nil { return d.highSwaggerModel, nil } - var errors []error if d.info == nil { - errors = append(errors, fmt.Errorf("unable to build swagger document, no specification has been loaded")) - return nil, errors + return nil, ErrNoConfiguration } if d.info.SpecFormat != datamodel.OAS2 { - errors = append(errors, fmt.Errorf("unable to build swagger document, "+ - "supplied spec is a different version (%v). Try 'BuildV3Model()'", d.info.SpecFormat)) - return nil, errors + return nil, fmt.Errorf("unable to build swagger document, "+ + "supplied spec is a different version (%v). Try 'BuildV3Model()'", d.info.SpecFormat) } var lowDoc *v2low.Swagger - if d.config == nil { - d.config = &datamodel.DocumentConfiguration{ - AllowFileReferences: false, - AllowRemoteReferences: false, - } - } - - lowDoc, errors = v2low.CreateDocumentFromConfig(d.info, d.config) - // Do not short-circuit on circular reference errors, so the client - // has the option of ignoring them. - for _, err := range errors { - if refErr, ok := err.(*resolver.ResolvingError); ok { - if refErr.CircularReference == nil { - return nil, errors - } - } else { - return nil, errors - } + lowDoc, err := v2low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) + err = errorutils.Filtered(err, d.errorFilter...) + if err != nil { + return nil, err } highDoc := v2high.NewSwaggerDocument(lowDoc) d.highSwaggerModel = &DocumentModel[v2high.Swagger]{ Model: *highDoc, Index: lowDoc.Index, } - return d.highSwaggerModel, errors + return d.highSwaggerModel, err } -func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) { +func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { if d.highOpenAPI3Model != nil { return d.highOpenAPI3Model, nil } - var errors []error + var err error if d.info == nil { - errors = append(errors, fmt.Errorf("unable to build document, no specification has been loaded")) - return nil, errors + return nil, ErrNoConfiguration } + if d.info.SpecFormat != datamodel.OAS3 { - errors = append(errors, fmt.Errorf("unable to build openapi document, "+ - "supplied spec is a different version (%v). Try 'BuildV2Model()'", d.info.SpecFormat)) - return nil, errors + return nil, fmt.Errorf("unable to build openapi document: "+ + "supplied spec is a different version (%v). Try 'BuildV2Model()'", d.info.SpecFormat) } var lowDoc *v3low.Document - if d.config == nil { - d.config = &datamodel.DocumentConfiguration{ - AllowFileReferences: false, - AllowRemoteReferences: false, - } + lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) + err = errorutils.Filtered(err, d.errorFilter...) + if err != nil { + return nil, err } - lowDoc, errors = v3low.CreateDocumentFromConfig(d.info, d.config) - // Do not short-circuit on circular reference errors, so the client - // has the option of ignoring them. - for _, err := range errors { - if refErr, ok := err.(*resolver.ResolvingError); ok { - if refErr.CircularReference == nil { - return nil, errors - } - } else { - return nil, errors - } - } highDoc := v3high.NewDocument(lowDoc) d.highOpenAPI3Model = &DocumentModel[v3high.Document]{ Model: *highDoc, Index: lowDoc.Index, } - return d.highOpenAPI3Model, errors + return d.highOpenAPI3Model, err } // CompareDocuments will accept a left and right Document implementing struct, build a model for the correct @@ -295,37 +304,31 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], []error) { // If there are any errors when building the models, those errors are returned with a nil pointer for the // model.DocumentChanges. If there are any changes found however between either Document, then a pointer to // model.DocumentChanges is returned containing every single change, broken down, model by model. -func CompareDocuments(original, updated Document) (*model.DocumentChanges, []error) { - var errors []error +func CompareDocuments(original, updated Document) (*model.DocumentChanges, error) { + var errs []error if original.GetSpecInfo().SpecType == utils.OpenApi3 && updated.GetSpecInfo().SpecType == utils.OpenApi3 { - v3ModelLeft, errs := original.BuildV3Model() - if len(errs) > 0 { - errors = errs - } - v3ModelRight, errs := updated.BuildV3Model() - if len(errs) > 0 { - errors = append(errors, errs...) - } + v3ModelLeft, err := original.BuildV3Model() + errs = append(errs, err) + + v3ModelRight, err := updated.BuildV3Model() + errs = append(errs, err) + if v3ModelLeft != nil && v3ModelRight != nil { - return what_changed.CompareOpenAPIDocuments(v3ModelLeft.Model.GoLow(), v3ModelRight.Model.GoLow()), errors + return what_changed.CompareOpenAPIDocuments(v3ModelLeft.Model.GoLow(), v3ModelRight.Model.GoLow()), errorutils.Join(errs...) } else { - return nil, errors + return nil, errorutils.Join(errs...) } } if original.GetSpecInfo().SpecType == utils.OpenApi2 && updated.GetSpecInfo().SpecType == utils.OpenApi2 { - v2ModelLeft, errs := original.BuildV2Model() - if len(errs) > 0 { - errors = errs - } - v2ModelRight, errs := updated.BuildV2Model() - if len(errs) > 0 { - errors = append(errors, errs...) - } + v2ModelLeft, err := original.BuildV2Model() + errs = append(errs, err) + v2ModelRight, err := updated.BuildV2Model() + errs = append(errs, err) if v2ModelLeft != nil && v2ModelRight != nil { - return what_changed.CompareSwaggerDocuments(v2ModelLeft.Model.GoLow(), v2ModelRight.Model.GoLow()), errors + return what_changed.CompareSwaggerDocuments(v2ModelLeft.Model.GoLow(), v2ModelRight.Model.GoLow()), errorutils.Join(errs...) } else { - return nil, errors + return nil, errorutils.Join(errs...) } } - return nil, []error{fmt.Errorf("unable to compare documents, one or both documents are not of the same version")} + return nil, ErrVersionMismatch } diff --git a/document_config.go b/document_config.go new file mode 100644 index 00000000..101f4b4d --- /dev/null +++ b/document_config.go @@ -0,0 +1,119 @@ +package libopenapi + +import ( + "net/http" + "net/url" + + "github.com/pb33f/libopenapi/datamodel" +) + +// Configuration is used to configure the document creation process. It was added in v0.6.0 to allow +// for more fine-grained control over controls and new features. +// +// The default configuration will set AllowFileReferences to false and AllowRemoteReferences to false, which means +// any non-local (local being the specification, not the file system) references, will be ignored. +type Configuration struct { + // The BaseURL will be the root from which relative references will be resolved from if they can't be found locally. + // Schema must be set to "http/https". + BaseURL *url.URL + + // RemoteURLHandler is a function that will be used to retrieve remote documents. If not set, the default + // remote document getter will be used. + // Resolves [#132]: https://github.com/pb33f/libopenapi/issues/132 + RemoteURLHandler func(url string) (*http.Response, error) + + // If resolving locally, the BasePath will be the root from which relative references will be resolved from. + // It's usually the location of the root specification. + BasePath string // set the Base Path for resolving relative references if the spec is exploded. + + // AllowFileReferences will allow the index to locate relative file references. This is disabled by default. + AllowFileReferences bool + + // AllowRemoteReferences will allow the index to lookup remote references. This is disabled by default. + AllowRemoteReferences bool + + // AvoidIndexBuild will avoid building the index. This is disabled by default, only use if you are sure you don't need it. + // This is useful for developers building out models that should be indexed later on. + AvoidIndexBuild bool + + // BypassDocumentCheck will bypass the document check. This is disabled by default. This will allow any document to + // passed in and used. Only enable this when parsing non openapi documents. + BypassDocumentCheck bool + + // ForbidCircularReferenceResolving will forbid circular references to be resolved. This is disabled by default. + // Will return an error in case of circular references if set to true. + ForbidCircularReferenceResolving bool +} + +func (c *Configuration) toModelConfig() *datamodel.DocumentConfiguration { + return &datamodel.DocumentConfiguration{ + BaseURL: c.BaseURL, + RemoteURLHandler: c.RemoteURLHandler, + BasePath: c.BasePath, + AllowFileReferences: c.AllowFileReferences, + AllowRemoteReferences: c.AllowRemoteReferences, + AvoidIndexBuild: c.AvoidIndexBuild, + BypassDocumentCheck: c.BypassDocumentCheck, + } +} + +type ConfigurationOption func(*Configuration) error + +func WithBaseURL(baseUrl *url.URL) ConfigurationOption { + return func(o *Configuration) error { + o.BaseURL = baseUrl + return nil + } +} + +func WithRemoteURLHandler(handler func(url string) (*http.Response, error)) ConfigurationOption { + return func(o *Configuration) error { + o.RemoteURLHandler = handler + return nil + + } +} + +func WithBasePath(basePath string) ConfigurationOption { + return func(o *Configuration) error { + o.BasePath = basePath + return nil + } +} + +func WithAllowFileReferences(allow bool) ConfigurationOption { + return func(o *Configuration) error { + o.AllowFileReferences = allow + return nil + } +} + +func WithAllowRemoteReferences(allow bool) ConfigurationOption { + return func(o *Configuration) error { + o.AllowRemoteReferences = allow + return nil + } +} + +func WithAvoidIndexBuild(avoid bool) ConfigurationOption { + return func(o *Configuration) error { + o.AvoidIndexBuild = avoid + return nil + } +} + +func WithBypassDocumentCheck(bypass bool) ConfigurationOption { + return func(o *Configuration) error { + o.BypassDocumentCheck = bypass + return nil + } +} + +// WithForbidCircularReferenceResolving returns an error for every detected circular reference if set to true. +// If set to false, circular references will be resolved (default behavior). +func WithForbidCircularReferenceResolving(forbidden bool) ConfigurationOption { + return func(o *Configuration) error { + o.ForbidCircularReferenceResolving = forbidden + return nil + } +} diff --git a/document_examples_test.go b/document_examples_test.go index 04131ff7..1f24a6bf 100644 --- a/document_examples_test.go +++ b/document_examples_test.go @@ -4,8 +4,8 @@ package libopenapi import ( + "errors" "fmt" - "github.com/pb33f/libopenapi/datamodel" "net/url" "os" "strings" @@ -15,6 +15,7 @@ import ( v3high "github.com/pb33f/libopenapi/datamodel/high/v3" low "github.com/pb33f/libopenapi/datamodel/low/base" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/resolver" "github.com/pb33f/libopenapi/utils" "github.com/stretchr/testify/assert" @@ -36,14 +37,11 @@ func ExampleNewDocument_fromOpenAPI3Document() { } // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := document.BuildV3Model() + v3Model, err := 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))) + if err != nil { + panic(fmt.Errorf("cannot create v3 model from document: %w", err)) } // get a count of the number of paths and schemas. @@ -64,7 +62,7 @@ func ExampleNewDocument_fromWithDocumentConfigurationFailure() { digitalOcean, _ := os.ReadFile("test_specs/digitalocean.yaml") // create a DocumentConfiguration that prevents loading file and remote references - config := datamodel.DocumentConfiguration{ + config := Configuration{ AllowFileReferences: false, AllowRemoteReferences: false, } @@ -78,10 +76,10 @@ func ExampleNewDocument_fromWithDocumentConfigurationFailure() { } // only errors will be thrown, so just capture them and print the number of errors. - _, errors := doc.BuildV3Model() + _, err = doc.BuildV3Model() // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { + if err != nil { fmt.Println("Error building Digital Ocean spec errors reported") } // Output: Error building Digital Ocean spec errors reported @@ -100,7 +98,7 @@ func ExampleNewDocument_fromWithDocumentConfigurationSuccess() { // create a DocumentConfiguration that allows loading file and remote references, and sets the baseURL // to somewhere that can resolve the relative references. - config := datamodel.DocumentConfiguration{ + config := Configuration{ AllowFileReferences: true, AllowRemoteReferences: true, BaseURL: baseURL, @@ -115,10 +113,10 @@ func ExampleNewDocument_fromWithDocumentConfigurationSuccess() { } // only errors will be thrown, so just capture them and print the number of errors. - _, errors := doc.BuildV3Model() + _, err = doc.BuildV3Model() // if anything went wrong when building the v3 model, a slice of errors will be returned - if len(errors) > 0 { + if err != nil { fmt.Println("Error building Digital Ocean spec errors reported") } else { fmt.Println("Digital Ocean spec built successfully") @@ -142,14 +140,11 @@ func ExampleNewDocument_fromSwaggerDocument() { } // because we know this is a v2 spec, we can build a ready to go model from it. - v2Model, errors := document.BuildV2Model() + v2Model, err := document.BuildV2Model() // 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))) + if err != nil { + panic(fmt.Errorf("cannot create v3 model from document: %w", err)) } // get a count of the number of paths and schemas. @@ -175,36 +170,31 @@ func ExampleNewDocument_fromUnknownVersion() { } var paths, schemas int - var errors []error + var errs []error // We don't know which type of document this is, so we can use the spec info to inform us if document.GetSpecInfo().SpecType == utils.OpenApi3 { - v3Model, errs := document.BuildV3Model() - if len(errs) > 0 { - errors = errs - } - if len(errors) <= 0 { + v3Model, err := document.BuildV3Model() + if err != nil { + errs = append(errs, err) + } else { paths = len(v3Model.Model.Paths.PathItems) schemas = len(v3Model.Model.Components.Schemas) } } if document.GetSpecInfo().SpecType == utils.OpenApi2 { - v2Model, errs := document.BuildV2Model() - if len(errs) > 0 { - errors = errs - } - if len(errors) <= 0 { + v2Model, err := document.BuildV2Model() + if err != nil { + errs = append(errs, err) + } else { paths = len(v2Model.Model.Paths.PathItems) schemas = len(v2Model.Model.Definitions.Definitions) } } // if anything went wrong when building the model, report errors. - 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))) + if len(errs) > 0 { + panic(fmt.Errorf("cannot create v3 model from document: %v", errs)) } // print the number of paths and schemas in the document @@ -236,14 +226,11 @@ info: } // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := document.BuildV3Model() + v3Model, err := 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))) + if err != nil { + panic(fmt.Errorf("cannot create v3 model from document: %w", err)) } // mutate the title, to do this we currently need to drop down to the low-level API. @@ -305,14 +292,11 @@ func ExampleCompareDocuments_openAPI() { } // Compare documents for all changes made - documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) + documentChanges, err := CompareDocuments(originalDoc, updatedDoc) // If anything went wrong when building models for documents. - if len(errs) > 0 { - for i := range errs { - fmt.Printf("error: %e\n", errs[i]) - } - panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) + if err != nil { + panic(fmt.Errorf("cannot compare documents: %w", err)) } // Extract SchemaChanges from components changes. @@ -352,14 +336,11 @@ func ExampleCompareDocuments_swagger() { } // Compare documents for all changes made - documentChanges, errs := CompareDocuments(originalDoc, updatedDoc) + documentChanges, err := CompareDocuments(originalDoc, updatedDoc) // If anything went wrong when building models for documents. - if len(errs) > 0 { - for i := range errs { - fmt.Printf("error: %e\n", errs[i]) - } - panic(fmt.Sprintf("cannot compare documents: %d errors reported", len(errs))) + if err != nil { + panic(fmt.Errorf("cannot compare documents: %w", err)) } // Extract SchemaChanges from components changes. @@ -420,20 +401,26 @@ components: - testThing ` // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + doc, err := NewDocument([]byte(spec), WithForbidCircularReferenceResolving(true)) // if anything went wrong, an error is thrown if err != nil { panic(fmt.Sprintf("cannot create new document: %e", err)) } - _, errs := doc.BuildV3Model() + _, err = doc.BuildV3Model() // extract resolving error - resolvingError := errs[0] - // resolving error is a pointer to *resolver.ResolvingError // which provides access to rich details about the error. - circularReference := resolvingError.(*resolver.ResolvingError).CircularReference + var ( + resolvingError *resolver.ResolvingError + circularReference *index.CircularReferenceResult + ) + if errors.As(err, &resolvingError) { + circularReference = resolvingError.CircularReference + } else { + panic("cannot extract resolving error") + } // capture the journey with all details var buf strings.Builder @@ -478,9 +465,9 @@ components: if err != nil { panic(fmt.Sprintf("cannot create new document: %e", err)) } - _, errs := doc.BuildV3Model() + _, err = doc.BuildV3Model() - assert.Len(t, errs, 0) + assert.NoError(t, err) } // If you're using complex types with OpenAPI Extensions, it's simple to unpack extensions into complex @@ -618,14 +605,11 @@ func ExampleNewDocument_modifyAndReRender() { } // because we know this is a v3 spec, we can build a ready to go model from it. - v3Model, errors := doc.BuildV3Model() + v3Model, err := doc.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))) + if err != nil { + panic(fmt.Errorf("cannot create v3 model from document: %w", err)) } // create a new path item and operation. @@ -647,11 +631,11 @@ func ExampleNewDocument_modifyAndReRender() { v3Model.Model.Paths.PathItems["/new/path"] = newPath // render the document back to bytes and reload the model. - rawBytes, _, newModel, errs := doc.RenderAndReload() + rawBytes, _, newModel, err := doc.RenderAndReload() // if anything went wrong when re-rendering the v3 model, a slice of errors will be returned - if len(errors) > 0 { - panic(fmt.Sprintf("cannot re-render document: %d errors reported", len(errs))) + if err != nil { + panic(fmt.Sprintf("cannot re-render document: %v", err)) } // capture new number of paths after re-rendering diff --git a/document_test.go b/document_test.go index 0998e059..662f2733 100644 --- a/document_test.go +++ b/document_test.go @@ -3,31 +3,32 @@ package libopenapi import ( + "errors" "fmt" - "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/datamodel/high/base" - "github.com/pb33f/libopenapi/what-changed/model" - "github.com/stretchr/testify/assert" - "io/ioutil" + "net/http" "os" "strings" "testing" + + "github.com/pb33f/libopenapi/datamodel/high/base" + "github.com/pb33f/libopenapi/internal/errorutils" + "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()) + require.NoError(t, err) + require.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() - + require.Nil(t, docErr) + require.Len(t, errorutils.ShallowUnwrap(docErr), 0) + require.NotNil(t, v2Doc) + require.NotNil(t, doc.GetSpecInfo()) } func TestLoadDocument_Simple_V2_Error(t *testing.T) { @@ -37,10 +38,26 @@ func TestLoadDocument_Simple_V2_Error(t *testing.T) { assert.NoError(t, err) v2Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 1) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 1) assert.Nil(t, v2Doc) } +func TestLoadDocument_Simple_WithErrorOption_V2(t *testing.T) { + yml := `swagger: 2.0.1` + _, err := NewDocument([]byte(yml), + WithBaseURL(nil), + WithBasePath(""), + WithRemoteURLHandler(http.Get), + WithAllowFileReferences(true), + WithAllowRemoteReferences(true), + WithAvoidIndexBuild(false), + WithBypassDocumentCheck(false), + WithForbidCircularReferenceResolving(false), + func(c *Configuration) error { return errors.New("test error") }, + ) + require.Error(t, err) +} + func TestLoadDocument_Simple_V2_Error_BadSpec(t *testing.T) { yml := `swagger: 2.0 @@ -48,11 +65,11 @@ definitions: thing: $ref: bork` doc, err := NewDocument([]byte(yml)) - assert.NoError(t, err) + require.NoError(t, err) v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, docErr, 2) - assert.Nil(t, v2Doc) + require.Len(t, errorutils.ShallowUnwrap(docErr), 2) + require.Nil(t, v2Doc) } func TestLoadDocument_Simple_V3_Error(t *testing.T) { @@ -62,7 +79,7 @@ func TestLoadDocument_Simple_V3_Error(t *testing.T) { assert.NoError(t, err) v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, docErr, 1) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 1) assert.Nil(t, v2Doc) } @@ -70,14 +87,14 @@ func TestLoadDocument_Error_V2NoSpec(t *testing.T) { doc := new(document) // not how this should be instantiated. _, err := doc.BuildV2Model() - assert.Len(t, err, 1) + assert.Len(t, errorutils.ShallowUnwrap(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) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestLoadDocument_Empty(t *testing.T) { @@ -94,7 +111,7 @@ func TestLoadDocument_Simple_V3(t *testing.T) { assert.Equal(t, "3.0.1", doc.GetVersion()) v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 0) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 0) assert.NotNil(t, v3Doc) } @@ -108,7 +125,7 @@ paths: assert.NoError(t, err) v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 2) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 2) assert.Nil(t, v3Doc) } @@ -154,32 +171,38 @@ info: func TestDocument_RenderAndReload_ChangeCheck_Burgershop(t *testing.T) { - bs, _ := os.ReadFile("test_specs/burgershop.openapi.yaml") - doc, _ := NewDocument(bs) - doc.BuildV3Model() + bs, err := os.ReadFile("test_specs/burgershop.openapi.yaml") + require.NoError(t, err) + doc, err := NewDocument(bs) + require.NoError(t, err) + _, err = doc.BuildV3Model() + require.NoError(t, err) - rend, newDoc, _, _ := doc.RenderAndReload() + rend, newDoc, _, err := doc.RenderAndReload() + require.NoError(t, err) + assert.NotNil(t, rend) // compare documents - compReport, errs := CompareDocuments(doc, newDoc) - - // should noth be nil. - assert.Nil(t, errs) - assert.NotNil(t, rend) + compReport, err := CompareDocuments(doc, newDoc) + // should both be nil. + require.NoError(t, err) assert.Nil(t, compReport) - } func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { bs, _ := os.ReadFile("test_specs/stripe.yaml") - doc, _ := NewDocument(bs) - doc.BuildV3Model() + doc, err := NewDocument(bs) + require.NoError(t, err) + _, err = doc.BuildV3Model() + require.NoError(t, err) - _, newDoc, _, _ := doc.RenderAndReload() + _, newDoc, _, err := doc.RenderAndReload() + require.NoError(t, err) // compare documents - compReport, errs := CompareDocuments(doc, newDoc) + compReport, err := CompareDocuments(doc, newDoc) + require.NoError(t, err) // get flat list of changes. flatChanges := compReport.GetAllChanges() @@ -192,7 +215,6 @@ func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { } } - assert.Nil(t, errs) tc := compReport.TotalChanges() bc := compReport.TotalBreakingChanges() assert.Equal(t, 0, bc) @@ -200,25 +222,25 @@ func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { // 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() + _, err := doc.BuildV3Model() + require.NoError(t, err) dat, newDoc, _, _ := doc.RenderAndReload() assert.NotNil(t, dat) // compare documents - compReport, errs := CompareDocuments(doc, newDoc) + compReport, err := CompareDocuments(doc, newDoc) // get flat list of changes. flatChanges := compReport.GetAllChanges() - assert.Nil(t, errs) + assert.Nil(t, err) tc := compReport.TotalChanges() assert.Equal(t, 21, tc) @@ -230,7 +252,7 @@ func TestDocument_RenderAndReload_ChangeCheck_Asana(t *testing.T) { func TestDocument_RenderAndReload(t *testing.T) { // load an OpenAPI 3 specification from bytes - petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json") + petstore, _ := os.ReadFile("test_specs/petstorev3.json") // create a new document from specification bytes doc, err := NewDocument(petstore) @@ -279,24 +301,26 @@ func TestDocument_RenderAndReload(t *testing.T) { } func TestDocument_RenderAndReload_Swagger(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/petstorev2.json") + 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()) - + assert.Error(t, e) + assert.ErrorIs(t, e, ErrOpenAPI3Operation) } func TestDocument_BuildModelPreBuild(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/petstorev3.json") + 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) + _, err := doc.BuildV3Model() + require.NoError(t, err) + _, err = doc.BuildV3Model() + require.NoError(t, err) + _, _, _, err = doc.RenderAndReload() + assert.Nil(t, err) + assert.Len(t, errorutils.ShallowUnwrap(err), 0) } func TestDocument_AnyDoc(t *testing.T) { @@ -307,26 +331,68 @@ func TestDocument_AnyDoc(t *testing.T) { func TestDocument_AnyDocWithConfig(t *testing.T) { anything := []byte(`{"chickens": "3.0.0", "burgers": {"title": "hello"}}`) - _, e := NewDocumentWithConfiguration(anything, &datamodel.DocumentConfiguration{ + _, e := NewDocumentWithConfiguration(anything, &Configuration{ BypassDocumentCheck: true, }) assert.NoError(t, e) } +func TestDocument_DocWithoutConfig_V3(t *testing.T) { + + var d = `openapi: "3.1"` + + doc, e := NewDocumentWithConfiguration([]byte(d), nil) + require.NoError(t, e) + + _, e = doc.BuildV3Model() + require.NoError(t, e) + +} + +func TestDocument_DocWithoutConfig_V2(t *testing.T) { + yml := `swagger: 2.0.1` + + doc, e := NewDocumentWithConfiguration([]byte(yml), nil) + require.NoError(t, e) + + _, e = doc.BuildV2Model() + require.NoError(t, e) +} + +func TestSetNilConfig(t *testing.T) { + defer func() { + i := recover() + require.NotNil(t, i) + }() + + var d = `openapi: "3.1"` + + doc, err := NewDocumentWithConfiguration([]byte(d), nil) + require.NoError(t, err) + // panics + doc.SetConfiguration(nil) +} + func TestDocument_BuildModelCircular(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/circular-tests.yaml") - doc, _ := NewDocument(petstore) - m, e := doc.BuildV3Model() - assert.NotNil(t, m) - assert.Len(t, e, 3) + petstore, _ := os.ReadFile("test_specs/circular-tests.yaml") + doc, err := NewDocument(petstore, WithForbidCircularReferenceResolving(true)) + require.NoError(t, err) + m, err := doc.BuildV3Model() + require.Error(t, err) + + // top level library does not return broken objects + // with an error, only one or the other + assert.Nil(t, m) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) } func TestDocument_BuildModelBad(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml") - doc, _ := NewDocument(petstore) - m, e := doc.BuildV3Model() + petstore, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml") + doc, err := NewDocument(petstore) + require.NoError(t, err) + m, err := doc.BuildV3Model() assert.Nil(t, m) - assert.Len(t, e, 9) + assert.Len(t, errorutils.ShallowUnwrap(err), 9) } func TestDocument_Serialize_JSON_Modified(t *testing.T) { @@ -338,9 +404,11 @@ func TestDocument_Serialize_JSON_Modified(t *testing.T) { } ` jsonModified := `{"info":{"title":"The magic API - but now, altered!"},"openapi":"3.0"}` - doc, _ := NewDocument([]byte(json)) + doc, err := NewDocument([]byte(json)) + require.NoError(t, err) - v3Doc, _ := doc.BuildV3Model() + v3Doc, err := doc.BuildV3Model() + require.NoError(t, err) // eventually this will be encapsulated up high. // mutation does not replace low model, eventually pointers will be used. @@ -368,14 +436,10 @@ paths: - $ref: '#/components/parameters/Param1'` doc, err := NewDocument([]byte(data)) - if err != nil { - panic(err) - } + require.NoError(t, err) - result, errs := doc.BuildV3Model() - if len(errs) > 0 { - panic(errs) - } + result, err := doc.BuildV3Model() + require.NoError(t, err) // extract operation. operation := result.Model.Paths.PathItems["/something"].Get @@ -387,72 +451,71 @@ paths: } func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) { - burgerShopOriginal, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml") - burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml") - originalDoc, _ := NewDocument(burgerShopOriginal) - updatedDoc, _ := NewDocument(burgerShopUpdated) - changes, errors := CompareDocuments(originalDoc, updatedDoc) - assert.Len(t, errors, 9) - assert.Nil(t, changes) + burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml") + burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml") + originalDoc, _ := NewDocument(burgerShopOriginal, + WithAllowRemoteReferences(true), + WithAllowFileReferences(true), + ) + updatedDoc, _ := NewDocument(burgerShopUpdated, + WithAllowRemoteReferences(true), + WithAllowFileReferences(true), + ) + changes, err := CompareDocuments(originalDoc, updatedDoc) + require.Len(t, errorutils.ShallowUnwrap(err), 9) + require.Nil(t, changes) } func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) { - burgerShopOriginal, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml") - burgerShopUpdated, _ := ioutil.ReadFile("test_specs/burgershop.openapi-modified.yaml") + 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, 9) - assert.Nil(t, changes) - + changes, err := CompareDocuments(updatedDoc, originalDoc) + require.Len(t, errorutils.ShallowUnwrap(err), 9) + require.Nil(t, changes) } func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) { - burgerShopOriginal, _ := ioutil.ReadFile("test_specs/petstorev2-badref.json") - burgerShopUpdated, _ := ioutil.ReadFile("test_specs/petstorev2-badref.json") + 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, 2) - assert.Nil(t, changes) + changes, err := CompareDocuments(updatedDoc, originalDoc) + require.Len(t, errorutils.ShallowUnwrap(err), 2) + require.Nil(t, changes) } func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) { - burgerShopOriginal, _ := ioutil.ReadFile("test_specs/petstorev2.json") - burgerShopUpdated, _ := ioutil.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) + burgerShopOriginal, err := os.ReadFile("test_specs/petstorev2.json") + require.NoError(t, err) + burgerShopUpdated, err := os.ReadFile("test_specs/petstorev3.json") + require.NoError(t, err) + originalDoc, err := NewDocument(burgerShopOriginal) + require.NoError(t, err) + updatedDoc, err := NewDocument(burgerShopUpdated) + require.NoError(t, err) + changes, err := CompareDocuments(updatedDoc, originalDoc) + require.Len(t, errorutils.ShallowUnwrap(err), 1) + require.Nil(t, changes) } func TestSchemaRefIsFollowed(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/ref-followed.yaml") + 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)) - } + require.NoError(t, err, "cannot create new document") // 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))) - } + v3Model, err := document.BuildV3Model() + // if anything went wrong when building the v3 model, a slice of err will be returned + require.NoError(t, err, "cannot create v3 model from document") // get a count of the number of paths and schemas. schemas := v3Model.Model.Components.Schemas @@ -505,17 +568,14 @@ paths: type: string` doc, err := NewDocument([]byte(d)) - if err != nil { - panic(err) - } + require.NoError(t, err) - result, errs := doc.BuildV3Model() - if len(errs) > 0 { - panic(errs) - } + result, err := doc.BuildV3Model() + require.NoError(t, err) // render the document. - rend, _ := result.Model.Render() + rend, err := result.Model.Render() + require.NoError(t, err) assert.Equal(t, d, strings.TrimSpace(string(rend))) } @@ -541,10 +601,8 @@ paths: // panic(err) // } // -// result, errs := doc.BuildV3Model() -// if len(errs) > 0 { -// panic(errs) -// } +// result, err := doc.BuildV3Model() +// require.NoError(t, err) // // assert.Equal(t, "crs", result.Model.Paths.PathItems["/test"].Get.Parameters[0].Name) //} @@ -567,17 +625,14 @@ components: type: object` doc, err := NewDocument([]byte(d)) - if err != nil { - panic(err) - } + require.NoError(t, err) - result, errs := doc.BuildV3Model() - if len(errs) > 0 { - panic(errs) - } + result, err := doc.BuildV3Model() + require.NoError(t, err) // render the document. - rend, _ := result.Model.Render() + rend, err := result.Model.Render() + require.NoError(t, err) assert.Len(t, rend, 644) } @@ -597,15 +652,14 @@ paths: get: $ref: test-operation.yaml` - doc, err := NewDocumentWithConfiguration([]byte(d), datamodel.NewOpenDocumentConfiguration()) - if err != nil { - panic(err) - } + doc, err := NewDocument([]byte(d), + WithAllowFileReferences(true), + WithAllowRemoteReferences(true), + ) + require.NoError(t, err) - result, errs := doc.BuildV3Model() - if len(errs) > 0 { - panic(errs) - } + result, err := doc.BuildV3Model() + require.NoError(t, err) // render the document. rend, _ := result.Model.Render() @@ -626,10 +680,11 @@ func TestDocument_InputAsJSON(t *testing.T) { } }` - doc, err := NewDocumentWithConfiguration([]byte(d), datamodel.NewOpenDocumentConfiguration()) - if err != nil { - panic(err) - } + doc, err := NewDocument([]byte(d), + WithAllowFileReferences(true), + WithAllowRemoteReferences(true), + ) + require.NoError(t, err) _, _ = doc.BuildV3Model() @@ -652,10 +707,11 @@ func TestDocument_InputAsJSON_LargeIndent(t *testing.T) { } }` - doc, err := NewDocumentWithConfiguration([]byte(d), datamodel.NewOpenDocumentConfiguration()) - if err != nil { - panic(err) - } + doc, err := NewDocument([]byte(d), + WithAllowFileReferences(true), + WithAllowRemoteReferences(true), + ) + require.NoError(t, err) _, _ = doc.BuildV3Model() @@ -676,12 +732,11 @@ paths: get: operationId: 'test'` - config := datamodel.NewOpenDocumentConfiguration() - - doc, err := NewDocumentWithConfiguration([]byte(spec), config) - if err != nil { - panic(err) - } + doc, err := NewDocument([]byte(spec), + WithAllowFileReferences(true), + WithAllowRemoteReferences(true), + ) + require.NoError(t, err) _, _ = doc.BuildV3Model() diff --git a/errors.go b/errors.go new file mode 100644 index 00000000..20a57fbd --- /dev/null +++ b/errors.go @@ -0,0 +1,39 @@ +package libopenapi + +import ( + "errors" + + "github.com/pb33f/libopenapi/resolver" +) + +var ( + ErrNoConfiguration = errors.New("no configuration available") + ErrVersionMismatch = errors.New("document version mismatch") + ErrOpenAPI3Operation = errors.New("this operation is only supported for OpenAPI 3 documents") +) + +func isCircularErr(err error) bool { + var resolvErr *resolver.ResolvingError + if errors.As(err, &resolvErr) { + return resolvErr.CircularReference != nil + } + + return false +} + +// returns a filter function that checks if a given error is a circular reference error +// and in case that circular references are allowed or not, it returns false +// in order to skip the error or true in order to keep the error in the wrapped error list. +func circularReferenceErrorFilter(forbidden bool) func(error) (keep bool) { + return func(err error) bool { + // no nil check needed, as errorutils.Filter already removes nil errors + + if isCircularErr(err) { + // if forbidded -> keep the error and pass it to the user + return forbidden + } + + // keep unknown error + return true + } +} diff --git a/go.mod b/go.mod index 7b931cd1..fb9c98e9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,8 @@ module github.com/pb33f/libopenapi go 1.20 require ( - github.com/stretchr/testify v1.8.0 + github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb + github.com/stretchr/testify v1.8.4 github.com/vmware-labs/yaml-jsonpath v0.3.2 golang.org/x/exp v0.0.0-20230811145659-89c5cff77bcb golang.org/x/sync v0.1.0 @@ -13,6 +14,5 @@ require ( require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect - github.com/lucasjones/reggen v0.0.0-20200904144131-37ba4fa293bb // indirect github.com/pmezard/go-difflib v1.0.0 // indirect ) diff --git a/go.sum b/go.sum index 674b83ab..d299d783 100644 --- a/go.sum +++ b/go.sum @@ -54,12 +54,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk= github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= diff --git a/index/extract_refs.go b/index/extract_refs.go index 637c3b54..e48e8e4f 100644 --- a/index/extract_refs.go +++ b/index/extract_refs.go @@ -149,9 +149,7 @@ func (index *SpecIndex) ExtractRefs(node, parent *yaml.Node, seenPath []string, index.linesWithRefs[n.Line] = true fp := make([]string, len(seenPath)) - for x, foundPathNode := range seenPath { - fp[x] = foundPathNode - } + copy(fp, seenPath) value := node.Content[i+1].Value diff --git a/index/find_component.go b/index/find_component.go index dc6746a3..e3664cf0 100644 --- a/index/find_component.go +++ b/index/find_component.go @@ -4,86 +4,86 @@ package index import ( - "fmt" - "io" - "net/http" - "net/url" - "os" - "path/filepath" - "strings" - "time" - - "github.com/pb33f/libopenapi/utils" - "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" - "gopkg.in/yaml.v3" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "time" + + "github.com/pb33f/libopenapi/utils" + "github.com/vmware-labs/yaml-jsonpath/pkg/yamlpath" + "gopkg.in/yaml.v3" ) // FindComponent will locate a component by its reference, returns nil if nothing is found. // This method will recurse through remote, local and file references. For each new external reference // a new index will be created. These indexes can then be traversed recursively. func (index *SpecIndex) FindComponent(componentId string, parent *yaml.Node) *Reference { - if index.root == nil { - return nil - } - - remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) { - if index.config.AllowRemoteLookup { - return index.lookupRemoteReference(id) - } else { - return nil, nil, fmt.Errorf("remote lookups are not permitted, " + - "please set AllowRemoteLookup to true in the configuration") - } - } - - fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) { - if index.config.AllowFileLookup { - return index.lookupFileReference(id) - } else { - return nil, nil, fmt.Errorf("local lookups are not permitted, " + - "please set AllowFileLookup to true in the configuration") - } - } - - switch DetermineReferenceResolveType(componentId) { - case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case. - return index.FindComponentInRoot(componentId) - - case HttpResolve: - uri := strings.Split(componentId, "#") - if len(uri) >= 2 { - return index.performExternalLookup(uri, componentId, remoteLookup, parent) - } - if len(uri) == 1 { - // if there is no reference, second segment is empty / has no name - // this means there is no component to look-up and the entire file should be pulled in. - // to stop all the other code from breaking (that is expecting a component), let's just post-pend - // a hash to the end of the componentId and ensure the uri slice is as expected. - // described in https://github.com/pb33f/libopenapi/issues/37 - componentId = fmt.Sprintf("%s#", componentId) - uri = append(uri, "") - return index.performExternalLookup(uri, componentId, remoteLookup, parent) - } - - case FileResolve: - uri := strings.Split(componentId, "#") - if len(uri) == 2 { - return index.performExternalLookup(uri, componentId, fileLookup, parent) - } - if len(uri) == 1 { - // if there is no reference, second segment is empty / has no name - // this means there is no component to look-up and the entire file should be pulled in. - // to stop all the other code from breaking (that is expecting a component), let's just post-pend - // a hash to the end of the componentId and ensure the uri slice is as expected. - // described in https://github.com/pb33f/libopenapi/issues/37 - // - // ^^ this same issue was re-reported in file based lookups in vacuum. - // more info here: https://github.com/daveshanley/vacuum/issues/225 - componentId = fmt.Sprintf("%s#", componentId) - uri = append(uri, "") - return index.performExternalLookup(uri, componentId, fileLookup, parent) - } - } - return nil + if index.root == nil { + return nil + } + + remoteLookup := func(id string) (*yaml.Node, *yaml.Node, error) { + if index.config.AllowRemoteLookup { + return index.lookupRemoteReference(id) + } else { + return nil, nil, fmt.Errorf("remote lookups are not permitted, " + + "please set AllowRemoteLookup to true in the configuration") + } + } + + fileLookup := func(id string) (*yaml.Node, *yaml.Node, error) { + if index.config.AllowFileLookup { + return index.lookupFileReference(id) + } else { + return nil, nil, fmt.Errorf("local lookups are not permitted, " + + "please set AllowFileLookup to true in the configuration") + } + } + + switch DetermineReferenceResolveType(componentId) { + case LocalResolve: // ideally, every single ref in every single spec is local. however, this is not the case. + return index.FindComponentInRoot(componentId) + + case HttpResolve: + uri := strings.Split(componentId, "#") + if len(uri) >= 2 { + return index.performExternalLookup(uri, componentId, remoteLookup, parent) + } + if len(uri) == 1 { + // if there is no reference, second segment is empty / has no name + // this means there is no component to look-up and the entire file should be pulled in. + // to stop all the other code from breaking (that is expecting a component), let's just post-pend + // a hash to the end of the componentId and ensure the uri slice is as expected. + // described in https://github.com/pb33f/libopenapi/issues/37 + componentId = fmt.Sprintf("%s#", componentId) + uri = append(uri, "") + return index.performExternalLookup(uri, componentId, remoteLookup, parent) + } + + case FileResolve: + uri := strings.Split(componentId, "#") + if len(uri) == 2 { + return index.performExternalLookup(uri, componentId, fileLookup, parent) + } + if len(uri) == 1 { + // if there is no reference, second segment is empty / has no name + // this means there is no component to look-up and the entire file should be pulled in. + // to stop all the other code from breaking (that is expecting a component), let's just post-pend + // a hash to the end of the componentId and ensure the uri slice is as expected. + // described in https://github.com/pb33f/libopenapi/issues/37 + // + // ^^ this same issue was re-reported in file based lookups in vacuum. + // more info here: https://github.com/daveshanley/vacuum/issues/225 + componentId = fmt.Sprintf("%s#", componentId) + uri = append(uri, "") + return index.performExternalLookup(uri, componentId, fileLookup, parent) + } + } + return nil } var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second} @@ -91,367 +91,367 @@ var httpClient = &http.Client{Timeout: time.Duration(60) * time.Second} type RemoteURLHandler = func(url string) (*http.Response, error) func getRemoteDoc(g RemoteURLHandler, u string, d chan []byte, e chan error) { - resp, err := g(u) - if err != nil { - e <- err - close(e) - close(d) - return - } - var body []byte - body, _ = io.ReadAll(resp.Body) - d <- body - close(e) - close(d) + resp, err := g(u) + if err != nil { + e <- err + close(e) + close(d) + return + } + var body []byte + body, _ = io.ReadAll(resp.Body) + d <- body + close(e) + close(d) } func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Node, error) { - // split string to remove file reference - uri := strings.Split(ref, "#") - - // have we already seen this remote source? - var parsedRemoteDocument *yaml.Node - alreadySeen, foundDocument := index.CheckForSeenRemoteSource(uri[0]) - - if alreadySeen { - parsedRemoteDocument = foundDocument - } else { - - d := make(chan bool) - var body []byte - var err error - - go func(uri string) { - bc := make(chan []byte) - ec := make(chan error) - var getter RemoteURLHandler = httpClient.Get - if index.config != nil && index.config.RemoteURLHandler != nil { - getter = index.config.RemoteURLHandler - } - - // if we have a remote handler, use it instead of the default. - if index.config != nil && index.config.FSHandler != nil { - go func() { - remoteFS := index.config.FSHandler - remoteFile, rErr := remoteFS.Open(uri) - if rErr != nil { - e := fmt.Errorf("unable to open remote file: %s", rErr) - ec <- e - return - } - b, ioErr := io.ReadAll(remoteFile) - if ioErr != nil { - e := fmt.Errorf("unable to read remote file bytes: %s", ioErr) - ec <- e - return - } - bc <- b - }() - } else { - go getRemoteDoc(getter, uri, bc, ec) - } - select { - case v := <-bc: - body = v - break - case er := <-ec: - err = er - break - } - if len(body) > 0 { - var remoteDoc yaml.Node - er := yaml.Unmarshal(body, &remoteDoc) - if er != nil { - err = er - d <- true - return - } - parsedRemoteDocument = &remoteDoc - if index.config != nil { - index.config.seenRemoteSources.Store(uri, &remoteDoc) - } - } - d <- true - }(uri[0]) - - // wait for double go fun. - <-d - if err != nil { - // no bueno. - return nil, nil, err - } - } - - // lookup item from reference by using a path query. - var query string - if len(uri) >= 2 { - query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", ".")) - } else { - query = "$" - } - - query, err := url.PathUnescape(query) - if err != nil { - return nil, nil, err - } - - // remove any URL encoding - query = strings.Replace(query, "~1", "./", 1) - query = strings.ReplaceAll(query, "~1", "/") - - path, err := yamlpath.NewPath(query) - if err != nil { - return nil, nil, err - } - result, _ := path.Find(parsedRemoteDocument) - if len(result) == 1 { - return result[0], parsedRemoteDocument, nil - } - return nil, nil, nil + // split string to remove file reference + uri := strings.Split(ref, "#") + + // have we already seen this remote source? + var parsedRemoteDocument *yaml.Node + alreadySeen, foundDocument := index.CheckForSeenRemoteSource(uri[0]) + + if alreadySeen { + parsedRemoteDocument = foundDocument + } else { + + d := make(chan bool) + var body []byte + var err error + + go func(uri string) { + bc := make(chan []byte) + ec := make(chan error) + var getter RemoteURLHandler = httpClient.Get + if index.config != nil && index.config.RemoteURLHandler != nil { + getter = index.config.RemoteURLHandler + } + + // if we have a remote handler, use it instead of the default. + if index.config != nil && index.config.FSHandler != nil { + go func() { + remoteFS := index.config.FSHandler + remoteFile, rErr := remoteFS.Open(uri) + if rErr != nil { + e := fmt.Errorf("unable to open remote file: %s", rErr) + ec <- e + return + } + b, ioErr := io.ReadAll(remoteFile) + if ioErr != nil { + e := fmt.Errorf("unable to read remote file bytes: %s", ioErr) + ec <- e + return + } + bc <- b + }() + } else { + go getRemoteDoc(getter, uri, bc, ec) + } + select { + case v := <-bc: + body = v + break + case er := <-ec: + err = er + break + } + if len(body) > 0 { + var remoteDoc yaml.Node + er := yaml.Unmarshal(body, &remoteDoc) + if er != nil { + err = er + d <- true + return + } + parsedRemoteDocument = &remoteDoc + if index.config != nil { + index.config.seenRemoteSources.Store(uri, &remoteDoc) + } + } + d <- true + }(uri[0]) + + // wait for double go fun. + <-d + if err != nil { + // no bueno. + return nil, nil, err + } + } + + // lookup item from reference by using a path query. + var query string + if len(uri) >= 2 { + query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", ".")) + } else { + query = "$" + } + + query, err := url.PathUnescape(query) + if err != nil { + return nil, nil, err + } + + // remove any URL encoding + query = strings.Replace(query, "~1", "./", 1) + query = strings.ReplaceAll(query, "~1", "/") + + path, err := yamlpath.NewPath(query) + if err != nil { + return nil, nil, err + } + result, _ := path.Find(parsedRemoteDocument) + if len(result) == 1 { + return result[0], parsedRemoteDocument, nil + } + return nil, nil, nil } func (index *SpecIndex) lookupFileReference(ref string) (*yaml.Node, *yaml.Node, error) { - // split string to remove file reference - uri := strings.Split(ref, "#") - file := strings.ReplaceAll(uri[0], "file:", "") - filePath := filepath.Dir(file) - fileName := filepath.Base(file) - - var parsedRemoteDocument *yaml.Node - - if index.seenRemoteSources[file] != nil { - parsedRemoteDocument = index.seenRemoteSources[file] - } else { - - base := index.config.BasePath - fileToRead := filepath.Join(base, filePath, fileName) - var body []byte - var err error - - // if we have an FS handler, use it instead of the default behavior - if index.config != nil && index.config.FSHandler != nil { - remoteFS := index.config.FSHandler - remoteFile, rErr := remoteFS.Open(fileToRead) - if rErr != nil { - e := fmt.Errorf("unable to open file: %s", rErr) - return nil, nil, e - } - body, err = io.ReadAll(remoteFile) - if err != nil { - e := fmt.Errorf("unable to read file bytes: %s", err) - return nil, nil, e - } - - } else { - - // try and read the file off the local file system, if it fails - // check for a baseURL and then ask our remote lookup function to go try and get it. - body, err = os.ReadFile(fileToRead) - - if err != nil { - - // if we have a baseURL, then we can try and get the file from there. - if index.config != nil && index.config.BaseURL != nil { - - u := index.config.BaseURL - remoteRef := GenerateCleanSpecConfigBaseURL(u, ref, true) - a, b, e := index.lookupRemoteReference(remoteRef) - if e != nil { - // give up, we can't find the file, not locally, not remotely. It's toast. - return nil, nil, e - } - return a, b, nil - - } else { - // no baseURL? then we can't do anything, give up. - return nil, nil, err - } - } - } - var remoteDoc yaml.Node - err = yaml.Unmarshal(body, &remoteDoc) - if err != nil { - return nil, nil, err - } - parsedRemoteDocument = &remoteDoc - if index.seenLocalSources != nil { - index.sourceLock.Lock() - index.seenLocalSources[file] = &remoteDoc - index.sourceLock.Unlock() - } - } - - // lookup item from reference by using a path query. - var query string - if len(uri) >= 2 { - query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", ".")) - } else { - query = "$" - } - - query, err := url.PathUnescape(query) - if err != nil { - return nil, nil, err - } - - // remove any URL encoding - query = strings.Replace(query, "~1", "./", 1) - query = strings.ReplaceAll(query, "~1", "/") - - path, err := yamlpath.NewPath(query) - if err != nil { - return nil, nil, err - } - result, _ := path.Find(parsedRemoteDocument) - if len(result) == 1 { - return result[0], parsedRemoteDocument, nil - } - - return nil, parsedRemoteDocument, nil + // split string to remove file reference + uri := strings.Split(ref, "#") + file := strings.ReplaceAll(uri[0], "file:", "") + filePath := filepath.Dir(file) + fileName := filepath.Base(file) + + var parsedRemoteDocument *yaml.Node + + if index.seenRemoteSources[file] != nil { + parsedRemoteDocument = index.seenRemoteSources[file] + } else { + + base := index.config.BasePath + fileToRead := filepath.Join(base, filePath, fileName) + var body []byte + var err error + + // if we have an FS handler, use it instead of the default behavior + if index.config != nil && index.config.FSHandler != nil { + remoteFS := index.config.FSHandler + remoteFile, rErr := remoteFS.Open(fileToRead) + if rErr != nil { + e := fmt.Errorf("unable to open file: %s", rErr) + return nil, nil, e + } + body, err = io.ReadAll(remoteFile) + if err != nil { + e := fmt.Errorf("unable to read file bytes: %s", err) + return nil, nil, e + } + + } else { + + // try and read the file off the local file system, if it fails + // check for a baseURL and then ask our remote lookup function to go try and get it. + body, err = os.ReadFile(fileToRead) + + if err != nil { + + // if we have a baseURL, then we can try and get the file from there. + if index.config != nil && index.config.BaseURL != nil { + + u := index.config.BaseURL + remoteRef := GenerateCleanSpecConfigBaseURL(u, ref, true) + a, b, e := index.lookupRemoteReference(remoteRef) + if e != nil { + // give up, we can't find the file, not locally, not remotely. It's toast. + return nil, nil, e + } + return a, b, nil + + } else { + // no baseURL? then we can't do anything, give up. + return nil, nil, err + } + } + } + var remoteDoc yaml.Node + err = yaml.Unmarshal(body, &remoteDoc) + if err != nil { + return nil, nil, err + } + parsedRemoteDocument = &remoteDoc + if index.seenLocalSources != nil { + index.sourceLock.Lock() + index.seenLocalSources[file] = &remoteDoc + index.sourceLock.Unlock() + } + } + + // lookup item from reference by using a path query. + var query string + if len(uri) >= 2 { + query = fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", ".")) + } else { + query = "$" + } + + query, err := url.PathUnescape(query) + if err != nil { + return nil, nil, err + } + + // remove any URL encoding + query = strings.Replace(query, "~1", "./", 1) + query = strings.ReplaceAll(query, "~1", "/") + + path, err := yamlpath.NewPath(query) + if err != nil { + return nil, nil, err + } + result, _ := path.Find(parsedRemoteDocument) + if len(result) == 1 { + return result[0], parsedRemoteDocument, nil + } + + return nil, parsedRemoteDocument, nil } func (index *SpecIndex) FindComponentInRoot(componentId string) *Reference { - if index.root != nil { - - // check component for url encoding. - if strings.Contains(componentId, "%") { - // decode the url. - componentId, _ = url.QueryUnescape(componentId) - } - - name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId) - path, err := yamlpath.NewPath(friendlySearch) - if path == nil || err != nil { - return nil // no component found - } - res, _ := path.Find(index.root) - - if len(res) == 1 { - resNode := res[0] - if res[0].Kind == yaml.DocumentNode { - resNode = res[0].Content[0] - } - ref := &Reference{ - Definition: componentId, - Name: name, - Node: resNode, - Path: friendlySearch, - RequiredRefProperties: index.extractDefinitionRequiredRefProperties(res[0], map[string][]string{}), - } - - return ref - } - } - return nil + if index.root != nil { + + // check component for url encoding. + if strings.Contains(componentId, "%") { + // decode the url. + componentId, _ = url.QueryUnescape(componentId) + } + + name, friendlySearch := utils.ConvertComponentIdIntoFriendlyPathSearch(componentId) + path, err := yamlpath.NewPath(friendlySearch) + if path == nil || err != nil { + return nil // no component found + } + res, _ := path.Find(index.root) + + if len(res) == 1 { + resNode := res[0] + if res[0].Kind == yaml.DocumentNode { + resNode = res[0].Content[0] + } + ref := &Reference{ + Definition: componentId, + Name: name, + Node: resNode, + Path: friendlySearch, + RequiredRefProperties: index.extractDefinitionRequiredRefProperties(res[0], map[string][]string{}), + } + + return ref + } + } + return nil } func (index *SpecIndex) performExternalLookup(uri []string, componentId string, - lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference { - if len(uri) > 0 { - index.externalLock.RLock() - externalSpecIndex := index.externalSpecIndex[uri[0]] - index.externalLock.RUnlock() - - if externalSpecIndex == nil { - _, newRoot, err := lookupFunction(componentId) - if err != nil { - indexError := &IndexingError{ - Err: err, - Node: parent, - Path: componentId, - } - index.errorLock.Lock() - index.refErrors = append(index.refErrors, indexError) - index.errorLock.Unlock() - return nil - } - - // cool, cool, lets index this spec also. This is a recursive action and will keep going - // until all remote references have been found. - var bp *url.URL - var bd string - - if index.config.BaseURL != nil { - bp = index.config.BaseURL - } - if index.config.BasePath != "" { - bd = index.config.BasePath - } - - var path, newBasePath string - var newUrl *url.URL - - if bp != nil { - path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false) - newUrl, _ = url.Parse(path) - newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path))) - } - if bd != "" { - if len(uri[0]) > 0 { - // if there is no base url defined, but we can know we have been requested remotely, - // set the base url to the remote url base path. - // first check if the first param is actually a URL - io, er := url.ParseRequestURI(uri[0]) - if er != nil { - newBasePath = filepath.Dir(filepath.Join(bd, uri[0])) - } else { - if newUrl == nil || newUrl.String() != io.String() { - newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path))) - } - newBasePath = filepath.Dir(filepath.Join(bd, uri[1])) - } - } else { - newBasePath = filepath.Dir(filepath.Join(bd, uri[0])) - } - } - - if newUrl != nil || newBasePath != "" { - newConfig := &SpecIndexConfig{ - BaseURL: newUrl, - BasePath: newBasePath, - AllowRemoteLookup: index.config.AllowRemoteLookup, - AllowFileLookup: index.config.AllowFileLookup, - ParentIndex: index, - seenRemoteSources: index.config.seenRemoteSources, - remoteLock: index.config.remoteLock, - uri: uri, - } - - var newIndex *SpecIndex - seen := index.SearchAncestryForSeenURI(uri[0]) - if seen == nil { - - newIndex = NewSpecIndexWithConfig(newRoot, newConfig) - index.refLock.Lock() - index.externalLock.Lock() - index.externalSpecIndex[uri[0]] = newIndex - index.externalLock.Unlock() - newIndex.relativePath = path - newIndex.parentIndex = index - index.AddChild(newIndex) - index.refLock.Unlock() - externalSpecIndex = newIndex - } else { - externalSpecIndex = seen - } - } - } - - if externalSpecIndex != nil { - foundRef := externalSpecIndex.FindComponentInRoot(uri[1]) - if foundRef != nil { - nameSegs := strings.Split(uri[1], "/") - ref := &Reference{ - Definition: componentId, - Name: nameSegs[len(nameSegs)-1], - Node: foundRef.Node, - IsRemote: true, - RemoteLocation: componentId, - Path: foundRef.Path, - } - return ref - } - } - } - return nil + lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference { + if len(uri) > 0 { + index.externalLock.RLock() + externalSpecIndex := index.externalSpecIndex[uri[0]] + index.externalLock.RUnlock() + + if externalSpecIndex == nil { + _, newRoot, err := lookupFunction(componentId) + if err != nil { + indexError := &IndexingError{ + Err: err, + Node: parent, + Path: componentId, + } + index.errorLock.Lock() + index.refErrors = append(index.refErrors, indexError) + index.errorLock.Unlock() + return nil + } + + // cool, cool, lets index this spec also. This is a recursive action and will keep going + // until all remote references have been found. + var bp *url.URL + var bd string + + if index.config.BaseURL != nil { + bp = index.config.BaseURL + } + if index.config.BasePath != "" { + bd = index.config.BasePath + } + + var path, newBasePath string + var newUrl *url.URL + + if bp != nil { + path = GenerateCleanSpecConfigBaseURL(bp, uri[0], false) + newUrl, _ = url.Parse(path) + newBasePath = filepath.Dir(filepath.Join(index.config.BasePath, filepath.Dir(newUrl.Path))) + } + if bd != "" { + if len(uri[0]) > 0 { + // if there is no base url defined, but we can know we have been requested remotely, + // set the base url to the remote url base path. + // first check if the first param is actually a URL + io, er := url.ParseRequestURI(uri[0]) + if er != nil { + newBasePath = filepath.Dir(filepath.Join(bd, uri[0])) + } else { + if newUrl == nil || newUrl.String() != io.String() { + newUrl, _ = url.Parse(fmt.Sprintf("%s://%s%s", io.Scheme, io.Host, filepath.Dir(io.Path))) + } + newBasePath = filepath.Dir(filepath.Join(bd, uri[1])) + } + } else { + newBasePath = filepath.Dir(filepath.Join(bd, uri[0])) + } + } + + if newUrl != nil || newBasePath != "" { + newConfig := &SpecIndexConfig{ + BaseURL: newUrl, + BasePath: newBasePath, + AllowRemoteLookup: index.config.AllowRemoteLookup, + AllowFileLookup: index.config.AllowFileLookup, + ParentIndex: index, + seenRemoteSources: index.config.seenRemoteSources, + remoteLock: index.config.remoteLock, + uri: uri, + } + + var newIndex *SpecIndex + seen := index.SearchAncestryForSeenURI(uri[0]) + if seen == nil { + + newIndex = NewSpecIndexWithConfig(newRoot, newConfig) + index.refLock.Lock() + index.externalLock.Lock() + index.externalSpecIndex[uri[0]] = newIndex + index.externalLock.Unlock() + newIndex.relativePath = path + newIndex.parentIndex = index + index.AddChild(newIndex) + index.refLock.Unlock() + externalSpecIndex = newIndex + } else { + externalSpecIndex = seen + } + } + } + + if externalSpecIndex != nil { + foundRef := externalSpecIndex.FindComponentInRoot(uri[1]) + if foundRef != nil { + nameSegs := strings.Split(uri[1], "/") + ref := &Reference{ + Definition: componentId, + Name: nameSegs[len(nameSegs)-1], + Node: foundRef.Node, + IsRemote: true, + RemoteLocation: componentId, + Path: foundRef.Path, + } + return ref + } + } + } + return nil } diff --git a/index/find_component_test.go b/index/find_component_test.go index 872abc1f..a2191850 100644 --- a/index/find_component_test.go +++ b/index/find_component_test.go @@ -15,6 +15,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) @@ -115,7 +116,8 @@ components: $ref: 'https://pb33f.io/site.webmanifest#/valid-but-missing'` var rootNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &rootNode) + err := yaml.Unmarshal([]byte(yml), &rootNode) + require.NoError(t, err) c := CreateOpenAPIIndexConfig() index := NewSpecIndexWithConfig(&rootNode, c) @@ -175,8 +177,10 @@ paths: // extract crs param from index crsParam := index.GetMappedReferences()["https://schemas.opengis.net/ogcapi/features/part2/1.0/openapi/ogcapi-features-2.yaml#/components/parameters/crs"] - assert.NotNil(t, crsParam) + require.NotNil(t, crsParam) assert.True(t, crsParam.IsRemote) + require.NotNil(t, crsParam.Node) + require.GreaterOrEqual(t, len(crsParam.Node.Content), 10) assert.Equal(t, "crs", crsParam.Node.Content[1].Value) assert.Equal(t, "query", crsParam.Node.Content[3].Value) assert.Equal(t, "form", crsParam.Node.Content[9].Value) diff --git a/index/spec_index_test.go b/index/spec_index_test.go index 9061f9eb..9ae8089e 100644 --- a/index/spec_index_test.go +++ b/index/spec_index_test.go @@ -18,7 +18,7 @@ import ( ) func TestSpecIndex_ExtractRefsStripe(t *testing.T) { - stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + stripe, _ := os.ReadFile("../test_specs/stripe.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(stripe, &rootNode) @@ -65,7 +65,7 @@ func TestSpecIndex_ExtractRefsStripe(t *testing.T) { } func TestSpecIndex_Asana(t *testing.T) { - asana, _ := ioutil.ReadFile("../test_specs/asana.yaml") + asana, _ := os.ReadFile("../test_specs/asana.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(asana, &rootNode) @@ -112,7 +112,7 @@ func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) { log.Fatalf("cmd.Run() failed with %s\n", err) } spec, _ := filepath.Abs(filepath.Join(tmp, "specification", "DigitalOcean-public.v2.yaml")) - doLocal, _ := ioutil.ReadFile(spec) + doLocal, _ := os.ReadFile(spec) var rootNode yaml.Node _ = yaml.Unmarshal(doLocal, &rootNode) @@ -141,7 +141,7 @@ func TestSpecIndex_DigitalOcean_FullCheckoutLocalResolve(t *testing.T) { } func TestSpecIndex_DigitalOcean_LookupsNotAllowed(t *testing.T) { - asana, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml") + asana, _ := os.ReadFile("../test_specs/digitalocean.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(asana, &rootNode) @@ -156,7 +156,7 @@ func TestSpecIndex_DigitalOcean_LookupsNotAllowed(t *testing.T) { } func TestSpecIndex_BaseURLError(t *testing.T) { - asana, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml") + asana, _ := os.ReadFile("../test_specs/digitalocean.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(asana, &rootNode) @@ -173,7 +173,7 @@ func TestSpecIndex_BaseURLError(t *testing.T) { } func TestSpecIndex_k8s(t *testing.T) { - asana, _ := ioutil.ReadFile("../test_specs/k8s.json") + asana, _ := os.ReadFile("../test_specs/k8s.json") var rootNode yaml.Node _ = yaml.Unmarshal(asana, &rootNode) @@ -198,7 +198,7 @@ func TestSpecIndex_k8s(t *testing.T) { } func TestSpecIndex_PetstoreV2(t *testing.T) { - asana, _ := ioutil.ReadFile("../test_specs/petstorev2.json") + asana, _ := os.ReadFile("../test_specs/petstorev2.json") var rootNode yaml.Node _ = yaml.Unmarshal(asana, &rootNode) @@ -222,7 +222,7 @@ func TestSpecIndex_PetstoreV2(t *testing.T) { } func TestSpecIndex_XSOAR(t *testing.T) { - xsoar, _ := ioutil.ReadFile("../test_specs/xsoar.json") + xsoar, _ := os.ReadFile("../test_specs/xsoar.json") var rootNode yaml.Node _ = yaml.Unmarshal(xsoar, &rootNode) @@ -240,7 +240,7 @@ func TestSpecIndex_XSOAR(t *testing.T) { } func TestSpecIndex_PetstoreV3(t *testing.T) { - petstore, _ := ioutil.ReadFile("../test_specs/petstorev3.json") + petstore, _ := os.ReadFile("../test_specs/petstorev3.json") var rootNode yaml.Node _ = yaml.Unmarshal(petstore, &rootNode) @@ -268,7 +268,7 @@ func TestSpecIndex_PetstoreV3(t *testing.T) { var mappedRefs = 15 func TestSpecIndex_BurgerShop(t *testing.T) { - burgershop, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + burgershop, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(burgershop, &rootNode) @@ -366,7 +366,7 @@ paths: } func TestSpecIndex_BurgerShop_AllTheComponents(t *testing.T) { - burgershop, _ := ioutil.ReadFile("../test_specs/all-the-components.yaml") + burgershop, _ := os.ReadFile("../test_specs/all-the-components.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(burgershop, &rootNode) @@ -435,7 +435,7 @@ func TestSpecIndex_NoRoot(t *testing.T) { } func TestSpecIndex_BurgerShopMixedRef(t *testing.T) { - spec, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") + spec, _ := os.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(spec, &rootNode) @@ -463,7 +463,7 @@ func TestSpecIndex_BurgerShopMixedRef(t *testing.T) { } func TestSpecIndex_TestEmptyBrokenReferences(t *testing.T) { - asana, _ := ioutil.ReadFile("../test_specs/badref-burgershop.openapi.yaml") + asana, _ := os.ReadFile("../test_specs/badref-burgershop.openapi.yaml") var rootNode yaml.Node _ = yaml.Unmarshal(asana, &rootNode) diff --git a/index/utility_methods.go b/index/utility_methods.go index 88038720..c63657ce 100644 --- a/index/utility_methods.go +++ b/index/utility_methods.go @@ -441,9 +441,8 @@ func GenerateCleanSpecConfigBaseURL(baseURL *url.URL, dir string, includeFile bo } } - if strings.HasSuffix(p, "/") { - p = p[:len(p)-1] - } + p = strings.TrimSuffix(p, "/") + return p } diff --git a/internal/errorutils/errorutils.go b/internal/errorutils/errorutils.go new file mode 100644 index 00000000..2719d4bc --- /dev/null +++ b/internal/errorutils/errorutils.go @@ -0,0 +1,78 @@ +package errorutils + +import ( + "fmt" + "strings" +) + +// MultiError is a collection of errors. +// It never contains nil values. +type MultiError struct { + errs []error +} + +func (e *MultiError) Error() string { + var b strings.Builder + b.Grow(len(e.errs) * 16) + for i, err := range e.errs { + b.WriteString(fmt.Sprintf("[%d] %v\n", i, err)) + } + return b.String() +} + +func (e *MultiError) Unwrap() []error { + return e.errs +} + +// Join should be used at the end of top level functions to join all errors. +func Join(errs ...error) error { + var result MultiError + + size := 0 + for _, err := range errs { + if err != nil { + size++ + } + } + if size == 0 { + return nil + } + + result.errs = make([]error, 0, size) + for _, err := range errs { + if err == nil { + continue + } + // try to keep MultiError flat + result.errs = append(result.errs, deepUnwrapMultiError(err)...) + } + return &result +} + +func ShallowUnwrap(err error) []error { + if err == nil { + return nil + } + unwrap, ok := err.(interface{ Unwrap() []error }) + if !ok { + return []error{err} + } + + return unwrap.Unwrap() +} + +func deepUnwrapMultiError(err error) []error { + if err == nil { + return nil + } + var result []error + + if multi, ok := err.(*MultiError); ok { + for _, e := range multi.Unwrap() { + result = append(result, deepUnwrapMultiError(e)...) + } + } else { + result = append(result, err) + } + return result +} diff --git a/internal/errorutils/errorutils_test.go b/internal/errorutils/errorutils_test.go new file mode 100644 index 00000000..7745609e --- /dev/null +++ b/internal/errorutils/errorutils_test.go @@ -0,0 +1,46 @@ +package errorutils + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUnwrapErrors(t *testing.T) { + err := Join(errors.New("foo"), errors.New("bar")) + require.NotEmpty(t, err.Error()) + + errs := ShallowUnwrap(err) + require.Len(t, errs, 2) +} + +func TestUnwrapError(t *testing.T) { + err := fmt.Errorf("foo: %w", errors.New("bar")) + + errs := ShallowUnwrap(err) + require.Len(t, errs, 1) +} + +func TestUnwrapHierarchyError(t *testing.T) { + err1 := errors.New("bar") + err2 := fmt.Errorf("foo: %w", err1) + + err3 := errors.New("fo") + err4 := fmt.Errorf("barr: %w", err3) + + err := Join(Join(nil, err2), Join(nil, err4, nil)) + + errs := ShallowUnwrap(err) + require.Len(t, errs, 2) +} + +func TestJoinNils(t *testing.T) { + err := Join(nil, nil) + require.Nil(t, err) +} + +func TestDeepMultiErrorUnwrapNil(t *testing.T) { + require.Nil(t, deepUnwrapMultiError(nil)) +} diff --git a/internal/errorutils/filter.go b/internal/errorutils/filter.go new file mode 100644 index 00000000..d98a8dfb --- /dev/null +++ b/internal/errorutils/filter.go @@ -0,0 +1,46 @@ +package errorutils + +// Filter returns a filtered multi error. +// filter functions must return false for their specific errors +// but true for every other unknown error in order to keep them untouched and potentially +// removed by another filter. +func Filtered(err error, filters ...func(error) (keep bool)) error { + if err == nil { + return nil + } + errs := ShallowUnwrap(err) + filtered := Filter(errs, and(filters...)) + if len(filtered) == 0 { + return nil + } + return Join(filtered...) +} + +func Filter(errs []error, filter func(error) (keep bool)) []error { + var result []error + var keep bool + for _, err := range errs { + if err == nil { + continue + } + keep = filter(err) + if keep { + result = append(result, err) + } + } + return result +} + +func and(filters ...func(error) (keep bool)) func(error) (keep bool) { + return func(err error) bool { + var keep bool + for _, filter := range filters { + keep = filter(err) + if !keep { + return false + } + } + // all true -> true + return true + } +} diff --git a/internal/errorutils/filter_test.go b/internal/errorutils/filter_test.go new file mode 100644 index 00000000..0036f340 --- /dev/null +++ b/internal/errorutils/filter_test.go @@ -0,0 +1,113 @@ +package errorutils + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +type customErr2 struct { + FieldValue string +} + +func (e *customErr2) Error() string { + return e.FieldValue +} + +type customErr struct { + FieldValue string +} + +func (e *customErr) Error() string { + return e.FieldValue +} + +func custom1Filter(err error) (keep bool) { + if _, ok := err.(*customErr); ok { + return false + } + // do not touch unknown errors + return true +} + +func custom2Filter(err error) bool { + if _, ok := err.(*customErr2); ok { + return false + } + return true +} + +func deepCustom1Filter(err error) bool { + var cerr *customErr + return !errors.As(err, &cerr) +} + +func removeAllFilter(err error) bool { + return false +} + +func TestFilter(t *testing.T) { + + errs := []error{ + &customErr{FieldValue: "foo"}, + fmt.Errorf("foo: %w", &customErr{FieldValue: "foo"}), + errors.New("bar"), + nil, + } + + filtered := Filter(errs, custom1Filter) + require.Len(t, filtered, 2) + + for _, err := range filtered { + require.NotNil(t, err) + _, ok := err.(*customErr) + require.False(t, ok, "cannot type assert to customErr") + } + + // additional filter that removes errors + filtered = Filter(errs, deepCustom1Filter) + + require.Len(t, filtered, 1) +} + +func TestFilteredNormal(t *testing.T) { + errs := []error{ + &customErr{FieldValue: "foo"}, + fmt.Errorf("foo: %w", &customErr{FieldValue: "foo"}), + errors.New("bar"), + &customErr2{"custom2 error"}, + nil, + } + err := Join(errs...) + + filtered := Filtered(err, custom1Filter) + + require.Len(t, ShallowUnwrap(filtered), 3) + + filtered = Filtered(err, deepCustom1Filter) + require.Len(t, ShallowUnwrap(filtered), 2) + + filtered = Filtered(err, deepCustom1Filter, custom2Filter) + require.Len(t, ShallowUnwrap(filtered), 1) +} + +func TestFilteredNil(t *testing.T) { + + filtered := Filtered(nil, custom1Filter) + require.Nil(t, filtered) +} + +func TestFilteredEmpty(t *testing.T) { + errs := []error{ + &customErr{FieldValue: "foo"}, + fmt.Errorf("foo: %w", &customErr{FieldValue: "foo"}), + errors.New("bar"), + nil, + } + err := Join(errs...) + + filtered := Filtered(err, removeAllFilter) + require.Len(t, ShallowUnwrap(filtered), 0) +} diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 1db450ec..5729ac9b 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -3,12 +3,13 @@ package resolver import ( "errors" "fmt" - "io/ioutil" "net/url" + "os" "testing" "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) @@ -17,7 +18,7 @@ func TestNewResolver(t *testing.T) { } func Benchmark_ResolveDocumentStripe(b *testing.B) { - stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + stripe, _ := os.ReadFile("../test_specs/stripe.yaml") for n := 0; n < b.N; n++ { var rootNode yaml.Node yaml.Unmarshal(stripe, &rootNode) @@ -28,7 +29,7 @@ func Benchmark_ResolveDocumentStripe(b *testing.B) { } func TestResolver_ResolveComponents_CircularSpec(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") + circular, _ := os.ReadFile("../test_specs/circular-tests.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -45,7 +46,7 @@ func TestResolver_ResolveComponents_CircularSpec(t *testing.T) { } func TestResolver_CheckForCircularReferences(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") + circular, _ := os.ReadFile("../test_specs/circular-tests.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -64,7 +65,7 @@ func TestResolver_CheckForCircularReferences(t *testing.T) { } func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/digitalocean.yaml") + circular, _ := os.ReadFile("../test_specs/digitalocean.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -77,19 +78,19 @@ func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) { }) resolver := NewResolver(index) - assert.NotNil(t, resolver) + require.NotNil(t, resolver) circ := resolver.CheckForCircularReferences() - assert.Len(t, circ, 0) - assert.Len(t, resolver.GetResolvingErrors(), 0) - assert.Len(t, resolver.GetCircularErrors(), 0) + require.Len(t, circ, 0) + require.Len(t, resolver.GetResolvingErrors(), 0) + require.Len(t, resolver.GetCircularErrors(), 0) _, err := yaml.Marshal(resolver.resolvedRoot) - assert.NoError(t, err) + require.NoError(t, err) } func TestResolver_CircularReferencesRequiredValid(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml") + circular, _ := os.ReadFile("../test_specs/swagger-valid-recursive-model.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -106,7 +107,7 @@ func TestResolver_CircularReferencesRequiredValid(t *testing.T) { } func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml") + circular, _ := os.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -133,7 +134,7 @@ func TestResolver_DeepJourney(t *testing.T) { } func TestResolver_ResolveComponents_Stripe(t *testing.T) { - stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + stripe, _ := os.ReadFile("../test_specs/stripe.yaml") var rootNode yaml.Node yaml.Unmarshal(stripe, &rootNode) @@ -150,7 +151,7 @@ func TestResolver_ResolveComponents_Stripe(t *testing.T) { } func TestResolver_ResolveComponents_BurgerShop(t *testing.T) { - mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + mixedref, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") var rootNode yaml.Node yaml.Unmarshal(mixedref, &rootNode) @@ -261,7 +262,7 @@ components: } func TestResolver_ResolveComponents_MixedRef(t *testing.T) { - mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") + mixedref, _ := os.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") var rootNode yaml.Node yaml.Unmarshal(mixedref, &rootNode) @@ -282,7 +283,7 @@ func TestResolver_ResolveComponents_MixedRef(t *testing.T) { } func TestResolver_ResolveComponents_k8s(t *testing.T) { - k8s, _ := ioutil.ReadFile("../test_specs/k8s.json") + k8s, _ := os.ReadFile("../test_specs/k8s.json") var rootNode yaml.Node yaml.Unmarshal(k8s, &rootNode) @@ -301,7 +302,7 @@ func ExampleNewResolver() { var rootNode yaml.Node // load in the Stripe OpenAPI spec (lots of polymorphic complexity in here) - stripeBytes, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + stripeBytes, _ := os.ReadFile("../test_specs/stripe.yaml") // unmarshal bytes into our rootNode. _ = yaml.Unmarshal(stripeBytes, &rootNode) diff --git a/utils/type_check.go b/utils/type_check.go index aceee4bb..b7f08d83 100644 --- a/utils/type_check.go +++ b/utils/type_check.go @@ -17,31 +17,31 @@ func AreValuesCorrectlyTyped(valType string, values interface{}) map[string]stri results := make(map[string]string) for _, v := range arr { - switch v.(type) { + switch t := v.(type) { case string: if valType != "string" { - results[v.(string)] = fmt.Sprintf("enum value '%v' is a "+ - "string, but it's defined as a '%v'", v, valType) + results[t] = fmt.Sprintf("enum value '%v' is a "+ + "string, but it's defined as a '%v'", t, valType) } case int64: if valType != "integer" && valType != "number" { results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ - "integer, but it's defined as a '%v'", v, valType) + "integer, but it's defined as a '%v'", t, valType) } case int: if valType != "integer" && valType != "number" { results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ - "integer, but it's defined as a '%v'", v, valType) + "integer, but it's defined as a '%v'", t, valType) } case float64: if valType != "number" { results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ - "number, but it's defined as a '%v'", v, valType) + "number, but it's defined as a '%v'", t, valType) } case bool: if valType != "boolean" { results[fmt.Sprintf("%v", v)] = fmt.Sprintf("enum value '%v' is a "+ - "boolean, but it's defined as a '%v'", v, valType) + "boolean, but it's defined as a '%v'", t, valType) } } } diff --git a/utils/utils.go b/utils/utils.go index e384906e..95087303 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -560,8 +560,11 @@ func IsHttpVerb(verb string) bool { return false } -// define bracket name expression -var bracketNameExp = regexp.MustCompile("^(\\w+)\\[(\\w+)\\]$") +var ( + // define bracket name expression + bracketNameExp = regexp.MustCompile(`^(\w+)\[(\w+)\]$`) + pathCharExp = regexp.MustCompile(`[%=;~.]`) +) func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { segs := strings.Split(id, "/") @@ -569,9 +572,9 @@ func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { var cleaned []string // check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path. + for i := range segs { - pathCharExp, _ := regexp.MatchString("[%=;~.]", segs[i]) - if pathCharExp { + if pathCharExp.MatchString(segs[i]) { segs[i], _ = url.QueryUnescape(strings.ReplaceAll(segs[i], "~1", "/")) segs[i] = fmt.Sprintf("['%s']", segs[i]) if len(cleaned) > 0 { @@ -609,11 +612,9 @@ func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { _, err := strconv.ParseInt(name, 10, 32) var replaced string if err != nil { - replaced = strings.ReplaceAll(fmt.Sprintf("%s", - strings.Join(cleaned, ".")), "#", "$") + replaced = strings.ReplaceAll(strings.Join(cleaned, "."), "#", "$") } else { - replaced = strings.ReplaceAll(fmt.Sprintf("%s", - strings.Join(cleaned, ".")), "#", "$") + replaced = strings.ReplaceAll(strings.Join(cleaned, "."), "#", "$") } if len(replaced) > 0 { @@ -660,18 +661,22 @@ func RenderCodeSnippet(startNode *yaml.Node, specData []string, before, after in return buf.String() } +var ( + // compile only once + pascalCase = regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$") + camelCase = regexp.MustCompile("^[a-z]+(?:[A-Z][a-z]+)*$") + screamingSnakeCase = regexp.MustCompile("^[A-Z]+(_[A-Z]+)*$") + snakeCase = regexp.MustCompile("^[a-z]+(_[a-z]+)*$") + kebabCase = regexp.MustCompile("^[a-z]+(-[a-z]+)*$") + screamingKebabCase = regexp.MustCompile("^[A-Z]+(-[A-Z]+)*$") +) + func DetectCase(input string) Case { trim := strings.TrimSpace(input) if trim == "" { return UnknownCase } - pascalCase := regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$") - camelCase := regexp.MustCompile("^[a-z]+(?:[A-Z][a-z]+)*$") - screamingSnakeCase := regexp.MustCompile("^[A-Z]+(_[A-Z]+)*$") - snakeCase := regexp.MustCompile("^[a-z]+(_[a-z]+)*$") - kebabCase := regexp.MustCompile("^[a-z]+(-[a-z]+)*$") - screamingKebabCase := regexp.MustCompile("^[A-Z]+(-[A-Z]+)*$") if pascalCase.MatchString(trim) { return PascalCase } @@ -708,10 +713,11 @@ func CheckEnumForDuplicates(seq []*yaml.Node) []*yaml.Node { return res } +var whitespaceExp = regexp.MustCompile(`\n( +)`) + // DetermineWhitespaceLength will determine the length of the whitespace for a JSON or YAML file. func DetermineWhitespaceLength(input string) int { - exp := regexp.MustCompile(`\n( +)`) - whiteSpace := exp.FindAllStringSubmatch(input, -1) + whiteSpace := whitespaceExp.FindAllStringSubmatch(input, -1) var filtered []string for i := range whiteSpace { filtered = append(filtered, whiteSpace[i][1]) diff --git a/utils/utils_test.go b/utils/utils_test.go index 5721052c..92f7194e 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -1,11 +1,12 @@ package utils import ( - "github.com/stretchr/testify/assert" - "gopkg.in/yaml.v3" "os" "sync" "testing" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" ) type petstore []byte @@ -168,57 +169,51 @@ func TestConvertInterfaceToStringArray_NoType(t *testing.T) { } func TestConvertInterfaceToStringArray_Invalid(t *testing.T) { - var d interface{} - d = "I am a carrot" + var d interface{} = "I am a carrot" parsed := ConvertInterfaceToStringArray(d) assert.Nil(t, parsed) } func TestConvertInterfaceArrayToStringArray(t *testing.T) { - var d interface{} m := []string{"maddox", "is", "my", "little", "champion"} - d = m + var d interface{} = m parsed := ConvertInterfaceArrayToStringArray(d) assert.Equal(t, "little", parsed[3]) } func TestConvertInterfaceArrayToStringArray_NoType(t *testing.T) { - var d interface{} m := make([]interface{}, 4) m[0] = "melody" m[1] = "is" m[2] = "my" m[3] = "baby" - d = m + var d interface{} = m parsed := ConvertInterfaceArrayToStringArray(d) assert.Equal(t, "baby", parsed[3]) } func TestConvertInterfaceArrayToStringArray_Invalid(t *testing.T) { - var d interface{} - d = "weed is good" + var d interface{} = "weed is good" parsed := ConvertInterfaceArrayToStringArray(d) assert.Nil(t, parsed) } func TestExtractValueFromInterfaceMap(t *testing.T) { - var d interface{} m := make(map[string][]string) m["melody"] = []string{"is", "my", "baby"} - d = m + var d interface{} = m parsed := ExtractValueFromInterfaceMap("melody", d) assert.Equal(t, "baby", parsed.([]string)[2]) } func TestExtractValueFromInterfaceMap_NoType(t *testing.T) { - var d interface{} m := make(map[string]interface{}) n := make([]interface{}, 3) n[0] = "maddy" n[1] = "the" n[2] = "champion" m["maddy"] = n - d = m + var d interface{} = m parsed := ExtractValueFromInterfaceMap("maddy", d) assert.Equal(t, "champion", parsed.([]interface{})[2]) } @@ -229,12 +224,11 @@ func TestExtractValueFromInterfaceMap_Flat(t *testing.T) { m["maddy"] = "niblet" d = m parsed := ExtractValueFromInterfaceMap("maddy", d) - assert.Equal(t, "niblet", parsed.(interface{})) + assert.Equal(t, "niblet", parsed) } func TestExtractValueFromInterfaceMap_NotFound(t *testing.T) { - var d interface{} - d = "not a map" + var d interface{} = "not a map" parsed := ExtractValueFromInterfaceMap("melody", d) assert.Nil(t, parsed) } diff --git a/what-changed/model/components.go b/what-changed/model/components.go index 220e4e55..9941347a 100644 --- a/what-changed/model/components.go +++ b/what-changed/model/components.go @@ -190,15 +190,12 @@ func CompareComponents(l, r any) *ComponentsChanges { case v3.SchemasLabel: completedComponents++ cc.SchemaChanges = res.result.(map[string]*SchemaChanges) - break case v3.SecuritySchemesLabel: completedComponents++ cc.SecuritySchemeChanges = res.result.(map[string]*SecuritySchemeChanges) - break case v3.ResponsesLabel, v3.ParametersLabel, v3.ExamplesLabel, v3.RequestBodiesLabel, v3.HeadersLabel, v3.LinksLabel, v3.CallbacksLabel: completedComponents++ - break } } } diff --git a/what-changed/model/path_item.go b/what-changed/model/path_item.go index 06dd3d49..a7cd6904 100644 --- a/what-changed/model/path_item.go +++ b/what-changed/model/path_item.go @@ -348,25 +348,18 @@ func compareSwaggerPathItem(lPath, rPath *v2.PathItem, changes *[]*Change, pc *P switch n.label { case v3.GetLabel: pc.GetChanges = n.changes - break case v3.PutLabel: pc.PutChanges = n.changes - break case v3.PostLabel: pc.PostChanges = n.changes - break case v3.DeleteLabel: pc.DeleteChanges = n.changes - break case v3.OptionsLabel: pc.OptionsChanges = n.changes - break case v2.HeadLabel: pc.HeadChanges = n.changes - break case v2.PatchLabel: pc.PatchChanges = n.changes - break } completedOperations++ } @@ -602,28 +595,20 @@ func compareOpenAPIPathItem(lPath, rPath *v3.PathItem, changes *[]*Change, pc *P switch n.label { case v3.GetLabel: pc.GetChanges = n.changes - break case v3.PutLabel: pc.PutChanges = n.changes - break case v3.PostLabel: pc.PostChanges = n.changes - break case v3.DeleteLabel: pc.DeleteChanges = n.changes - break case v3.OptionsLabel: pc.OptionsChanges = n.changes - break case v3.HeadLabel: pc.HeadChanges = n.changes - break case v3.PatchLabel: pc.PatchChanges = n.changes - break case v3.TraceLabel: pc.TraceChanges = n.changes - break } completedOperations++ } diff --git a/what-changed/model/schema_test.go b/what-changed/model/schema_test.go index 4c891fbb..242bd780 100644 --- a/what-changed/model/schema_test.go +++ b/what-changed/model/schema_test.go @@ -5,13 +5,14 @@ package model import ( "fmt" + "testing" + "github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/stretchr/testify/assert" - "testing" ) // These tests require full documents to be tested properly. schemas are perhaps the most complex @@ -172,17 +173,19 @@ func test_BuildDocv2(l, r string) (*v2.Swagger, *v2.Swagger) { leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l)) rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r)) - var err []error + var err error var leftDoc, rightDoc *v2.Swagger leftDoc, err = v2.CreateDocument(leftInfo) + if err != nil { + fmt.Printf("error: %v\n", err) + panic("failed to create doc") + } rightDoc, err = v2.CreateDocument(rightInfo) - - if len(err) > 0 { - for i := range err { - fmt.Printf("error: %v\n", err[i]) - } + if err != nil { + fmt.Printf("error: %v\n", err) panic("failed to create doc") } + return leftDoc, rightDoc } diff --git a/what-changed/reports/summary_test.go b/what-changed/reports/summary_test.go index 0fc6c341..6ba45ea1 100644 --- a/what-changed/reports/summary_test.go +++ b/what-changed/reports/summary_test.go @@ -4,17 +4,18 @@ package reports import ( + "os" + "testing" + "github.com/pb33f/libopenapi" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/what-changed/model" "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" ) func createDiff() *model.DocumentChanges { - burgerShopOriginal, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi.yaml") - burgerShopUpdated, _ := ioutil.ReadFile("../../test_specs/burgershop.openapi-modified.yaml") + burgerShopOriginal, _ := os.ReadFile("../../test_specs/burgershop.openapi.yaml") + burgerShopUpdated, _ := os.ReadFile("../../test_specs/burgershop.openapi-modified.yaml") originalDoc, _ := libopenapi.NewDocument(burgerShopOriginal) updatedDoc, _ := libopenapi.NewDocument(burgerShopUpdated) documentChanges, _ := libopenapi.CompareDocuments(originalDoc, updatedDoc) diff --git a/what-changed/what_changed_test.go b/what-changed/what_changed_test.go index a23e9e88..3ee03204 100644 --- a/what-changed/what_changed_test.go +++ b/what-changed/what_changed_test.go @@ -5,18 +5,19 @@ package what_changed import ( "fmt" + "os" + "testing" + "github.com/pb33f/libopenapi/datamodel" v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" ) func TestCompareOpenAPIDocuments(t *testing.T) { - original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") - modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml") + original, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") + modified, _ := os.ReadFile("../test_specs/burgershop.openapi-modified.yaml") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -32,8 +33,8 @@ func TestCompareOpenAPIDocuments(t *testing.T) { func TestCompareSwaggerDocuments(t *testing.T) { - original, _ := ioutil.ReadFile("../test_specs/petstorev2-complete.yaml") - modified, _ := ioutil.ReadFile("../test_specs/petstorev2-complete-modified.yaml") + original, _ := os.ReadFile("../test_specs/petstorev2-complete.yaml") + modified, _ := os.ReadFile("../test_specs/petstorev2-complete-modified.yaml") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -51,8 +52,8 @@ func TestCompareSwaggerDocuments(t *testing.T) { func Benchmark_CompareOpenAPIDocuments(b *testing.B) { - original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") - modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml") + original, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") + modified, _ := os.ReadFile("../test_specs/burgershop.openapi-modified.yaml") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -66,8 +67,8 @@ func Benchmark_CompareOpenAPIDocuments(b *testing.B) { func Benchmark_CompareSwaggerDocuments(b *testing.B) { - original, _ := ioutil.ReadFile("../test_specs/petstorev2-complete.yaml") - modified, _ := ioutil.ReadFile("../test_specs/petstorev2-complete-modified.yaml") + original, _ := os.ReadFile("../test_specs/petstorev2-complete.yaml") + modified, _ := os.ReadFile("../test_specs/petstorev2-complete-modified.yaml") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -81,8 +82,8 @@ func Benchmark_CompareSwaggerDocuments(b *testing.B) { func Benchmark_CompareOpenAPIDocuments_NoChange(b *testing.B) { - original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") - modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + original, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") + modified, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -96,8 +97,8 @@ func Benchmark_CompareOpenAPIDocuments_NoChange(b *testing.B) { func Benchmark_CompareK8s(b *testing.B) { - original, _ := ioutil.ReadFile("../test_specs/k8s.json") - modified, _ := ioutil.ReadFile("../test_specs/k8s.json") + original, _ := os.ReadFile("../test_specs/k8s.json") + modified, _ := os.ReadFile("../test_specs/k8s.json") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -111,8 +112,8 @@ func Benchmark_CompareK8s(b *testing.B) { func Benchmark_CompareStripe(b *testing.B) { - original, _ := ioutil.ReadFile("../test_specs/stripe.yaml") - modified, _ := ioutil.ReadFile("../test_specs/stripe.yaml") + original, _ := os.ReadFile("../test_specs/stripe.yaml") + modified, _ := os.ReadFile("../test_specs/stripe.yaml") infoOrig, _ := datamodel.ExtractSpecInfo(original) infoMod, _ := datamodel.ExtractSpecInfo(modified) @@ -127,10 +128,10 @@ func Benchmark_CompareStripe(b *testing.B) { func ExampleCompareOpenAPIDocuments() { // Read in a 'left' (original) OpenAPI specification - original, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") + original, _ := os.ReadFile("../test_specs/burgershop.openapi.yaml") // Read in a 'right' (modified) OpenAPI specification - modified, _ := ioutil.ReadFile("../test_specs/burgershop.openapi-modified.yaml") + modified, _ := os.ReadFile("../test_specs/burgershop.openapi-modified.yaml") // Extract SpecInfo from bytes infoOriginal, _ := datamodel.ExtractSpecInfo(original)