From c6f0308aaf8cc5e7eeaa9dd15ec677af0b1a68e2 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 01:36:21 +0200 Subject: [PATCH 01/42] initial error handling refactoring --- datamodel/high/v2/swagger_test.go | 7 +- datamodel/high/v3/document_test.go | 24 ++- datamodel/high/v3/media_type_test.go | 10 +- datamodel/high/v3/package_test.go | 13 +- datamodel/low/v2/package_test.go | 27 ++- datamodel/low/v2/swagger.go | 17 +- datamodel/low/v2/swagger_test.go | 31 +-- datamodel/low/v3/create_document.go | 11 +- datamodel/low/v3/create_document_test.go | 38 ++-- datamodel/low/v3/examples_test.go | 13 +- datamodel/spec_info_test.go | 4 +- document.go | 213 ++++++++++----------- document_config.go | 110 +++++++++++ document_examples_test.go | 121 ++++++------ document_test.go | 232 +++++++++++------------ errors.go | 37 ++++ go.mod | 4 +- go.sum | 10 +- index/spec_index_test.go | 26 +-- internal/errorutils/errorutils.go | 65 +++++++ internal/errorutils/errorutils_test.go | 37 ++++ internal/errorutils/filter.go | 34 ++++ resolver/resolver_test.go | 24 +-- what-changed/model/schema_test.go | 17 +- what-changed/reports/summary_test.go | 9 +- what-changed/what_changed_test.go | 37 ++-- 26 files changed, 712 insertions(+), 459 deletions(-) create mode 100644 document_config.go create mode 100644 errors.go create mode 100644 internal/errorutils/errorutils.go create mode 100644 internal/errorutils/errorutils_test.go create mode 100644 internal/errorutils/filter.go 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/document_test.go b/datamodel/high/v3/document_test.go index 3c26be72..8ecac7ad 100644 --- a/datamodel/high/v3/document_test.go +++ b/datamodel/high/v3/document_test.go @@ -22,7 +22,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, @@ -383,7 +383,7 @@ func testBurgerShop(t *testing.T, h *Document, checkLines bool) { func TestStripeAsDoc(t *testing.T) { 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) d := NewDocument(lowDoc) @@ -393,7 +393,7 @@ func TestStripeAsDoc(t *testing.T) { func TestK8sAsDoc(t *testing.T) { 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) @@ -403,7 +403,7 @@ func TestK8sAsDoc(t *testing.T) { func TestAsanaAsDoc(t *testing.T) { 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") @@ -416,7 +416,7 @@ func TestAsanaAsDoc(t *testing.T) { func TestDigitalOceanAsDocFromSHA(t *testing.T) { 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{ @@ -427,9 +427,7 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) { lowDoc, err = lowv3.CreateDocumentFromConfig(info, &config) if err != nil { - for e := range err { - fmt.Println(err[e]) - } + fmt.Printf("error: %v\n", err) panic("broken something") } d := NewDocument(lowDoc) @@ -441,7 +439,7 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) { func TestPetstoreAsDoc(t *testing.T) { 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") @@ -454,7 +452,7 @@ func TestPetstoreAsDoc(t *testing.T) { func TestCircularReferencesDoc(t *testing.T) { 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) d := NewDocument(lowDoc) @@ -616,7 +614,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, @@ -670,7 +668,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, @@ -699,7 +697,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/media_type_test.go b/datamodel/high/v3/media_type_test.go index 157342f3..409c2170 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" @@ -18,9 +18,9 @@ import ( func TestMediaType_MarshalYAMLInline(t *testing.T) { // 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") @@ -108,9 +108,9 @@ example: testing a nice mutation` func TestMediaType_MarshalYAML(t *testing.T) { // 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") 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/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/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..83d63274 100644 --- a/datamodel/low/v2/swagger_test.go +++ b/datamodel/low/v2/swagger_test.go @@ -5,10 +5,11 @@ package v2 import ( "fmt" + "os" + "testing" + "github.com/pb33f/libopenapi/datamodel" "github.com/stretchr/testify/assert" - "io/ioutil" - "testing" ) var doc *Swagger @@ -17,9 +18,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 +39,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 +183,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 { @@ -200,7 +201,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 { @@ -222,7 +223,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 { @@ -240,7 +241,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 { @@ -258,7 +259,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 { @@ -276,7 +277,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 { @@ -294,7 +295,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 { @@ -312,7 +313,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 { @@ -330,7 +331,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 { @@ -344,7 +345,7 @@ func TestCreateDocument_InfoBad(t *testing.T) { 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) diff --git a/datamodel/low/v3/create_document.go b/datamodel/low/v3/create_document.go index 75cc3b24..d0f2d78f 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,7 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur go runExtraction(info, &doc, idx, f, &errs, &wg) } wg.Wait() - return &doc, errs + 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..55c97c2d 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -18,7 +18,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 { @@ -179,7 +179,7 @@ func TestCreateDocument_WebHooks_Error(t *testing.T) { $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -568,7 +568,7 @@ components: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -587,7 +587,7 @@ webhooks: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error doc, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -603,7 +603,7 @@ components: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -619,7 +619,7 @@ paths: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -633,7 +633,7 @@ tags: - $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -647,7 +647,7 @@ security: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -661,7 +661,7 @@ externalDocs: $ref: #bork` info, _ := datamodel.ExtractSpecInfo([]byte(yml)) - var err []error + var err error _, err = CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ AllowFileReferences: false, AllowRemoteReferences: false, @@ -677,16 +677,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 +730,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") } 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/spec_info_test.go b/datamodel/spec_info_test.go index 702ae147..cccae7a7 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" @@ -290,7 +290,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..fd2eb085 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,65 @@ 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, + AllowCircularReferenceResolving: 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) { + 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: defaultErrorFilter, + } + + 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) { + d.config = config - if d != nil { - d.SetConfiguration(configuration) + if config == nil { + d.errorFilter = defaultErrorFilter + return } - return d, err + + d.errorFilter = errorutils.And( + // more filters can be added here if needed + circularReferenceErrorFilter(config.AllowCircularReferenceResolving), + ) +} + +func NewDocumentWithTypeCheck(specByteArray []byte, bypassCheck bool) (Document, error) { + return NewDocument(specByteArray, WithBypassDocumentCheck(bypassCheck)) } func (d *document) GetVersion() string { @@ -151,10 +184,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 +196,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 +221,79 @@ 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, - } + return nil, ErrNoConfiguration } - 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, circularReferenceErrorFilter(d.config.AllowCircularReferenceResolving)) + 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, - } + return nil, ErrNoConfiguration } - 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 - } + lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) + err = errorutils.Filtered(err, circularReferenceErrorFilter(d.config.AllowCircularReferenceResolving)) + if err != nil { + return nil, err } + highDoc := v3high.NewDocument(lowDoc) d.highOpenAPI3Model = &DocumentModel[v3high.Document]{ Model: *highDoc, Index: lowDoc.Index, } - return d.highOpenAPI3Model, errors + return d.highOpenAPI3Model, nil } // CompareDocuments will accept a left and right Document implementing struct, build a model for the correct @@ -295,37 +302,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..cec203aa --- /dev/null +++ b/document_config.go @@ -0,0 +1,110 @@ +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 + + // AllowCircularReferences will allow circular references to be resolved. This is disabled by default. + // Will return an error in case of circular references. + AllowCircularReferenceResolving 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 + } +} diff --git a/document_examples_test.go b/document_examples_test.go index 4c3600a2..3b3541f8 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,32 @@ 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) + } 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 err != nil { + panic(fmt.Errorf("cannot create v3 model from document: %w", err)) } // print the number of paths and schemas in the document @@ -236,14 +227,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 +293,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 +337,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. @@ -426,14 +408,20 @@ components: 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 +466,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 +606,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 +632,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..0860bed8 100644 --- a/document_test.go +++ b/document_test.go @@ -4,14 +4,15 @@ package libopenapi import ( "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" "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) { @@ -22,7 +23,7 @@ func TestLoadDocument_Simple_V2(t *testing.T) { assert.Equal(t, "2.0.1", doc.GetVersion()) v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, docErr, 0) + assert.Len(t, errorutils.Unwrap(docErr), 0) assert.NotNil(t, v2Doc) assert.NotNil(t, doc.GetSpecInfo()) @@ -37,7 +38,7 @@ 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.Unwrap(docErr), 1) assert.Nil(t, v2Doc) } @@ -48,11 +49,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.Unwrap(docErr), 2) + require.Nil(t, v2Doc) } func TestLoadDocument_Simple_V3_Error(t *testing.T) { @@ -62,7 +63,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.Unwrap(docErr), 1) assert.Nil(t, v2Doc) } @@ -70,14 +71,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.Unwrap(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.Unwrap(err), 1) } func TestLoadDocument_Empty(t *testing.T) { @@ -94,7 +95,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.Unwrap(docErr), 0) assert.NotNil(t, v3Doc) } @@ -108,7 +109,7 @@ paths: assert.NoError(t, err) v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, docErr, 2) + assert.Len(t, errorutils.Unwrap(docErr), 2) assert.Nil(t, v3Doc) } @@ -154,32 +155,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 +199,7 @@ func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { } } - assert.Nil(t, errs) + assert.Nil(t, err) tc := compReport.TotalChanges() bc := compReport.TotalBreakingChanges() assert.Equal(t, 0, bc) @@ -213,12 +220,12 @@ func TestDocument_RenderAndReload_ChangeCheck_Asana(t *testing.T) { 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 +237,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,18 +286,17 @@ 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() @@ -307,14 +313,14 @@ 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_BuildModelCircular(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/circular-tests.yaml") + petstore, _ := os.ReadFile("test_specs/circular-tests.yaml") doc, _ := NewDocument(petstore) m, e := doc.BuildV3Model() assert.NotNil(t, m) @@ -322,7 +328,7 @@ func TestDocument_BuildModelCircular(t *testing.T) { } func TestDocument_BuildModelBad(t *testing.T) { - petstore, _ := ioutil.ReadFile("test_specs/badref-burgershop.openapi.yaml") + petstore, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml") doc, _ := NewDocument(petstore) m, e := doc.BuildV3Model() assert.Nil(t, m) @@ -368,14 +374,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 +389,67 @@ 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") + burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml") + burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml") originalDoc, _ := NewDocument(burgerShopOriginal) updatedDoc, _ := NewDocument(burgerShopUpdated) - changes, errors := CompareDocuments(originalDoc, updatedDoc) - assert.Len(t, errors, 9) + changes, err := CompareDocuments(originalDoc, updatedDoc) + assert.Len(t, err, 9) assert.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) + changes, err := CompareDocuments(updatedDoc, originalDoc) + assert.Len(t, err, 9) assert.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) + changes, err := CompareDocuments(updatedDoc, originalDoc) + assert.Len(t, errorutils.Unwrap(err), 2) assert.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) + 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) + assert.Len(t, errorutils.Unwrap(err), 1) assert.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 +502,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 +535,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,14 +559,10 @@ 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() @@ -597,15 +585,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 +613,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 +640,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 +665,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..1805848d --- /dev/null +++ b/errors.go @@ -0,0 +1,37 @@ +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 defaultErrorFilter(err error) bool { + return true +} + +// 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(refAllowed bool) func(error) (keep bool) { + return func(err error) bool { + if refErr, ok := err.(*resolver.ResolvingError); ok { + if refAllowed && refErr.CircularReference != nil { + // allowed & is ref -> skip + return false + } else if !refAllowed && refErr.CircularReference != nil { + // not allowed + is ref -> keep + return true + } + // some other error -> keep + return true + } + return true + } +} diff --git a/go.mod b/go.mod index ba8ef189..a24793c3 100644 --- a/go.mod +++ b/go.mod @@ -3,9 +3,9 @@ module github.com/pb33f/libopenapi go 1.20 require ( - github.com/stretchr/testify v1.8.0 + github.com/stretchr/testify v1.8.4 github.com/vmware-labs/yaml-jsonpath v0.3.2 - golang.org/x/sync v0.1.0 + golang.org/x/sync v0.3.0 gopkg.in/yaml.v3 v3.0.1 ) diff --git a/go.sum b/go.sum index 67087946..397ccd4a 100644 --- a/go.sum +++ b/go.sum @@ -52,12 +52,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= @@ -76,8 +74,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/index/spec_index_test.go b/index/spec_index_test.go index 340b2cc5..532a213a 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/internal/errorutils/errorutils.go b/internal/errorutils/errorutils.go new file mode 100644 index 00000000..2373a0f3 --- /dev/null +++ b/internal/errorutils/errorutils.go @@ -0,0 +1,65 @@ +package errorutils + +type MultiError struct { + errs []error +} + +func (e *MultiError) Error() string { + var b []byte + for i, err := range e.errs { + if i > 0 { + b = append(b, '\n') + } + b = append(b, err.Error()...) + } + return string(b) +} + +func (e *MultiError) Unwrap() []error { + return e.errs +} + +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 { + result.errs = append(result.errs, err) + } + } + return &result +} + +// Unwrap recursively unwraps errors and flattens them into a slice of errors. +func Unwrap(err error) []error { + if err == nil { + return nil + } + var result []error + if unwrap, ok := err.(interface{ Unwrap() []error }); ok { + // ignore wrapping error - no hierarchy + for _, e := range unwrap.Unwrap() { + result = append(result, Unwrap(e)...) + } + return result + } else if unwrap, ok := err.(interface{ Unwrap() error }); ok { + // add parent error to result + result = append(result, err) + result = append(result, Unwrap(unwrap.Unwrap())...) + return result + } + // no unwrapping needed, as it's not wrapped + 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..31ffdd34 --- /dev/null +++ b/internal/errorutils/errorutils_test.go @@ -0,0 +1,37 @@ +package errorutils + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestUnwrapErrors(t *testing.T) { + err := Join(errors.New("foo"), errors.New("bar")) + + errs := Unwrap(err) + require.Len(t, errs, 2) +} + +func TestUnwrapError(t *testing.T) { + err := fmt.Errorf("foo: %w", errors.New("bar")) + + errs := Unwrap(err) + require.Len(t, errs, 2) +} + +func TestUnwrapHierarchyError(t *testing.T) { + err1 := fmt.Errorf("foo: %w", errors.New("bar")) + err2 := Join(nil, fmt.Errorf("barr: %w", errors.New("fo"))) + err := Join(err1, err2) + + errs := Unwrap(err) + require.Len(t, errs, 4) +} + +func TestJoinNils(t *testing.T) { + err := Join(nil, nil) + require.Nil(t, err) +} diff --git a/internal/errorutils/filter.go b/internal/errorutils/filter.go new file mode 100644 index 00000000..6277e540 --- /dev/null +++ b/internal/errorutils/filter.go @@ -0,0 +1,34 @@ +package errorutils + +func Filtered(err error, filters ...func(error) (keep bool)) error { + if err == nil { + return nil + } + errs := Unwrap(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 + for _, err := range errs { + if filter(err) { + result = append(result, err) + } + } + return result +} + +func And(filters ...func(error) (keep bool)) func(error) (keep bool) { + return func(err error) bool { + for _, filter := range filters { + if !filter(err) { + return false + } + } + return true + } +} diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 1db450ec..6400e7bc 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -3,8 +3,8 @@ package resolver import ( "errors" "fmt" - "io/ioutil" "net/url" + "os" "testing" "github.com/pb33f/libopenapi/index" @@ -17,7 +17,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 +28,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 +45,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 +64,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) @@ -89,7 +89,7 @@ func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) { } 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 +106,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 +133,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 +150,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 +261,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 +282,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 +301,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/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 4fb40200..9f7d070a 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 2f13ac9f..c51a4787 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) From 1cc2a4009cce6c9553f9c200bd94e73cb6290120 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 01:55:27 +0200 Subject: [PATCH 02/42] fix some tests --- document_test.go | 67 ++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 28 deletions(-) diff --git a/document_test.go b/document_test.go index 0860bed8..582ede90 100644 --- a/document_test.go +++ b/document_test.go @@ -19,13 +19,14 @@ 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, errorutils.Unwrap(docErr), 0) - assert.NotNil(t, v2Doc) - assert.NotNil(t, doc.GetSpecInfo()) + require.Nil(t, docErr) + require.Len(t, errorutils.Unwrap(docErr), 0) + require.NotNil(t, v2Doc) + require.NotNil(t, doc.GetSpecInfo()) fmt.Print() @@ -301,8 +302,9 @@ func TestDocument_BuildModelPreBuild(t *testing.T) { assert.NoError(t, e) doc.BuildV3Model() doc.BuildV3Model() - _, _, _, er := doc.RenderAndReload() - assert.Len(t, er, 0) + _, _, _, err := doc.RenderAndReload() + assert.Nil(t, err) + assert.Len(t, errorutils.Unwrap(err), 0) } func TestDocument_AnyDoc(t *testing.T) { @@ -321,18 +323,20 @@ func TestDocument_AnyDocWithConfig(t *testing.T) { func TestDocument_BuildModelCircular(t *testing.T) { petstore, _ := os.ReadFile("test_specs/circular-tests.yaml") - doc, _ := NewDocument(petstore) - m, e := doc.BuildV3Model() + doc, err := NewDocument(petstore) + assert.NoError(t, err) + m, err := doc.BuildV3Model() assert.NotNil(t, m) - assert.Len(t, e, 3) + assert.Len(t, errorutils.Unwrap(err), 3) } func TestDocument_BuildModelBad(t *testing.T) { petstore, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml") - doc, _ := NewDocument(petstore) - m, e := doc.BuildV3Model() + 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.Unwrap(err), 9) } func TestDocument_Serialize_JSON_Modified(t *testing.T) { @@ -344,9 +348,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. @@ -391,11 +397,17 @@ paths: func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) { burgerShopOriginal, _ := os.ReadFile("test_specs/badref-burgershop.openapi.yaml") burgerShopUpdated, _ := os.ReadFile("test_specs/burgershop.openapi-modified.yaml") - originalDoc, _ := NewDocument(burgerShopOriginal) - updatedDoc, _ := NewDocument(burgerShopUpdated) + originalDoc, _ := NewDocument(burgerShopOriginal, + WithAllowRemoteReferences(true), + WithAllowFileReferences(true), + ) + updatedDoc, _ := NewDocument(burgerShopUpdated, + WithAllowRemoteReferences(true), + WithAllowFileReferences(true), + ) changes, err := CompareDocuments(originalDoc, updatedDoc) - assert.Len(t, err, 9) - assert.Nil(t, changes) + require.Len(t, errorutils.Unwrap(err), 9) + require.Nil(t, changes) } func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) { @@ -405,9 +417,8 @@ func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) { originalDoc, _ := NewDocument(burgerShopOriginal) updatedDoc, _ := NewDocument(burgerShopUpdated) changes, err := CompareDocuments(updatedDoc, originalDoc) - assert.Len(t, err, 9) - assert.Nil(t, changes) - + require.Len(t, errorutils.Unwrap(err), 9) + require.Nil(t, changes) } func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) { @@ -417,8 +428,8 @@ func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) { originalDoc, _ := NewDocument(burgerShopOriginal) updatedDoc, _ := NewDocument(burgerShopUpdated) changes, err := CompareDocuments(updatedDoc, originalDoc) - assert.Len(t, errorutils.Unwrap(err), 2) - assert.Nil(t, changes) + require.Len(t, errorutils.Unwrap(err), 2) + require.Nil(t, changes) } @@ -434,9 +445,8 @@ func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) { require.NoError(t, err) changes, err := CompareDocuments(updatedDoc, originalDoc) - assert.Len(t, errorutils.Unwrap(err), 1) - assert.Nil(t, changes) - + require.Len(t, errorutils.Unwrap(err), 1) + require.Nil(t, changes) } func TestSchemaRefIsFollowed(t *testing.T) { @@ -565,7 +575,8 @@ components: 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) } From bd65eb90c58be2f4ad9d3f07f6f67dae92d1a936 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 01:55:46 +0200 Subject: [PATCH 03/42] use filter func that is initialized only once --- document.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/document.go b/document.go index fd2eb085..f1fca59c 100644 --- a/document.go +++ b/document.go @@ -250,7 +250,7 @@ func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { } lowDoc, err := v2low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) - err = errorutils.Filtered(err, circularReferenceErrorFilter(d.config.AllowCircularReferenceResolving)) + err = errorutils.Filtered(err, d.errorFilter) if err != nil { return nil, err } @@ -283,7 +283,7 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { } lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) - err = errorutils.Filtered(err, circularReferenceErrorFilter(d.config.AllowCircularReferenceResolving)) + err = errorutils.Filtered(err, d.errorFilter) if err != nil { return nil, err } From 82f80e37f6236c4581679eb4db9b569caafb418e Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 02:10:21 +0200 Subject: [PATCH 04/42] return broken object upon error as expected in the tests --- .gitignore | 3 ++- document.go | 10 +--------- document_config.go | 7 +++++++ document_test.go | 16 +++++++++------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/.gitignore b/.gitignore index 069611e7..66362429 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ test-operation.yaml .idea/ -*.iml \ No newline at end of file +*.iml +__debug_bin* diff --git a/document.go b/document.go index f1fca59c..20f56875 100644 --- a/document.go +++ b/document.go @@ -251,10 +251,6 @@ func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { 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, @@ -284,16 +280,12 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) err = errorutils.Filtered(err, d.errorFilter) - if err != nil { - return nil, err - } - highDoc := v3high.NewDocument(lowDoc) d.highOpenAPI3Model = &DocumentModel[v3high.Document]{ Model: *highDoc, Index: lowDoc.Index, } - return d.highOpenAPI3Model, nil + return d.highOpenAPI3Model, err } // CompareDocuments will accept a left and right Document implementing struct, build a model for the correct diff --git a/document_config.go b/document_config.go index cec203aa..0f299ab8 100644 --- a/document_config.go +++ b/document_config.go @@ -108,3 +108,10 @@ func WithBypassDocumentCheck(bypass bool) ConfigurationOption { return nil } } + +func WithAllowCircularReferenceResolving(allow bool) ConfigurationOption { + return func(o *Configuration) error { + o.AllowCircularReferenceResolving = allow + return nil + } +} diff --git a/document_test.go b/document_test.go index 582ede90..762e5e39 100644 --- a/document_test.go +++ b/document_test.go @@ -177,7 +177,7 @@ func TestDocument_RenderAndReload_ChangeCheck_Burgershop(t *testing.T) { func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { bs, _ := os.ReadFile("test_specs/stripe.yaml") - doc, err := NewDocument(bs) + doc, err := NewDocument(bs, WithAllowCircularReferenceResolving(true)) require.NoError(t, err) _, err = doc.BuildV3Model() require.NoError(t, err) @@ -208,14 +208,14 @@ 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) @@ -300,9 +300,11 @@ func TestDocument_BuildModelPreBuild(t *testing.T) { petstore, _ := os.ReadFile("test_specs/petstorev3.json") doc, e := NewDocument(petstore) assert.NoError(t, e) - doc.BuildV3Model() - doc.BuildV3Model() - _, _, _, err := doc.RenderAndReload() + _, 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.Unwrap(err), 0) } @@ -324,7 +326,7 @@ func TestDocument_AnyDocWithConfig(t *testing.T) { func TestDocument_BuildModelCircular(t *testing.T) { petstore, _ := os.ReadFile("test_specs/circular-tests.yaml") doc, err := NewDocument(petstore) - assert.NoError(t, err) + require.NoError(t, err) m, err := doc.BuildV3Model() assert.NotNil(t, m) assert.Len(t, errorutils.Unwrap(err), 3) From a3eb5262bd7a4a6f83f0d1e292c7ff0b11ee2811 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 02:32:56 +0200 Subject: [PATCH 05/42] fix example --- document_examples_test.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/document_examples_test.go b/document_examples_test.go index 3b3541f8..290a4977 100644 --- a/document_examples_test.go +++ b/document_examples_test.go @@ -185,17 +185,16 @@ func ExampleNewDocument_fromUnknownVersion() { if document.GetSpecInfo().SpecType == utils.OpenApi2 { v2Model, err := document.BuildV2Model() if err != nil { - errs = append(errs) + 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 err != nil { - panic(fmt.Errorf("cannot create v3 model from document: %w", err)) + 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 From 8bc8538de6972faa93c461e35824633cfd877c6e Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 02:33:39 +0200 Subject: [PATCH 06/42] update lower level tests --- datamodel/low/v3/create_document_test.go | 29 ++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 55c97c2d..e715c49a 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -7,7 +7,9 @@ import ( "github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel/low/base" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var doc *Document @@ -46,7 +48,7 @@ func BenchmarkCreateDocument_Circular(b *testing.B) { AllowRemoteReferences: false, }) if err != nil { - panic("this should not error") + panic(fmt.Errorf("this should not error: %w", err)) } } } @@ -76,8 +78,10 @@ func TestCircularReferenceError(t *testing.T) { AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.NotNil(t, circDoc) - assert.Len(t, err, 3) + assert.Len(t, errorutils.Unwrap(err), 3) + // do not rely on potentially broken data when an error is returned. + // one should never rely on a returned pointer in case of an error. + assert.Nil(t, circDoc) } func BenchmarkCreateDocument_Stripe(b *testing.B) { @@ -118,7 +122,7 @@ func TestCreateDocumentStripe(t *testing.T) { AllowRemoteReferences: false, BasePath: "/here", }) - assert.Len(t, err, 3) + assert.Len(t, errorutils.Unwrap(err), 3) assert.Equal(t, "3.0.0", d.Version.Value) assert.Equal(t, "Stripe API", d.Info.Value.Title.Value) @@ -184,7 +188,7 @@ func TestCreateDocument_WebHooks_Error(t *testing.T) { AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_Servers(t *testing.T) { @@ -573,7 +577,8 @@ components: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 0) + require.NoError(t, err) + require.Len(t, errorutils.Unwrap(err), 0) ob := doc.Components.Value.FindSchema("bork").Value ob.Schema() @@ -592,7 +597,7 @@ webhooks: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_Components_Error_Extract(t *testing.T) { @@ -608,7 +613,7 @@ components: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } @@ -624,7 +629,7 @@ paths: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_Tags_Errors(t *testing.T) { @@ -638,7 +643,7 @@ tags: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_Security_Error(t *testing.T) { @@ -652,7 +657,7 @@ security: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_ExternalDoc_Error(t *testing.T) { @@ -666,7 +671,7 @@ externalDocs: AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, err, 1) + require.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_YamlAnchor(t *testing.T) { From 23ca2ac8c95905b57afc3e1da8d55c78110b8216 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 02:34:03 +0200 Subject: [PATCH 07/42] do not return objects with errors --- datamodel/low/v3/create_document.go | 8 +++++++- document.go | 7 +++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/datamodel/low/v3/create_document.go b/datamodel/low/v3/create_document.go index d0f2d78f..462e62db 100644 --- a/datamodel/low/v3/create_document.go +++ b/datamodel/low/v3/create_document.go @@ -110,7 +110,13 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur go runExtraction(info, &doc, idx, f, &errs, &wg) } wg.Wait() - return &doc, errorutils.Join(errs...) + + err := errorutils.Join(errs...) + if err != nil { + return nil, err + } + + return &doc, nil } func extractInfo(info *datamodel.SpecInfo, doc *Document, idx *index.SpecIndex) error { diff --git a/document.go b/document.go index 20f56875..90162218 100644 --- a/document.go +++ b/document.go @@ -251,6 +251,9 @@ func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { 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, @@ -280,6 +283,10 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) err = errorutils.Filtered(err, d.errorFilter) + if err != nil { + return nil, err + } + highDoc := v3high.NewDocument(lowDoc) d.highOpenAPI3Model = &DocumentModel[v3high.Document]{ Model: *highDoc, From f64b8689b587478064f15bc95ca2d9e2c6b18f39 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 04:49:49 +0200 Subject: [PATCH 08/42] fix tests, revert to returning objects even tho we have errors (circular reference) --- datamodel/high/v3/document_test.go | 7 ++-- datamodel/low/reference.go | 1 + datamodel/low/v2/swagger_test.go | 21 ++++++------ datamodel/low/v3/create_document.go | 8 ++--- datamodel/low/v3/create_document_test.go | 35 +++++++++++--------- document.go | 2 +- document_test.go | 9 +++-- errors.go | 29 ++++++++++++---- internal/errorutils/errorutils_test.go | 10 ++++-- internal/errorutils/filter.go | 4 +-- internal/errorutils/mapper.go | 42 ++++++++++++++++++++++++ 11 files changed, 116 insertions(+), 52 deletions(-) create mode 100644 internal/errorutils/mapper.go diff --git a/datamodel/high/v3/document_test.go b/datamodel/high/v3/document_test.go index 8ecac7ad..50ca92ba 100644 --- a/datamodel/high/v3/document_test.go +++ b/datamodel/high/v3/document_test.go @@ -14,6 +14,7 @@ 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" ) @@ -385,7 +386,7 @@ func TestStripeAsDoc(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(data) var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) - assert.Len(t, err, 3) + assert.Len(t, errorutils.Unwrap(err), 3) d := NewDocument(lowDoc) assert.NotNil(t, d) } @@ -396,7 +397,7 @@ func TestK8sAsDoc(t *testing.T) { var err error lowSwag, err := lowv2.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) d := v2.NewSwaggerDocument(lowSwag) - assert.Len(t, err, 0) + assert.Len(t, errorutils.Unwrap(err), 0) assert.NotNil(t, d) } @@ -454,7 +455,7 @@ func TestCircularReferencesDoc(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(data) var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) - assert.Len(t, err, 3) + assert.Len(t, errorutils.Unwrap(err), 3) d := NewDocument(lowDoc) assert.Len(t, d.Components.Schemas, 9) assert.Len(t, d.Index.GetCircularReferences(), 3) diff --git a/datamodel/low/reference.go b/datamodel/low/reference.go index 30b75d80..3a7dcda0 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" diff --git a/datamodel/low/v2/swagger_test.go b/datamodel/low/v2/swagger_test.go index 83d63274..8ca3ea93 100644 --- a/datamodel/low/v2/swagger_test.go +++ b/datamodel/low/v2/swagger_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/pb33f/libopenapi/datamodel" + "github.com/pb33f/libopenapi/internal/errorutils" "github.com/stretchr/testify/assert" ) @@ -192,7 +193,7 @@ func TestCreateDocument_ExternalDocsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_TagsBad(t *testing.T) { @@ -210,7 +211,7 @@ func TestCreateDocument_TagsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_PathsBad(t *testing.T) { @@ -232,7 +233,7 @@ func TestCreateDocument_PathsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_SecurityBad(t *testing.T) { @@ -250,7 +251,7 @@ func TestCreateDocument_SecurityBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { @@ -268,7 +269,7 @@ func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_ResponsesBad(t *testing.T) { @@ -286,7 +287,7 @@ func TestCreateDocument_ResponsesBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_ParametersBad(t *testing.T) { @@ -304,7 +305,7 @@ func TestCreateDocument_ParametersBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_DefinitionsBad(t *testing.T) { @@ -322,7 +323,7 @@ func TestCreateDocument_DefinitionsBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCreateDocument_InfoBad(t *testing.T) { @@ -340,7 +341,7 @@ func TestCreateDocument_InfoBad(t *testing.T) { wait = false } } - assert.Len(t, err, 1) + assert.Len(t, errorutils.Unwrap(err), 1) } func TestCircularReferenceError(t *testing.T) { @@ -349,6 +350,6 @@ func TestCircularReferenceError(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(data) circDoc, err := CreateDocument(info) assert.NotNil(t, circDoc) - assert.Len(t, err, 3) + assert.Len(t, errorutils.Unwrap(err), 3) } diff --git a/datamodel/low/v3/create_document.go b/datamodel/low/v3/create_document.go index 462e62db..43b71589 100644 --- a/datamodel/low/v3/create_document.go +++ b/datamodel/low/v3/create_document.go @@ -111,12 +111,8 @@ func createDocument(info *datamodel.SpecInfo, config *datamodel.DocumentConfigur } wg.Wait() - err := errorutils.Join(errs...) - if err != nil { - return nil, err - } - - return &doc, nil + // 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 e715c49a..a5ab4ccf 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -1,6 +1,7 @@ package v3 import ( + "errors" "fmt" "os" "testing" @@ -8,6 +9,7 @@ import ( "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" ) @@ -47,9 +49,7 @@ func BenchmarkCreateDocument_Circular(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic(fmt.Errorf("this should not error: %w", err)) - } + panicOnUnknown(err) } } @@ -64,9 +64,7 @@ func BenchmarkCreateDocument_k8s(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } @@ -79,9 +77,9 @@ func TestCircularReferenceError(t *testing.T) { AllowRemoteReferences: false, }) assert.Len(t, errorutils.Unwrap(err), 3) - // do not rely on potentially broken data when an error is returned. - // one should never rely on a returned pointer in case of an error. - assert.Nil(t, circDoc) + + // lower level packages return errors and potentially a document. + assert.NotNil(t, circDoc) } func BenchmarkCreateDocument_Stripe(b *testing.B) { @@ -93,9 +91,7 @@ func BenchmarkCreateDocument_Stripe(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } @@ -107,9 +103,7 @@ func BenchmarkCreateDocument_Petstore(b *testing.B) { AllowFileReferences: false, AllowRemoteReferences: false, }) - if err != nil { - panic("this should not error") - } + panicOnUnknown(err) } } @@ -750,3 +744,14 @@ func ExampleCreateDocument() { fmt.Print(document.Info.Value.Contact.Value.Email.Value) // Output: apiteam@swagger.io } + +func panicOnUnknown(err error) { + for _, err := range errorutils.Unwrap(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/document.go b/document.go index 90162218..aaedf5a6 100644 --- a/document.go +++ b/document.go @@ -166,7 +166,7 @@ func (d *document) SetConfiguration(config *Configuration) { return } - d.errorFilter = errorutils.And( + d.errorFilter = errorutils.AndFilter( // more filters can be added here if needed circularReferenceErrorFilter(config.AllowCircularReferenceResolving), ) diff --git a/document_test.go b/document_test.go index 762e5e39..892e6462 100644 --- a/document_test.go +++ b/document_test.go @@ -27,9 +27,6 @@ func TestLoadDocument_Simple_V2(t *testing.T) { require.Len(t, errorutils.Unwrap(docErr), 0) require.NotNil(t, v2Doc) require.NotNil(t, doc.GetSpecInfo()) - - fmt.Print() - } func TestLoadDocument_Simple_V2_Error(t *testing.T) { @@ -200,7 +197,6 @@ func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { } } - assert.Nil(t, err) tc := compReport.TotalChanges() bc := compReport.TotalBreakingChanges() assert.Equal(t, 0, bc) @@ -328,7 +324,10 @@ func TestDocument_BuildModelCircular(t *testing.T) { doc, err := NewDocument(petstore) require.NoError(t, err) m, err := doc.BuildV3Model() - assert.NotNil(t, m) + + // top level library does not return broken objects + // with an error, only one or the other + assert.Nil(t, m) assert.Len(t, errorutils.Unwrap(err), 3) } diff --git a/errors.go b/errors.go index 1805848d..661e2abc 100644 --- a/errors.go +++ b/errors.go @@ -16,22 +16,37 @@ func defaultErrorFilter(err error) bool { return true } +func isCircularErr(err error) bool { + if err == nil { + return false + } + + 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(refAllowed bool) func(error) (keep bool) { return func(err error) bool { - if refErr, ok := err.(*resolver.ResolvingError); ok { - if refAllowed && refErr.CircularReference != nil { - // allowed & is ref -> skip + if err == nil { + return false + } + + if isCircularErr(err) { + if refAllowed { return false - } else if !refAllowed && refErr.CircularReference != nil { - // not allowed + is ref -> keep + } else { return true } - // some other error -> keep - return true } + + // keep unknown error return true } } diff --git a/internal/errorutils/errorutils_test.go b/internal/errorutils/errorutils_test.go index 31ffdd34..6b4e87aa 100644 --- a/internal/errorutils/errorutils_test.go +++ b/internal/errorutils/errorutils_test.go @@ -23,9 +23,13 @@ func TestUnwrapError(t *testing.T) { } func TestUnwrapHierarchyError(t *testing.T) { - err1 := fmt.Errorf("foo: %w", errors.New("bar")) - err2 := Join(nil, fmt.Errorf("barr: %w", errors.New("fo"))) - err := Join(err1, err2) + 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 := Unwrap(err) require.Len(t, errs, 4) diff --git a/internal/errorutils/filter.go b/internal/errorutils/filter.go index 6277e540..9207297a 100644 --- a/internal/errorutils/filter.go +++ b/internal/errorutils/filter.go @@ -5,7 +5,7 @@ func Filtered(err error, filters ...func(error) (keep bool)) error { return nil } errs := Unwrap(err) - filtered := Filter(errs, And(filters...)) + filtered := Filter(errs, AndFilter(filters...)) if len(filtered) == 0 { return nil } @@ -22,7 +22,7 @@ func Filter(errs []error, filter func(error) (keep bool)) []error { return result } -func And(filters ...func(error) (keep bool)) func(error) (keep bool) { +func AndFilter(filters ...func(error) (keep bool)) func(error) (keep bool) { return func(err error) bool { for _, filter := range filters { if !filter(err) { diff --git a/internal/errorutils/mapper.go b/internal/errorutils/mapper.go new file mode 100644 index 00000000..eebb471a --- /dev/null +++ b/internal/errorutils/mapper.go @@ -0,0 +1,42 @@ +package errorutils + +func Mapped(err error, mapper ...func(src error) (dst error, keep bool)) error { + if err == nil { + return nil + } + errs := Unwrap(err) + mapped := Map(errs, AndMapper(mapper...)) + if len(mapped) == 0 { + return nil + } + return Join(mapped...) +} + +func Map(errs []error, mapper func(src error) (dst error, keep bool)) []error { + var result []error + for _, err := range errs { + dst, keep := mapper(err) + if keep { + result = append(result, dst) + } + } + return result +} + +func AndMapper(mappers ...func(error) (error, bool)) func(error) (error, bool) { + return func(srcErr error) (error, bool) { + var ( + dstErr = srcErr + keep bool + ) + for _, mapper := range mappers { + + dstErr, keep = mapper(dstErr) + if !keep { + return nil, false + } + } + // final result to keep + return dstErr, true + } +} From 18902b98a550da35178080ec6f9a416d39285130 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 14:49:05 +0200 Subject: [PATCH 09/42] update ci go version to match go.mod version --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index d8b47544..6c3a3cf5 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -16,7 +16,7 @@ jobs: - name: Set up Go 1.x uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: 1.20 id: go - name: Checkout code From b11b31ad0b1d2cb0b32afdebf5bb333ca59573ba Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 15:38:49 +0200 Subject: [PATCH 10/42] harden ci pipeline --- .github/workflows/build.yaml | 101 +++++++++++++++++++++++------------ 1 file changed, 67 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 6c3a3cf5..12c71998 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -1,49 +1,82 @@ -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.20 - 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: Build + run: go build ./... + + - name: Test & Code Coverage + run: go test ./... -v -timeout 20m -race -count=1 -covermode=atomic -coverprofile=coverage.txt + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.xml - flags: unittests + files: ./coverage.txt fail_ci_if_error: false verbose: true + From beed338ad1b36ce8ca0c2b566b098f23a4750531 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 15:40:31 +0200 Subject: [PATCH 11/42] add dependabot --- .github/dependabot.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/dependabot.yaml 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 From 6b2595b1c3ccd7d7a8a4c4ddaa332f9b14c3fb93 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 17:50:34 +0200 Subject: [PATCH 12/42] go fmt ./... --- datamodel/high/base/schema_test.go | 34 +- datamodel/low/base/schema_test.go | 34 +- index/find_component.go | 850 ++++++++++++++--------------- 3 files changed, 459 insertions(+), 459 deletions(-) diff --git a/datamodel/high/base/schema_test.go b/datamodel/high/base/schema_test.go index a42c8061..d2d31aed 100644 --- a/datamodel/high/base/schema_test.go +++ b/datamodel/high/base/schema_test.go @@ -499,10 +499,10 @@ required: [cake, fish]` assert.NotNil(t, compiled) assert.Nil(t, schemaProxy.GetBuildError()) - assert.True(t, compiled.ExclusiveMaximum.A) - assert.Equal(t, float64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum.B) - assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B) - assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) + assert.True(t, compiled.ExclusiveMaximum.A) + assert.Equal(t, float64(123), compiled.Properties["somethingB"].Schema().ExclusiveMinimum.B) + assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B) + assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description) @@ -587,25 +587,25 @@ type: number } func TestSchemaNumberMultipleOfInt(t *testing.T) { - yml := ` + yml := ` type: number multipleOf: 5 ` highSchema := getHighSchema(t, yml) value := float64(5) - assert.EqualValues(t, &value, highSchema.MultipleOf) + assert.EqualValues(t, &value, highSchema.MultipleOf) } func TestSchemaNumberMultipleOfFloat(t *testing.T) { - yml := ` + yml := ` type: number multipleOf: 0.5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - value := 0.5 - assert.EqualValues(t, &value, highSchema.MultipleOf) + value := 0.5 + assert.EqualValues(t, &value, highSchema.MultipleOf) } func TestSchemaNumberMinimumInt(t *testing.T) { @@ -616,17 +616,17 @@ minimum: 5 highSchema := getHighSchema(t, yml) value := float64(5) - assert.EqualValues(t, &value, highSchema.Minimum) + assert.EqualValues(t, &value, highSchema.Minimum) } func TestSchemaNumberMinimumFloat(t *testing.T) { - yml := ` + yml := ` type: number minimum: 0.5 ` - highSchema := getHighSchema(t, yml) + highSchema := getHighSchema(t, yml) - value := 0.5 + value := 0.5 assert.EqualValues(t, &value, highSchema.Minimum) } @@ -638,7 +638,7 @@ minimum: 0 highSchema := getHighSchema(t, yml) value := float64(0) - assert.EqualValues(t, &value, highSchema.Minimum) + assert.EqualValues(t, &value, highSchema.Minimum) } func TestSchemaNumberExclusiveMinimum(t *testing.T) { @@ -661,7 +661,7 @@ maximum: 5 highSchema := getHighSchema(t, yml) value := float64(5) - assert.EqualValues(t, &value, highSchema.Maximum) + assert.EqualValues(t, &value, highSchema.Maximum) } func TestSchemaNumberMaximumZero(t *testing.T) { @@ -672,7 +672,7 @@ maximum: 0 highSchema := getHighSchema(t, yml) value := float64(0) - assert.EqualValues(t, &value, highSchema.Maximum) + assert.EqualValues(t, &value, highSchema.Maximum) } func TestSchemaNumberExclusiveMaximum(t *testing.T) { diff --git a/datamodel/low/base/schema_test.go b/datamodel/low/base/schema_test.go index ab1382dc..2c351fdd 100644 --- a/datamodel/low/base/schema_test.go +++ b/datamodel/low/base/schema_test.go @@ -415,23 +415,23 @@ 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) { 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 } From 1a740b7098f5128c6607fde54f467e3f4d67d0a3 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:08:12 +0200 Subject: [PATCH 13/42] fix some linter warnings --- datamodel/high/node_builder.go | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) 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. From 1d14787acc99dfbd7177ffa40ede11d148a96922 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:08:57 +0200 Subject: [PATCH 14/42] remove redundant break statements --- what-changed/model/components.go | 3 --- what-changed/model/path_item.go | 15 --------------- 2 files changed, 18 deletions(-) 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++ } From f0d8af046a59653aeee8e8dd27420aeabc8a2803 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:09:37 +0200 Subject: [PATCH 15/42] compile regex only once --- utils/utils.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index e384906e..596d286e 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+)\]$`) + pathCharRegex = 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 pathCharRegex.MatchString(segs[i]) { segs[i], _ = url.QueryUnescape(strings.ReplaceAll(segs[i], "~1", "/")) segs[i] = fmt.Sprintf("['%s']", segs[i]) if len(cleaned) > 0 { From 2af5be83aa6f118354a058a21a32471795962fa1 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:10:52 +0200 Subject: [PATCH 16/42] use copy intead of for loop --- index/extract_refs.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/index/extract_refs.go b/index/extract_refs.go index 8346eb76..0240d03c 100644 --- a/index/extract_refs.go +++ b/index/extract_refs.go @@ -6,9 +6,10 @@ package index import ( "errors" "fmt" + "strings" + "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" - "strings" ) // ExtractRefs will return a deduplicated slice of references for every unique ref found in the document. @@ -111,9 +112,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 From 3e55a4162e1ed5a1c45b4f9dbb1378f76321de64 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:26:51 +0200 Subject: [PATCH 17/42] fix some linter warnings --- datamodel/high/base/schema.go | 2 +- datamodel/high/v3/components.go | 3 +-- datamodel/low/model_builder_test.go | 6 ++++-- datamodel/low/reference.go | 5 +---- datamodel/low/v2/path_item.go | 21 +++++++++++---------- datamodel/low/v3/path_item.go | 16 ++++++++-------- 6 files changed, 26 insertions(+), 27 deletions(-) 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/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/low/model_builder_test.go b/datamodel/low/model_builder_test.go index 2dbeac90..b59345df 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 { @@ -379,6 +380,7 @@ func TestHandleSlicesOfBools(t *testing.T) { func TestSetField_Ignore(t *testing.T) { type Complex struct { + //lint:ignore U1000 only used in test name string } type internal struct { diff --git a/datamodel/low/reference.go b/datamodel/low/reference.go index 3a7dcda0..b943bf71 100644 --- a/datamodel/low/reference.go +++ b/datamodel/low/reference.go @@ -259,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/v2/path_item.go b/datamodel/low/v2/path_item.go index b98517d5..f2613f7e 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/v3/path_item.go b/datamodel/low/v3/path_item.go index ffa83aa4..2e5746c7 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. } From 987651d0be1060c91be3e1f1ebd8f60305f66557 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:33:49 +0200 Subject: [PATCH 18/42] fix some linter warnings --- utils/type_check.go | 14 +++++++------- utils/utils_test.go | 28 +++++++++++----------------- 2 files changed, 18 insertions(+), 24 deletions(-) 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_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) } From 06876cbe47a07f89bc38d25b45548c4e84e52625 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:38:39 +0200 Subject: [PATCH 19/42] compile regex only once --- utils/utils.go | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/utils/utils.go b/utils/utils.go index 596d286e..95087303 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -563,7 +563,7 @@ func IsHttpVerb(verb string) bool { var ( // define bracket name expression bracketNameExp = regexp.MustCompile(`^(\w+)\[(\w+)\]$`) - pathCharRegex = regexp.MustCompile(`[%=;~.]`) + pathCharExp = regexp.MustCompile(`[%=;~.]`) ) func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { @@ -574,7 +574,7 @@ func ConvertComponentIdIntoFriendlyPathSearch(id string) (string, string) { // check for strange spaces, chars and if found, wrap them up, clean them and create a new cleaned path. for i := range segs { - if pathCharRegex.MatchString(segs[i]) { + 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 { @@ -612,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 { @@ -663,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 } @@ -711,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]) From 5438afe7b404ccd9e9b3ed707c432273096556ad Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 18:40:58 +0200 Subject: [PATCH 20/42] trim suffix with stdlib --- index/utility_methods.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 } From 127b681be2c06ced826ead3edfedab0677970e40 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 19:24:36 +0200 Subject: [PATCH 21/42] update test --- datamodel/high/v3/document_test.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/datamodel/high/v3/document_test.go b/datamodel/high/v3/document_test.go index 50ca92ba..b8742bf7 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" @@ -16,6 +15,7 @@ import ( 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 @@ -427,10 +427,7 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) { } lowDoc, err = lowv3.CreateDocumentFromConfig(info, &config) - if err != nil { - fmt.Printf("error: %v\n", err) - panic("broken something") - } + require.NoError(t, err) d := NewDocument(lowDoc) assert.NotNil(t, d) assert.Equal(t, 183, len(d.Paths.PathItems)) From ecf7db34abc82e3c350a5057a3df79251aeb9aed Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 19:27:36 +0200 Subject: [PATCH 22/42] allow reference resolving by default makes the user handle errors differently the user, depending in their requirement, will either want or not want to resolve circular references. In case that the user decides to forbid resolving circular references, they will handle returned errors as such and they will not need to pick out any specific error from the returned multi error. Changing this default removes the need to actually check if one of the errors is the circular reference error. --- document.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document.go b/document.go index aaedf5a6..199feed8 100644 --- a/document.go +++ b/document.go @@ -123,7 +123,7 @@ func NewDocument(specByteArray []byte, options ...ConfigurationOption) (Document AllowRemoteReferences: false, AvoidIndexBuild: false, BypassDocumentCheck: false, - AllowCircularReferenceResolving: false, + AllowCircularReferenceResolving: true, } var err error From 745af7921d9393277c6de54ea83a89d56c44c6ff Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 19:33:22 +0200 Subject: [PATCH 23/42] update test to accomodate the default value change --- document_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/document_test.go b/document_test.go index 892e6462..da7bbcab 100644 --- a/document_test.go +++ b/document_test.go @@ -321,7 +321,7 @@ func TestDocument_AnyDocWithConfig(t *testing.T) { func TestDocument_BuildModelCircular(t *testing.T) { petstore, _ := os.ReadFile("test_specs/circular-tests.yaml") - doc, err := NewDocument(petstore) + doc, err := NewDocument(petstore, WithAllowCircularReferenceResolving(false)) require.NoError(t, err) m, err := doc.BuildV3Model() From 38d5c3163d52f8300cbd16d12480ad2a797efbed Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 19:36:58 +0200 Subject: [PATCH 24/42] fix example --- document_config.go | 2 ++ document_examples_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/document_config.go b/document_config.go index 0f299ab8..d27dbf6d 100644 --- a/document_config.go +++ b/document_config.go @@ -109,6 +109,8 @@ func WithBypassDocumentCheck(bypass bool) ConfigurationOption { } } +// WithAllowCircularReferenceResolving returns an error for every detected circular reference if set to false. +// If set to true, circular references will be resolved (default behavior). func WithAllowCircularReferenceResolving(allow bool) ConfigurationOption { return func(o *Configuration) error { o.AllowCircularReferenceResolving = allow diff --git a/document_examples_test.go b/document_examples_test.go index 290a4977..31e5ce22 100644 --- a/document_examples_test.go +++ b/document_examples_test.go @@ -401,7 +401,7 @@ components: - testThing ` // create a new document from specification bytes - doc, err := NewDocument([]byte(spec)) + doc, err := NewDocument([]byte(spec), WithAllowCircularReferenceResolving(false)) // if anything went wrong, an error is thrown if err != nil { From d8abbdbfb5bc9fe963f249e8d73253241c5ecaa6 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 19:53:29 +0200 Subject: [PATCH 25/42] update MultiError --- internal/errorutils/errorutils.go | 17 +++++++++++------ resolver/resolver_test.go | 3 ++- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/internal/errorutils/errorutils.go b/internal/errorutils/errorutils.go index 2373a0f3..ee528692 100644 --- a/internal/errorutils/errorutils.go +++ b/internal/errorutils/errorutils.go @@ -1,18 +1,23 @@ 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 []byte + var b strings.Builder + b.Grow(len(e.errs) * 16) for i, err := range e.errs { - if i > 0 { - b = append(b, '\n') - } - b = append(b, err.Error()...) + b.WriteString(fmt.Sprintf("[%d] %v\n", i, err)) } - return string(b) + return b.String() } func (e *MultiError) Unwrap() []error { diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 6400e7bc..17d0d329 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -9,6 +9,7 @@ import ( "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v3" ) @@ -77,7 +78,7 @@ 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) From d9b4b3791378b5e9693f766fbf59689df13b4935 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 20:43:55 +0200 Subject: [PATCH 26/42] try to keep MultiError flat --- index/find_component_test.go | 4 +++- internal/errorutils/errorutils.go | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/index/find_component_test.go b/index/find_component_test.go index 872abc1f..f92d12c4 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) diff --git a/internal/errorutils/errorutils.go b/internal/errorutils/errorutils.go index ee528692..d99a51de 100644 --- a/internal/errorutils/errorutils.go +++ b/internal/errorutils/errorutils.go @@ -39,7 +39,10 @@ func Join(errs ...error) error { result.errs = make([]error, 0, size) for _, err := range errs { - if err != nil { + // try to keep MultiError flat + if multi, ok := err.(*MultiError); ok { + result.errs = append(result.errs, multi.Unwrap()...) + } else { result.errs = append(result.errs, err) } } From c48b1bd8632c16dc0917d1265e7c26bc584705d4 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 21:44:00 +0200 Subject: [PATCH 27/42] remove unused error mapper functions --- internal/errorutils/mapper.go | 42 ----------------------------------- 1 file changed, 42 deletions(-) delete mode 100644 internal/errorutils/mapper.go diff --git a/internal/errorutils/mapper.go b/internal/errorutils/mapper.go deleted file mode 100644 index eebb471a..00000000 --- a/internal/errorutils/mapper.go +++ /dev/null @@ -1,42 +0,0 @@ -package errorutils - -func Mapped(err error, mapper ...func(src error) (dst error, keep bool)) error { - if err == nil { - return nil - } - errs := Unwrap(err) - mapped := Map(errs, AndMapper(mapper...)) - if len(mapped) == 0 { - return nil - } - return Join(mapped...) -} - -func Map(errs []error, mapper func(src error) (dst error, keep bool)) []error { - var result []error - for _, err := range errs { - dst, keep := mapper(err) - if keep { - result = append(result, dst) - } - } - return result -} - -func AndMapper(mappers ...func(error) (error, bool)) func(error) (error, bool) { - return func(srcErr error) (error, bool) { - var ( - dstErr = srcErr - keep bool - ) - for _, mapper := range mappers { - - dstErr, keep = mapper(dstErr) - if !keep { - return nil, false - } - } - // final result to keep - return dstErr, true - } -} From c4fcda170a1ad423872b283b31c335b51538bfe2 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 21:45:05 +0200 Subject: [PATCH 28/42] decrease error handling complexity --- datamodel/high/v3/document_test.go | 6 ++-- datamodel/low/v2/swagger_test.go | 20 ++++++------ datamodel/low/v3/create_document_test.go | 22 ++++++------- document_test.go | 30 ++++++++--------- internal/errorutils/errorutils.go | 41 +++++++++++++----------- internal/errorutils/filter.go | 2 +- 6 files changed, 63 insertions(+), 58 deletions(-) diff --git a/datamodel/high/v3/document_test.go b/datamodel/high/v3/document_test.go index b8742bf7..360f62d5 100644 --- a/datamodel/high/v3/document_test.go +++ b/datamodel/high/v3/document_test.go @@ -386,7 +386,7 @@ func TestStripeAsDoc(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(data) var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) - assert.Len(t, errorutils.Unwrap(err), 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) d := NewDocument(lowDoc) assert.NotNil(t, d) } @@ -397,7 +397,7 @@ func TestK8sAsDoc(t *testing.T) { var err error lowSwag, err := lowv2.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) d := v2.NewSwaggerDocument(lowSwag) - assert.Len(t, errorutils.Unwrap(err), 0) + assert.Len(t, errorutils.ShallowUnwrap(err), 0) assert.NotNil(t, d) } @@ -452,7 +452,7 @@ func TestCircularReferencesDoc(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(data) var err error lowDoc, err = lowv3.CreateDocumentFromConfig(info, datamodel.NewOpenDocumentConfiguration()) - assert.Len(t, errorutils.Unwrap(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) diff --git a/datamodel/low/v2/swagger_test.go b/datamodel/low/v2/swagger_test.go index 8ca3ea93..96eeecc1 100644 --- a/datamodel/low/v2/swagger_test.go +++ b/datamodel/low/v2/swagger_test.go @@ -193,7 +193,7 @@ func TestCreateDocument_ExternalDocsBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_TagsBad(t *testing.T) { @@ -211,7 +211,7 @@ func TestCreateDocument_TagsBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_PathsBad(t *testing.T) { @@ -233,7 +233,7 @@ func TestCreateDocument_PathsBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_SecurityBad(t *testing.T) { @@ -251,7 +251,7 @@ func TestCreateDocument_SecurityBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { @@ -269,7 +269,7 @@ func TestCreateDocument_SecurityDefinitionsBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_ResponsesBad(t *testing.T) { @@ -287,7 +287,7 @@ func TestCreateDocument_ResponsesBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_ParametersBad(t *testing.T) { @@ -305,7 +305,7 @@ func TestCreateDocument_ParametersBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_DefinitionsBad(t *testing.T) { @@ -323,7 +323,7 @@ func TestCreateDocument_DefinitionsBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_InfoBad(t *testing.T) { @@ -341,7 +341,7 @@ func TestCreateDocument_InfoBad(t *testing.T) { wait = false } } - assert.Len(t, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCircularReferenceError(t *testing.T) { @@ -350,6 +350,6 @@ func TestCircularReferenceError(t *testing.T) { info, _ := datamodel.ExtractSpecInfo(data) circDoc, err := CreateDocument(info) assert.NotNil(t, circDoc) - assert.Len(t, errorutils.Unwrap(err), 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) } diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index a5ab4ccf..1df4515e 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -76,7 +76,7 @@ func TestCircularReferenceError(t *testing.T) { AllowFileReferences: false, AllowRemoteReferences: false, }) - assert.Len(t, errorutils.Unwrap(err), 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) // lower level packages return errors and potentially a document. assert.NotNil(t, circDoc) @@ -116,7 +116,7 @@ func TestCreateDocumentStripe(t *testing.T) { AllowRemoteReferences: false, BasePath: "/here", }) - assert.Len(t, errorutils.Unwrap(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) @@ -182,7 +182,7 @@ func TestCreateDocument_WebHooks_Error(t *testing.T) { AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Servers(t *testing.T) { @@ -572,7 +572,7 @@ components: AllowRemoteReferences: false, }) require.NoError(t, err) - require.Len(t, errorutils.Unwrap(err), 0) + require.Len(t, errorutils.ShallowUnwrap(err), 0) ob := doc.Components.Value.FindSchema("bork").Value ob.Schema() @@ -591,7 +591,7 @@ webhooks: AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Components_Error_Extract(t *testing.T) { @@ -607,7 +607,7 @@ components: AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } @@ -623,7 +623,7 @@ paths: AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Tags_Errors(t *testing.T) { @@ -637,7 +637,7 @@ tags: AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_Security_Error(t *testing.T) { @@ -651,7 +651,7 @@ security: AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_ExternalDoc_Error(t *testing.T) { @@ -665,7 +665,7 @@ externalDocs: AllowFileReferences: false, AllowRemoteReferences: false, }) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestCreateDocument_YamlAnchor(t *testing.T) { @@ -746,7 +746,7 @@ func ExampleCreateDocument() { } func panicOnUnknown(err error) { - for _, err := range errorutils.Unwrap(err) { + for _, err := range errorutils.ShallowUnwrap(err) { var resolvErr *resolver.ResolvingError if errors.As(err, &resolvErr) && resolvErr.CircularReference != nil { continue diff --git a/document_test.go b/document_test.go index da7bbcab..8580f15a 100644 --- a/document_test.go +++ b/document_test.go @@ -24,7 +24,7 @@ func TestLoadDocument_Simple_V2(t *testing.T) { v2Doc, docErr := doc.BuildV2Model() require.Nil(t, docErr) - require.Len(t, errorutils.Unwrap(docErr), 0) + require.Len(t, errorutils.ShallowUnwrap(docErr), 0) require.NotNil(t, v2Doc) require.NotNil(t, doc.GetSpecInfo()) } @@ -36,7 +36,7 @@ func TestLoadDocument_Simple_V2_Error(t *testing.T) { assert.NoError(t, err) v2Doc, docErr := doc.BuildV3Model() - assert.Len(t, errorutils.Unwrap(docErr), 1) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 1) assert.Nil(t, v2Doc) } @@ -50,7 +50,7 @@ definitions: require.NoError(t, err) v2Doc, docErr := doc.BuildV2Model() - require.Len(t, errorutils.Unwrap(docErr), 2) + require.Len(t, errorutils.ShallowUnwrap(docErr), 2) require.Nil(t, v2Doc) } @@ -61,7 +61,7 @@ func TestLoadDocument_Simple_V3_Error(t *testing.T) { assert.NoError(t, err) v2Doc, docErr := doc.BuildV2Model() - assert.Len(t, errorutils.Unwrap(docErr), 1) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 1) assert.Nil(t, v2Doc) } @@ -69,14 +69,14 @@ func TestLoadDocument_Error_V2NoSpec(t *testing.T) { doc := new(document) // not how this should be instantiated. _, err := doc.BuildV2Model() - assert.Len(t, errorutils.Unwrap(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, errorutils.Unwrap(err), 1) + assert.Len(t, errorutils.ShallowUnwrap(err), 1) } func TestLoadDocument_Empty(t *testing.T) { @@ -93,7 +93,7 @@ func TestLoadDocument_Simple_V3(t *testing.T) { assert.Equal(t, "3.0.1", doc.GetVersion()) v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, errorutils.Unwrap(docErr), 0) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 0) assert.NotNil(t, v3Doc) } @@ -107,7 +107,7 @@ paths: assert.NoError(t, err) v3Doc, docErr := doc.BuildV3Model() - assert.Len(t, errorutils.Unwrap(docErr), 2) + assert.Len(t, errorutils.ShallowUnwrap(docErr), 2) assert.Nil(t, v3Doc) } @@ -302,7 +302,7 @@ func TestDocument_BuildModelPreBuild(t *testing.T) { require.NoError(t, err) _, _, _, err = doc.RenderAndReload() assert.Nil(t, err) - assert.Len(t, errorutils.Unwrap(err), 0) + assert.Len(t, errorutils.ShallowUnwrap(err), 0) } func TestDocument_AnyDoc(t *testing.T) { @@ -328,7 +328,7 @@ func TestDocument_BuildModelCircular(t *testing.T) { // top level library does not return broken objects // with an error, only one or the other assert.Nil(t, m) - assert.Len(t, errorutils.Unwrap(err), 3) + assert.Len(t, errorutils.ShallowUnwrap(err), 3) } func TestDocument_BuildModelBad(t *testing.T) { @@ -337,7 +337,7 @@ func TestDocument_BuildModelBad(t *testing.T) { require.NoError(t, err) m, err := doc.BuildV3Model() assert.Nil(t, m) - assert.Len(t, errorutils.Unwrap(err), 9) + assert.Len(t, errorutils.ShallowUnwrap(err), 9) } func TestDocument_Serialize_JSON_Modified(t *testing.T) { @@ -407,7 +407,7 @@ func TestDocument_BuildModel_CompareDocsV3_LeftError(t *testing.T) { WithAllowFileReferences(true), ) changes, err := CompareDocuments(originalDoc, updatedDoc) - require.Len(t, errorutils.Unwrap(err), 9) + require.Len(t, errorutils.ShallowUnwrap(err), 9) require.Nil(t, changes) } @@ -418,7 +418,7 @@ func TestDocument_BuildModel_CompareDocsV3_RightError(t *testing.T) { originalDoc, _ := NewDocument(burgerShopOriginal) updatedDoc, _ := NewDocument(burgerShopUpdated) changes, err := CompareDocuments(updatedDoc, originalDoc) - require.Len(t, errorutils.Unwrap(err), 9) + require.Len(t, errorutils.ShallowUnwrap(err), 9) require.Nil(t, changes) } @@ -429,7 +429,7 @@ func TestDocument_BuildModel_CompareDocsV2_Error(t *testing.T) { originalDoc, _ := NewDocument(burgerShopOriginal) updatedDoc, _ := NewDocument(burgerShopUpdated) changes, err := CompareDocuments(updatedDoc, originalDoc) - require.Len(t, errorutils.Unwrap(err), 2) + require.Len(t, errorutils.ShallowUnwrap(err), 2) require.Nil(t, changes) } @@ -446,7 +446,7 @@ func TestDocument_BuildModel_CompareDocsV2V3Mix_Error(t *testing.T) { require.NoError(t, err) changes, err := CompareDocuments(updatedDoc, originalDoc) - require.Len(t, errorutils.Unwrap(err), 1) + require.Len(t, errorutils.ShallowUnwrap(err), 1) require.Nil(t, changes) } diff --git a/internal/errorutils/errorutils.go b/internal/errorutils/errorutils.go index d99a51de..2719d4bc 100644 --- a/internal/errorutils/errorutils.go +++ b/internal/errorutils/errorutils.go @@ -24,6 +24,7 @@ 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 @@ -39,35 +40,39 @@ func Join(errs ...error) error { result.errs = make([]error, 0, size) for _, err := range errs { - // try to keep MultiError flat - if multi, ok := err.(*MultiError); ok { - result.errs = append(result.errs, multi.Unwrap()...) - } else { - result.errs = append(result.errs, err) + if err == nil { + continue } + // try to keep MultiError flat + result.errs = append(result.errs, deepUnwrapMultiError(err)...) } return &result } -// Unwrap recursively unwraps errors and flattens them into a slice of errors. -func Unwrap(err error) []error { +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 unwrap, ok := err.(interface{ Unwrap() []error }); ok { - // ignore wrapping error - no hierarchy - for _, e := range unwrap.Unwrap() { - result = append(result, Unwrap(e)...) + + if multi, ok := err.(*MultiError); ok { + for _, e := range multi.Unwrap() { + result = append(result, deepUnwrapMultiError(e)...) } - return result - } else if unwrap, ok := err.(interface{ Unwrap() error }); ok { - // add parent error to result + } else { result = append(result, err) - result = append(result, Unwrap(unwrap.Unwrap())...) - return result } - // no unwrapping needed, as it's not wrapped - result = append(result, err) return result } diff --git a/internal/errorutils/filter.go b/internal/errorutils/filter.go index 9207297a..f19f5ff8 100644 --- a/internal/errorutils/filter.go +++ b/internal/errorutils/filter.go @@ -4,7 +4,7 @@ func Filtered(err error, filters ...func(error) (keep bool)) error { if err == nil { return nil } - errs := Unwrap(err) + errs := ShallowUnwrap(err) filtered := Filter(errs, AndFilter(filters...)) if len(filtered) == 0 { return nil From 4381bee03524f4276986130cc2abf7503276b57a Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 21:45:17 +0200 Subject: [PATCH 29/42] add errorutils tests --- internal/errorutils/errorutils_test.go | 15 +++-- internal/errorutils/filter_test.go | 93 ++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 5 deletions(-) create mode 100644 internal/errorutils/filter_test.go diff --git a/internal/errorutils/errorutils_test.go b/internal/errorutils/errorutils_test.go index 6b4e87aa..7745609e 100644 --- a/internal/errorutils/errorutils_test.go +++ b/internal/errorutils/errorutils_test.go @@ -10,16 +10,17 @@ import ( func TestUnwrapErrors(t *testing.T) { err := Join(errors.New("foo"), errors.New("bar")) + require.NotEmpty(t, err.Error()) - errs := Unwrap(err) + errs := ShallowUnwrap(err) require.Len(t, errs, 2) } func TestUnwrapError(t *testing.T) { err := fmt.Errorf("foo: %w", errors.New("bar")) - errs := Unwrap(err) - require.Len(t, errs, 2) + errs := ShallowUnwrap(err) + require.Len(t, errs, 1) } func TestUnwrapHierarchyError(t *testing.T) { @@ -31,11 +32,15 @@ func TestUnwrapHierarchyError(t *testing.T) { err := Join(Join(nil, err2), Join(nil, err4, nil)) - errs := Unwrap(err) - require.Len(t, errs, 4) + 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_test.go b/internal/errorutils/filter_test.go new file mode 100644 index 00000000..b53b67a8 --- /dev/null +++ b/internal/errorutils/filter_test.go @@ -0,0 +1,93 @@ +package errorutils + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +type customErr struct { + FieldValue string +} + +func (e *customErr) Error() string { + return e.FieldValue +} + +func shallowFilter(err error) bool { + _, ok := err.(*customErr) + return ok +} + +func deepFilter(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, shallowFilter) + + require.Len(t, filtered, 1) + + for _, err := range filtered { + require.NotNil(t, err) + cerr, ok := err.(*customErr) + require.True(t, ok, "cannot type assert to customErr") + + require.NotEmpty(t, cerr.FieldValue) + } + + filtered = Filter(errs, deepFilter) + + require.Len(t, filtered, 2) +} + +func TestFilteredNormal(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, shallowFilter) + + require.Len(t, ShallowUnwrap(filtered), 1) + + filtered = Filtered(err, deepFilter) + require.Len(t, ShallowUnwrap(filtered), 2) +} + +func TestFilteredNil(t *testing.T) { + + filtered := Filtered(nil, shallowFilter) + 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) +} From 9da55d5441e402997f05249a0ce6d9827628a564 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sun, 20 Aug 2023 22:49:11 +0200 Subject: [PATCH 30/42] make zero value config struct the same as the default config --- document.go | 33 +++++++++++++++++++-------------- document_config.go | 14 +++++++------- document_examples_test.go | 2 +- document_test.go | 5 +++-- errors.go | 9 +++------ index/find_component_test.go | 4 +++- internal/errorutils/filter.go | 13 +++++++++---- resolver/resolver_test.go | 8 ++++---- 8 files changed, 49 insertions(+), 39 deletions(-) diff --git a/document.go b/document.go index 199feed8..654d003c 100644 --- a/document.go +++ b/document.go @@ -92,7 +92,7 @@ type document struct { // skip optional errors like circular reference errors. // if configured - errorFilter func(error) bool + errorFilter []func(error) bool } // DocumentModel represents either a Swagger document (version 2) or an OpenAPI document (version 3) that is @@ -118,12 +118,12 @@ type DocumentModel[T v2high.Swagger | v3high.Document] struct { 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, - AllowCircularReferenceResolving: true, + RemoteURLHandler: http.Get, + AllowFileReferences: false, + AllowRemoteReferences: false, + AvoidIndexBuild: false, + BypassDocumentCheck: false, + ForbidCircularReferenceResolving: false, } var err error @@ -150,7 +150,7 @@ func NewDocumentWithConfiguration(specByteArray []byte, config *Configuration) ( info: info, highOpenAPI3Model: nil, highSwaggerModel: nil, - errorFilter: defaultErrorFilter, + errorFilter: []func(error) bool{defaultErrorFilter}, } d.SetConfiguration(config) @@ -162,14 +162,19 @@ func (d *document) SetConfiguration(config *Configuration) { d.config = config if config == nil { - d.errorFilter = defaultErrorFilter + d.errorFilter = []func(error) bool{defaultErrorFilter} return } - d.errorFilter = errorutils.AndFilter( + if config.RemoteURLHandler == nil { + // set default handler if + config.RemoteURLHandler = http.Get + } + + d.errorFilter = []func(error) bool{ // more filters can be added here if needed - circularReferenceErrorFilter(config.AllowCircularReferenceResolving), - ) + circularReferenceErrorFilter(config.ForbidCircularReferenceResolving), + } } func NewDocumentWithTypeCheck(specByteArray []byte, bypassCheck bool) (Document, error) { @@ -250,7 +255,7 @@ func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { } lowDoc, err := v2low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) - err = errorutils.Filtered(err, d.errorFilter) + err = errorutils.Filtered(err, d.errorFilter...) if err != nil { return nil, err } @@ -282,7 +287,7 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { } lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) - err = errorutils.Filtered(err, d.errorFilter) + err = errorutils.Filtered(err, d.errorFilter...) if err != nil { return nil, err } diff --git a/document_config.go b/document_config.go index d27dbf6d..101f4b4d 100644 --- a/document_config.go +++ b/document_config.go @@ -40,9 +40,9 @@ type Configuration struct { // passed in and used. Only enable this when parsing non openapi documents. BypassDocumentCheck bool - // AllowCircularReferences will allow circular references to be resolved. This is disabled by default. - // Will return an error in case of circular references. - AllowCircularReferenceResolving 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 { @@ -109,11 +109,11 @@ func WithBypassDocumentCheck(bypass bool) ConfigurationOption { } } -// WithAllowCircularReferenceResolving returns an error for every detected circular reference if set to false. -// If set to true, circular references will be resolved (default behavior). -func WithAllowCircularReferenceResolving(allow bool) ConfigurationOption { +// 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.AllowCircularReferenceResolving = allow + o.ForbidCircularReferenceResolving = forbidden return nil } } diff --git a/document_examples_test.go b/document_examples_test.go index 31e5ce22..d02dfa8d 100644 --- a/document_examples_test.go +++ b/document_examples_test.go @@ -401,7 +401,7 @@ components: - testThing ` // create a new document from specification bytes - doc, err := NewDocument([]byte(spec), WithAllowCircularReferenceResolving(false)) + doc, err := NewDocument([]byte(spec), WithForbidCircularReferenceResolving(true)) // if anything went wrong, an error is thrown if err != nil { diff --git a/document_test.go b/document_test.go index 8580f15a..8221a4fe 100644 --- a/document_test.go +++ b/document_test.go @@ -174,7 +174,7 @@ func TestDocument_RenderAndReload_ChangeCheck_Burgershop(t *testing.T) { func TestDocument_RenderAndReload_ChangeCheck_Stripe(t *testing.T) { bs, _ := os.ReadFile("test_specs/stripe.yaml") - doc, err := NewDocument(bs, WithAllowCircularReferenceResolving(true)) + doc, err := NewDocument(bs) require.NoError(t, err) _, err = doc.BuildV3Model() require.NoError(t, err) @@ -321,9 +321,10 @@ func TestDocument_AnyDocWithConfig(t *testing.T) { func TestDocument_BuildModelCircular(t *testing.T) { petstore, _ := os.ReadFile("test_specs/circular-tests.yaml") - doc, err := NewDocument(petstore, WithAllowCircularReferenceResolving(false)) + 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 diff --git a/errors.go b/errors.go index 661e2abc..3a98f768 100644 --- a/errors.go +++ b/errors.go @@ -32,18 +32,15 @@ func isCircularErr(err error) bool { // 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(refAllowed bool) func(error) (keep bool) { +func circularReferenceErrorFilter(forbidden bool) func(error) (keep bool) { return func(err error) bool { if err == nil { return false } if isCircularErr(err) { - if refAllowed { - return false - } else { - return true - } + // if forbidded -> keep the error and pass it to the user + return forbidden } // keep unknown error diff --git a/index/find_component_test.go b/index/find_component_test.go index f92d12c4..a2191850 100644 --- a/index/find_component_test.go +++ b/index/find_component_test.go @@ -177,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/internal/errorutils/filter.go b/internal/errorutils/filter.go index f19f5ff8..71c64dec 100644 --- a/internal/errorutils/filter.go +++ b/internal/errorutils/filter.go @@ -5,7 +5,7 @@ func Filtered(err error, filters ...func(error) (keep bool)) error { return nil } errs := ShallowUnwrap(err) - filtered := Filter(errs, AndFilter(filters...)) + filtered := Filter(errs, and(filters...)) if len(filtered) == 0 { return nil } @@ -14,21 +14,26 @@ func Filtered(err error, filters ...func(error) (keep bool)) error { func Filter(errs []error, filter func(error) (keep bool)) []error { var result []error + var keep bool for _, err := range errs { - if filter(err) { + keep = filter(err) + if keep { result = append(result, err) } } return result } -func AndFilter(filters ...func(error) (keep bool)) func(error) (keep bool) { +func and(filters ...func(error) (keep bool)) func(error) (keep bool) { return func(err error) bool { + var keep bool for _, filter := range filters { - if !filter(err) { + keep = filter(err) + if !keep { return false } } + // all true -> true return true } } diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index 17d0d329..5729ac9b 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -81,12 +81,12 @@ func TestResolver_CheckForCircularReferences_DigitalOcean(t *testing.T) { 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) { From c3aa80c2b8bf6ebcc06e0d6c71ce3a69979606e0 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sat, 26 Aug 2023 11:58:48 +0200 Subject: [PATCH 31/42] test splitting test, race & coverage --- .github/workflows/build.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 12c71998..1f0d890b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -69,8 +69,11 @@ jobs: - name: Build run: go build ./... + - name: Test & Race Detection + run: go test -v -timeout 10m -race ./... + - name: Test & Code Coverage - run: go test ./... -v -timeout 20m -race -count=1 -covermode=atomic -coverprofile=coverage.txt + run: go test -v -timeout 10m covermode=atomic -coverprofile=coverage.txt ./... - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 From b434c8945d30e4dd5da1e678d613ec00fc26797e Mon Sep 17 00:00:00 2001 From: John Behm Date: Sat, 26 Aug 2023 12:09:31 +0200 Subject: [PATCH 32/42] update go.sum --- go.sum | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.sum b/go.sum index 46622644..40ac507e 100644 --- a/go.sum +++ b/go.sum @@ -76,8 +76,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= From 8c2c821c741ba6461efe61596f00f53874f780d5 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sat, 26 Aug 2023 12:28:46 +0200 Subject: [PATCH 33/42] try w/o timeout --- .github/workflows/build.yaml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 1f0d890b..41c496d3 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -66,20 +66,18 @@ jobs: # Path to SARIF file relative to the root of the repository sarif_file: results.sarif - - name: Build - run: go build ./... - - name: Test & Race Detection - run: go test -v -timeout 10m -race ./... + - name: Test + run: go test -v -race ./... - - name: Test & Code Coverage - run: go test -v -timeout 10m covermode=atomic -coverprofile=coverage.txt ./... + - name: Code Coverage + run: go test -v covermode=atomic -coverprofile=coverage.out ./... - name: Upload coverage to Codecov uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} - files: ./coverage.txt + files: ./coverage.out fail_ci_if_error: false verbose: true From f6735fed2bfd592265032b41f2192790f6e8de35 Mon Sep 17 00:00:00 2001 From: John Behm Date: Sat, 26 Aug 2023 12:29:04 +0200 Subject: [PATCH 34/42] add makefile with the complete test commands --- Makefile | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..657a768d --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ + +build: + go build ./... + +test: + 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 From 63207d200c88ec97231a81dc7475966e4a3cdbab Mon Sep 17 00:00:00 2001 From: John Behm Date: Sat, 26 Aug 2023 15:23:22 +0200 Subject: [PATCH 35/42] add t.Parallel to tests that don't have global shared stats --- Makefile | 3 + datamodel/document_config_test.go | 5 +- datamodel/high/base/contact_test.go | 7 +- datamodel/high/base/discriminator_test.go | 7 +- datamodel/high/base/dynamic_value_test.go | 21 ++-- datamodel/high/base/example_test.go | 8 +- datamodel/high/base/external_doc_test.go | 7 +- datamodel/high/base/info_test.go | 2 + datamodel/high/base/licence_test.go | 7 +- datamodel/high/base/schema_proxy_test.go | 8 +- datamodel/high/base/schema_test.go | 31 ++++++ .../high/base/security_requirement_test.go | 7 +- datamodel/high/base/tag_test.go | 4 +- datamodel/high/node_builder_test.go | 50 ++++++++-- datamodel/high/shared_test.go | 6 +- datamodel/high/v2/path_item_test.go | 5 +- datamodel/high/v3/callback_test.go | 7 +- datamodel/high/v3/components_test.go | 7 +- datamodel/high/v3/document_test.go | 12 +++ datamodel/high/v3/encoding_test.go | 5 +- datamodel/high/v3/header_test.go | 7 +- datamodel/high/v3/link_test.go | 4 +- datamodel/high/v3/media_type_test.go | 3 + datamodel/high/v3/oauth_flow_test.go | 13 ++- datamodel/high/v3/oauth_flows_test.go | 7 +- datamodel/high/v3/operation_test.go | 7 +- datamodel/high/v3/parameter_test.go | 12 +-- datamodel/high/v3/path_item_test.go | 4 + datamodel/high/v3/paths_test.go | 2 + datamodel/high/v3/request_body_test.go | 6 +- datamodel/high/v3/response_test.go | 4 +- datamodel/high/v3/responses_test.go | 3 + datamodel/high/v3/security_scheme_test.go | 6 +- datamodel/high/v3/server_test.go | 4 +- datamodel/high/v3/server_variable_test.go | 4 +- datamodel/low/base/contact_test.go | 5 +- datamodel/low/base/discriminator_test.go | 6 +- datamodel/low/base/example_test.go | 21 ++-- datamodel/low/base/external_doc_test.go | 9 +- datamodel/low/base/info_test.go | 2 +- datamodel/low/base/license_test.go | 10 +- datamodel/low/base/schema_proxy_test.go | 11 ++- datamodel/low/base/schema_test.go | 44 ++++++++- .../low/base/security_requirement_test.go | 5 +- datamodel/low/base/tag_test.go | 9 +- datamodel/low/extraction_functions_test.go | 98 +++++++++++++------ datamodel/low/model_builder_test.go | 6 +- datamodel/low/reference_test.go | 47 ++++++++- datamodel/low/v2/definitions_test.go | 13 +-- datamodel/low/v2/examples_test.go | 5 +- datamodel/low/v2/header_test.go | 13 +-- datamodel/low/v2/items_test.go | 11 ++- datamodel/low/v2/operation_test.go | 13 +-- datamodel/low/v2/parameter_test.go | 5 +- datamodel/low/v2/path_item_test.go | 9 +- datamodel/low/v2/paths_test.go | 9 +- datamodel/low/v2/response_test.go | 11 ++- datamodel/low/v2/responses_test.go | 11 ++- datamodel/low/v2/scopes_test.go | 5 +- datamodel/low/v2/security_scheme_test.go | 9 +- datamodel/low/v3/callback_test.go | 11 ++- datamodel/low/v3/components_test.go | 15 +-- datamodel/low/v3/create_document_test.go | 13 ++- datamodel/low/v3/encoding_test.go | 9 +- datamodel/low/v3/header_test.go | 10 +- datamodel/low/v3/link_test.go | 9 +- datamodel/low/v3/media_type_test.go | 8 +- datamodel/low/v3/oauth_flows_test.go | 14 ++- datamodel/low/v3/operation_test.go | 13 ++- datamodel/low/v3/parameter_test.go | 9 +- datamodel/low/v3/path_item_test.go | 5 +- datamodel/low/v3/paths_test.go | 30 +++--- datamodel/low/v3/request_body_test.go | 9 +- datamodel/low/v3/response_test.go | 27 ++--- datamodel/low/v3/security_scheme_test.go | 6 +- datamodel/low/v3/server_test.go | 7 +- datamodel/spec_info_test.go | 36 ++++--- 77 files changed, 639 insertions(+), 274 deletions(-) diff --git a/Makefile b/Makefile index 657a768d..458c3801 100644 --- a/Makefile +++ b/Makefile @@ -3,6 +3,9 @@ build: go build ./... test: + go test -v -timeout 30m -count=1 ./... + +race: go test -v -timeout 30m -count=1 -race ./... coverage: 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_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_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/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_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 360f62d5..83da294b 100644 --- a/datamodel/high/v3/document_test.go +++ b/datamodel/high/v3/document_test.go @@ -382,6 +382,7 @@ 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 @@ -392,6 +393,7 @@ func TestStripeAsDoc(t *testing.T) { } func TestK8sAsDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/k8s.json") info, _ := datamodel.ExtractSpecInfo(data) var err error @@ -402,6 +404,7 @@ func TestK8sAsDoc(t *testing.T) { } func TestAsanaAsDoc(t *testing.T) { + t.Parallel() data, _ := os.ReadFile("../../../test_specs/asana.yaml") info, _ := datamodel.ExtractSpecInfo(data) var err error @@ -415,6 +418,7 @@ 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 @@ -435,6 +439,7 @@ 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 @@ -448,6 +453,7 @@ 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 @@ -477,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) @@ -495,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) @@ -513,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) @@ -551,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 @@ -629,6 +639,7 @@ components: } func TestDocument_MarshalYAML_TestParamRefs(t *testing.T) { + t.Parallel() // create a new document yml := `openapi: 3.1.0 @@ -682,6 +693,7 @@ components: } func TestDocument_MarshalYAML_TestModifySchemas(t *testing.T) { + t.Parallel() // create a new document yml := `openapi: 3.1.0 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 03344513..bc7f1f8a 100644 --- a/datamodel/high/v3/media_type_test.go +++ b/datamodel/high/v3/media_type_test.go @@ -17,6 +17,7 @@ import ( ) func TestMediaType_MarshalYAMLInline(t *testing.T) { + t.Parallel() // load the petstore spec data, _ := os.ReadFile("../../../test_specs/petstorev3.json") info, _ := datamodel.ExtractSpecInfo(data) @@ -107,6 +108,7 @@ example: testing a nice mutation` } func TestMediaType_MarshalYAML(t *testing.T) { + t.Parallel() // load the petstore spec data, _ := os.ReadFile("../../../test_specs/petstorev3.json") info, _ := datamodel.ExtractSpecInfo(data) @@ -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/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 2c351fdd..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 @@ -435,6 +439,7 @@ examples: } 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 b59345df..df642d9b 100644 --- a/datamodel/low/model_builder_test.go +++ b/datamodel/low/model_builder_test.go @@ -378,7 +378,7 @@ 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 @@ -403,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]] } @@ -428,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_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/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_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/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_test.go b/datamodel/low/v3/create_document_test.go index 1df4515e..0d903379 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -69,7 +69,7 @@ func BenchmarkCreateDocument_k8s(b *testing.B) { } 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{ @@ -108,7 +108,7 @@ func BenchmarkCreateDocument_Petstore(b *testing.B) { } func TestCreateDocumentStripe(t *testing.T) { - + t.Parallel() data, _ := os.ReadFile("../../../test_specs/stripe.yaml") info, _ := datamodel.ExtractSpecInfo(data) d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ @@ -173,6 +173,7 @@ func TestCreateDocument_WebHooks(t *testing.T) { } func TestCreateDocument_WebHooks_Error(t *testing.T) { + t.Parallel() yml := `webhooks: $ref: #bork` @@ -557,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: @@ -580,6 +582,7 @@ components: } func TestCreateDocument_Webhooks_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 webhooks: aHook: @@ -595,6 +598,7 @@ webhooks: } func TestCreateDocument_Components_Error_Extract(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 components: parameters: @@ -612,6 +616,7 @@ components: } func TestCreateDocument_Paths_Errors(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 paths: /p: @@ -627,6 +632,7 @@ paths: } func TestCreateDocument_Tags_Errors(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 tags: - $ref: #bork` @@ -641,6 +647,7 @@ tags: } func TestCreateDocument_Security_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 security: $ref: #bork` @@ -655,6 +662,7 @@ security: } func TestCreateDocument_ExternalDoc_Error(t *testing.T) { + t.Parallel() yml := `openapi: 3.0 externalDocs: $ref: #bork` @@ -669,6 +677,7 @@ externalDocs: } func TestCreateDocument_YamlAnchor(t *testing.T) { + t.Parallel() // load petstore into bytes anchorDocument, _ := os.ReadFile("../../../test_specs/yaml-anchor.yaml") 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/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_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 6f8e53df..4e19794b 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 @@ -120,7 +120,7 @@ func TestPaths_Build_FailRef(t *testing.T) { } func TestPaths_Build_FailRefDeadEnd(t *testing.T) { - + t.Parallel() // this is nuts. yml := `"/no/path": get: @@ -144,7 +144,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 @@ -181,7 +181,7 @@ func TestPaths_Build_SuccessRef(t *testing.T) { } func TestPaths_Build_BadParams(t *testing.T) { - + t.Parallel() yml := `"/some/path": parameters: this: shouldFail` @@ -199,7 +199,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 @@ -225,7 +225,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 @@ -255,7 +255,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 @@ -285,7 +285,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":` @@ -303,7 +303,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': @@ -341,7 +341,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': @@ -377,7 +377,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': @@ -414,7 +414,7 @@ func TestPath_Build_Using_CircularRefWithOp(t *testing.T) { } func TestPaths_Build_BrokenOp(t *testing.T) { - + t.Parallel() yml := `"/some/path": post: externalDocs: @@ -433,7 +433,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 cccae7a7..e989d53d 100644 --- a/datamodel/spec_info_test.go +++ b/datamodel/spec_info_test.go @@ -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,46 @@ 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() + 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 +165,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 +173,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 +182,7 @@ func TestExtractSpecInfo_OpenAPI31(t *testing.T) { } func TestExtractSpecInfo_AnyDocument(t *testing.T) { - + t.Parallel() random := `something: yeah nothing: - one @@ -190,7 +198,7 @@ why: } func TestExtractSpecInfo_AnyDocument_JSON(t *testing.T) { - + t.Parallel() random := `{ "something" : "yeah"}` r, e := ExtractSpecInfoWithDocumentCheck([]byte(random), true) @@ -201,7 +209,7 @@ func TestExtractSpecInfo_AnyDocument_JSON(t *testing.T) { } func TestExtractSpecInfo_AnyDocumentFromConfig(t *testing.T) { - + t.Parallel() random := `something: yeah nothing: - one @@ -219,14 +227,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 +244,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 +252,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 +261,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 +269,7 @@ func TestExtractSpecInfo_AsyncAPI_OddVersion(t *testing.T) { } func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) { - + t.Parallel() yml := `openapi: should: fail` @@ -270,7 +278,7 @@ func TestExtractSpecInfo_BadVersion_OpenAPI3(t *testing.T) { } func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) { - + t.Parallel() yml := `swagger: should: fail` @@ -279,7 +287,7 @@ func TestExtractSpecInfo_BadVersion_Swagger(t *testing.T) { } func TestExtractSpecInfo_BadVersion_AsyncAPI(t *testing.T) { - + t.Parallel() yml := `asyncapi: should: fail` From 9ec2200f770ffc66a83e49126cb1d0acfbb95cfd Mon Sep 17 00:00:00 2001 From: John Behm Date: Wed, 30 Aug 2023 16:32:20 +0200 Subject: [PATCH 36/42] rm -race flag --- .github/workflows/build.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 41c496d3..887b5261 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -67,10 +67,7 @@ jobs: sarif_file: results.sarif - - name: Test - run: go test -v -race ./... - - - name: Code Coverage + - name: Test & Code Coverage run: go test -v covermode=atomic -coverprofile=coverage.out ./... - name: Upload coverage to Codecov From 63818341f6e7ccc1b2f9954a55e7c19461ce7494 Mon Sep 17 00:00:00 2001 From: John Behm Date: Fri, 1 Sep 2023 18:51:14 +0200 Subject: [PATCH 37/42] fix build.yaml --- .github/workflows/build.yaml | 6 +++++- .gitignore | 1 + Makefile | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 887b5261..a5e70c58 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -68,7 +68,11 @@ jobs: - name: Test & Code Coverage - run: go test -v covermode=atomic -coverprofile=coverage.out ./... + # 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 diff --git a/.gitignore b/.gitignore index 66362429..63406912 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ test-operation.yaml .idea/ *.iml __debug_bin* +coverage.out diff --git a/Makefile b/Makefile index 458c3801..180cfdad 100644 --- a/Makefile +++ b/Makefile @@ -9,4 +9,4 @@ 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 + go test -v -timeout 30m -count=1 -covermode=atomic -coverprofile=coverage.out ./... \ No newline at end of file From 04407cc5c90bc1a6cff638ea36496ce979b11889 Mon Sep 17 00:00:00 2001 From: John Behm Date: Fri, 1 Sep 2023 19:02:18 +0200 Subject: [PATCH 38/42] fix multiple calls to t.Parallel() --- datamodel/spec_info_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/datamodel/spec_info_test.go b/datamodel/spec_info_test.go index e989d53d..6490944d 100644 --- a/datamodel/spec_info_test.go +++ b/datamodel/spec_info_test.go @@ -142,7 +142,6 @@ func TestExtractSpecInfo_ValidYAML(t *testing.T) { } func TestExtractSpecInfo_InvalidYAML(t *testing.T) { - t.Parallel() t.Parallel() _, e := ExtractSpecInfo([]byte(badYAML)) assert.Error(t, e) From e834c01921a8618a21a321262d7d33cd411cb03b Mon Sep 17 00:00:00 2001 From: John Behm Date: Fri, 1 Sep 2023 19:20:06 +0200 Subject: [PATCH 39/42] and filter logic -> or filter logic --- internal/errorutils/filter.go | 12 ++++++------ internal/errorutils/filter_test.go | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/internal/errorutils/filter.go b/internal/errorutils/filter.go index 71c64dec..77988e27 100644 --- a/internal/errorutils/filter.go +++ b/internal/errorutils/filter.go @@ -5,7 +5,7 @@ func Filtered(err error, filters ...func(error) (keep bool)) error { return nil } errs := ShallowUnwrap(err) - filtered := Filter(errs, and(filters...)) + filtered := Filter(errs, or(filters...)) if len(filtered) == 0 { return nil } @@ -24,16 +24,16 @@ func Filter(errs []error, filter func(error) (keep bool)) []error { return result } -func and(filters ...func(error) (keep bool)) func(error) (keep bool) { +func or(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 + if keep { + return true } } - // all true -> true - return true + // all false -> false + return false } } diff --git a/internal/errorutils/filter_test.go b/internal/errorutils/filter_test.go index b53b67a8..3963d557 100644 --- a/internal/errorutils/filter_test.go +++ b/internal/errorutils/filter_test.go @@ -8,6 +8,14 @@ import ( "github.com/stretchr/testify/require" ) +type customErr2 struct { + FieldValue string +} + +func (e *customErr2) Error() string { + return e.FieldValue +} + type customErr struct { FieldValue string } @@ -21,6 +29,11 @@ func shallowFilter(err error) bool { return ok } +func custom2Filter(err error) bool { + _, ok := err.(*customErr2) + return ok +} + func deepFilter(err error) bool { var cerr *customErr return errors.As(err, &cerr) @@ -61,6 +74,7 @@ func TestFilteredNormal(t *testing.T) { &customErr{FieldValue: "foo"}, fmt.Errorf("foo: %w", &customErr{FieldValue: "foo"}), errors.New("bar"), + &customErr2{"custom2 error"}, nil, } err := Join(errs...) @@ -71,6 +85,12 @@ func TestFilteredNormal(t *testing.T) { filtered = Filtered(err, deepFilter) require.Len(t, ShallowUnwrap(filtered), 2) + + // we define multiple filters that specialize on their own type of error + // to is one of those filters deems the error as one that we want to keep, + // the error is then kept in the resulting multi error list. + filtered = Filtered(err, deepFilter, custom2Filter) + require.Len(t, ShallowUnwrap(filtered), 3) } func TestFilteredNil(t *testing.T) { From d2a8bc8f9ed3bb339ac46d3fe254d797f55b1270 Mon Sep 17 00:00:00 2001 From: John Behm Date: Fri, 1 Sep 2023 20:26:27 +0200 Subject: [PATCH 40/42] improve configuration & add tests --- document.go | 24 +++++++++++------------- document_test.go | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/document.go b/document.go index 654d003c..249fe9a9 100644 --- a/document.go +++ b/document.go @@ -140,6 +140,12 @@ func NewDocument(specByteArray []byte, options ...ConfigurationOption) (Document // 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 @@ -150,7 +156,7 @@ func NewDocumentWithConfiguration(specByteArray []byte, config *Configuration) ( info: info, highOpenAPI3Model: nil, highSwaggerModel: nil, - errorFilter: []func(error) bool{defaultErrorFilter}, + errorFilter: []func(error) bool{defaultErrorFilter}, // set in SetConfiguration } d.SetConfiguration(config) @@ -159,11 +165,9 @@ func NewDocumentWithConfiguration(specByteArray []byte, config *Configuration) ( } func (d *document) SetConfiguration(config *Configuration) { - d.config = config - if config == nil { - d.errorFilter = []func(error) bool{defaultErrorFilter} - return + // programming error, should panic + panic("configuration cannot be nil") } if config.RemoteURLHandler == nil { @@ -171,6 +175,8 @@ func (d *document) SetConfiguration(config *Configuration) { config.RemoteURLHandler = http.Get } + d.config = config + d.errorFilter = []func(error) bool{ // more filters can be added here if needed circularReferenceErrorFilter(config.ForbidCircularReferenceResolving), @@ -250,10 +256,6 @@ func (d *document) BuildV2Model() (*DocumentModel[v2high.Swagger], error) { } var lowDoc *v2low.Swagger - if d.config == nil { - return nil, ErrNoConfiguration - } - lowDoc, err := v2low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) err = errorutils.Filtered(err, d.errorFilter...) if err != nil { @@ -282,10 +284,6 @@ func (d *document) BuildV3Model() (*DocumentModel[v3high.Document], error) { } var lowDoc *v3low.Document - if d.config == nil { - return nil, ErrNoConfiguration - } - lowDoc, err = v3low.CreateDocumentFromConfig(d.info, d.config.toModelConfig()) err = errorutils.Filtered(err, d.errorFilter...) if err != nil { diff --git a/document_test.go b/document_test.go index 8221a4fe..4a97cc52 100644 --- a/document_test.go +++ b/document_test.go @@ -3,6 +3,7 @@ package libopenapi import ( + "errors" "fmt" "os" "strings" @@ -40,6 +41,12 @@ func TestLoadDocument_Simple_V2_Error(t *testing.T) { assert.Nil(t, v2Doc) } +func TestLoadDocument_Simple_WithErrorOption_V2(t *testing.T) { + yml := `swagger: 2.0.1` + _, err := NewDocument([]byte(yml), 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 @@ -319,6 +326,42 @@ func TestDocument_AnyDocWithConfig(t *testing.T) { 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, _ := os.ReadFile("test_specs/circular-tests.yaml") doc, err := NewDocument(petstore, WithForbidCircularReferenceResolving(true)) From b842c4493e189e7e53b3f7c777c59a79e0356d32 Mon Sep 17 00:00:00 2001 From: John Behm Date: Fri, 1 Sep 2023 21:04:59 +0200 Subject: [PATCH 41/42] improve filter & make tests more understandable --- document.go | 2 +- errors.go | 12 +------ go.mod | 2 +- internal/errorutils/filter.go | 19 ++++++++---- internal/errorutils/filter_test.go | 50 +++++++++++++++--------------- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/document.go b/document.go index 249fe9a9..2c35fc8b 100644 --- a/document.go +++ b/document.go @@ -156,7 +156,7 @@ func NewDocumentWithConfiguration(specByteArray []byte, config *Configuration) ( info: info, highOpenAPI3Model: nil, highSwaggerModel: nil, - errorFilter: []func(error) bool{defaultErrorFilter}, // set in SetConfiguration + errorFilter: nil, // set in SetConfiguration } d.SetConfiguration(config) diff --git a/errors.go b/errors.go index 3a98f768..20a57fbd 100644 --- a/errors.go +++ b/errors.go @@ -12,15 +12,7 @@ var ( ErrOpenAPI3Operation = errors.New("this operation is only supported for OpenAPI 3 documents") ) -func defaultErrorFilter(err error) bool { - return true -} - func isCircularErr(err error) bool { - if err == nil { - return false - } - var resolvErr *resolver.ResolvingError if errors.As(err, &resolvErr) { return resolvErr.CircularReference != nil @@ -34,9 +26,7 @@ func isCircularErr(err error) bool { // 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 { - if err == nil { - return false - } + // 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 diff --git a/go.mod b/go.mod index 2ee2c70f..fb9c98e9 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/pb33f/libopenapi go 1.20 require ( + 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 @@ -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/internal/errorutils/filter.go b/internal/errorutils/filter.go index 77988e27..d98a8dfb 100644 --- a/internal/errorutils/filter.go +++ b/internal/errorutils/filter.go @@ -1,11 +1,15 @@ 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, or(filters...)) + filtered := Filter(errs, and(filters...)) if len(filtered) == 0 { return nil } @@ -16,6 +20,9 @@ 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) @@ -24,16 +31,16 @@ func Filter(errs []error, filter func(error) (keep bool)) []error { return result } -func or(filters ...func(error) (keep bool)) func(error) (keep bool) { +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 true + if !keep { + return false } } - // all false -> false - return false + // all true -> true + return true } } diff --git a/internal/errorutils/filter_test.go b/internal/errorutils/filter_test.go index 3963d557..0036f340 100644 --- a/internal/errorutils/filter_test.go +++ b/internal/errorutils/filter_test.go @@ -24,19 +24,24 @@ func (e *customErr) Error() string { return e.FieldValue } -func shallowFilter(err error) bool { - _, ok := err.(*customErr) - return ok +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 { - _, ok := err.(*customErr2) - return ok + if _, ok := err.(*customErr2); ok { + return false + } + return true } -func deepFilter(err error) bool { +func deepCustom1Filter(err error) bool { var cerr *customErr - return errors.As(err, &cerr) + return !errors.As(err, &cerr) } func removeAllFilter(err error) bool { @@ -52,21 +57,19 @@ func TestFilter(t *testing.T) { nil, } - filtered := Filter(errs, shallowFilter) - - require.Len(t, filtered, 1) + filtered := Filter(errs, custom1Filter) + require.Len(t, filtered, 2) for _, err := range filtered { require.NotNil(t, err) - cerr, ok := err.(*customErr) - require.True(t, ok, "cannot type assert to customErr") - - require.NotEmpty(t, cerr.FieldValue) + _, ok := err.(*customErr) + require.False(t, ok, "cannot type assert to customErr") } - filtered = Filter(errs, deepFilter) + // additional filter that removes errors + filtered = Filter(errs, deepCustom1Filter) - require.Len(t, filtered, 2) + require.Len(t, filtered, 1) } func TestFilteredNormal(t *testing.T) { @@ -79,23 +82,20 @@ func TestFilteredNormal(t *testing.T) { } err := Join(errs...) - filtered := Filtered(err, shallowFilter) + filtered := Filtered(err, custom1Filter) - require.Len(t, ShallowUnwrap(filtered), 1) + require.Len(t, ShallowUnwrap(filtered), 3) - filtered = Filtered(err, deepFilter) + filtered = Filtered(err, deepCustom1Filter) require.Len(t, ShallowUnwrap(filtered), 2) - // we define multiple filters that specialize on their own type of error - // to is one of those filters deems the error as one that we want to keep, - // the error is then kept in the resulting multi error list. - filtered = Filtered(err, deepFilter, custom2Filter) - require.Len(t, ShallowUnwrap(filtered), 3) + filtered = Filtered(err, deepCustom1Filter, custom2Filter) + require.Len(t, ShallowUnwrap(filtered), 1) } func TestFilteredNil(t *testing.T) { - filtered := Filtered(nil, shallowFilter) + filtered := Filtered(nil, custom1Filter) require.Nil(t, filtered) } From c9cb487d083849df34773bd2e8f370f27f668d9e Mon Sep 17 00:00:00 2001 From: John Behm Date: Fri, 1 Sep 2023 21:25:55 +0200 Subject: [PATCH 42/42] call all option functions for more test coverage --- document_test.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/document_test.go b/document_test.go index 4a97cc52..662f2733 100644 --- a/document_test.go +++ b/document_test.go @@ -5,6 +5,7 @@ package libopenapi import ( "errors" "fmt" + "net/http" "os" "strings" "testing" @@ -43,7 +44,17 @@ func TestLoadDocument_Simple_V2_Error(t *testing.T) { func TestLoadDocument_Simple_WithErrorOption_V2(t *testing.T) { yml := `swagger: 2.0.1` - _, err := NewDocument([]byte(yml), func(c *Configuration) error { return errors.New("test error") }) + _, 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) }