From e8a954d5aef2932fb99bca8a8421a2b6ee64db5d Mon Sep 17 00:00:00 2001 From: "Benjamin Nolan (TwoWholeWorms)" Date: Thu, 5 Jan 2023 14:07:39 +0100 Subject: [PATCH] feat: Resolver tests for valid and invalid recursive models --- resolver/resolver_test.go | 60 +++++++---- test_specs/circular-tests.yaml | 23 ++++- test_specs/swagger-circular-tests.yaml | 20 +++- .../swagger-invalid-recursive-model.yaml | 99 +++++++++++++++++++ test_specs/swagger-valid-recursive-model.yaml | 93 +++++++++++++++++ 5 files changed, 270 insertions(+), 25 deletions(-) create mode 100644 test_specs/swagger-invalid-recursive-model.yaml create mode 100644 test_specs/swagger-valid-recursive-model.yaml diff --git a/resolver/resolver_test.go b/resolver/resolver_test.go index e023cc9..8fb724f 100644 --- a/resolver/resolver_test.go +++ b/resolver/resolver_test.go @@ -2,11 +2,12 @@ package resolver import ( "fmt" + "io/ioutil" + "testing" + "github.com/pb33f/libopenapi/index" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" - "io/ioutil" - "testing" ) func TestNewResolver(t *testing.T) { @@ -25,7 +26,6 @@ func Benchmark_ResolveDocumentStripe(b *testing.B) { } func TestResolver_ResolveComponents_CircularSpec(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -43,7 +43,6 @@ func TestResolver_ResolveComponents_CircularSpec(t *testing.T) { } func TestResolver_CheckForCircularReferences(t *testing.T) { - circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml") var rootNode yaml.Node yaml.Unmarshal(circular, &rootNode) @@ -62,8 +61,41 @@ func TestResolver_CheckForCircularReferences(t *testing.T) { assert.NoError(t, err) } -func TestResolver_DeepJourney(t *testing.T) { +func TestResolver_CircularReferencesRequiredValid(t *testing.T) { + circular, _ := ioutil.ReadFile("../test_specs/swagger-valid-recursive-model.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) + index := index.NewSpecIndex(&rootNode) + + resolver := NewResolver(index) + assert.NotNil(t, resolver) + + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 0) + + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) +} + +func TestResolver_CircularReferencesRequiredInvalid(t *testing.T) { + circular, _ := ioutil.ReadFile("../test_specs/swagger-invalid-recursive-model.yaml") + var rootNode yaml.Node + yaml.Unmarshal(circular, &rootNode) + + index := index.NewSpecIndex(&rootNode) + + resolver := NewResolver(index) + assert.NotNil(t, resolver) + + circ := resolver.CheckForCircularReferences() + assert.Len(t, circ, 2) + + _, err := yaml.Marshal(resolver.resolvedRoot) + assert.NoError(t, err) +} + +func TestResolver_DeepJourney(t *testing.T) { var journey []*index.Reference for f := 0; f < 200; f++ { journey = append(journey, nil) @@ -71,11 +103,9 @@ func TestResolver_DeepJourney(t *testing.T) { index := index.NewSpecIndex(nil) resolver := NewResolver(index) assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false)) - } func TestResolver_ResolveComponents_Stripe(t *testing.T) { - stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") var rootNode yaml.Node yaml.Unmarshal(stripe, &rootNode) @@ -86,15 +116,13 @@ func TestResolver_ResolveComponents_Stripe(t *testing.T) { assert.NotNil(t, resolver) circ := resolver.Resolve() - assert.Len(t, circ, 23) + assert.Len(t, circ, 3) assert.Len(t, resolver.GetNonPolymorphicCircularErrors(), 3) assert.Len(t, resolver.GetPolymorphicCircularErrors(), 20) - } func TestResolver_ResolveComponents_BurgerShop(t *testing.T) { - mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml") var rootNode yaml.Node yaml.Unmarshal(mixedref, &rootNode) @@ -106,11 +134,9 @@ func TestResolver_ResolveComponents_BurgerShop(t *testing.T) { circ := resolver.Resolve() assert.Len(t, circ, 0) - } func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) { - yml := `paths: /hey: get: @@ -141,11 +167,9 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) - } func TestResolver_ResolveComponents_MixedRef(t *testing.T) { - mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") var rootNode yaml.Node yaml.Unmarshal(mixedref, &rootNode) @@ -157,11 +181,9 @@ func TestResolver_ResolveComponents_MixedRef(t *testing.T) { circ := resolver.Resolve() assert.Len(t, circ, 10) - } func TestResolver_ResolveComponents_k8s(t *testing.T) { - k8s, _ := ioutil.ReadFile("../test_specs/k8s.json") var rootNode yaml.Node yaml.Unmarshal(k8s, &rootNode) @@ -172,12 +194,11 @@ func TestResolver_ResolveComponents_k8s(t *testing.T) { assert.NotNil(t, resolver) circ := resolver.Resolve() - assert.Len(t, circ, 1) + assert.Len(t, circ, 0) } // Example of how to resolve the Stripe OpenAPI specification, and check for circular reference errors func ExampleNewResolver() { - // create a yaml.Node reference as a root node. var rootNode yaml.Node @@ -203,5 +224,4 @@ func ExampleNewResolver() { fmt.Printf("There are %d circular reference errors, %d of them are polymorphic errors, %d are not", len(circularErrors), len(resolver.GetPolymorphicCircularErrors()), len(resolver.GetNonPolymorphicCircularErrors())) // Output: There are 23 circular reference errors, 20 of them are polymorphic errors, 3 are not - -} \ No newline at end of file +} diff --git a/test_specs/circular-tests.yaml b/test_specs/circular-tests.yaml index 1b1cb14..beeb677 100644 --- a/test_specs/circular-tests.yaml +++ b/test_specs/circular-tests.yaml @@ -14,6 +14,8 @@ components: properties: things: "$ref": "#/components/schemas/Two" + required: + - things Two: description: "test two" properties: @@ -21,6 +23,9 @@ components: "$ref": "#/components/schemas/One" anyOf: - "$ref": "#/components/schemas/Four" + required: + - testThing + - anyOf Three: description: "test three" properties: @@ -30,30 +35,40 @@ components: "$ref": "#/components/schemas/Seven" yester: "$ref": "#/components/schemas/Seven" + required: + - tester + - bester + - yester Four: description: "test four" properties: lemons: "$ref": "#/components/schemas/Nine" + required: + - lemons Five: properties: rice: "$ref": "#/components/schemas/Six" + required: + - rice Six: properties: mints: "$ref": "#/components/schemas/Nine" + required: + - mints Seven: properties: wow: "$ref": "#/components/schemas/Three" + required: + - wow Nine: description: done. Ten: properties: yeah: "$ref": "#/components/schemas/Ten" - - - - + required: + - yeah diff --git a/test_specs/swagger-circular-tests.yaml b/test_specs/swagger-circular-tests.yaml index 9612165..53f437c 100644 --- a/test_specs/swagger-circular-tests.yaml +++ b/test_specs/swagger-circular-tests.yaml @@ -12,11 +12,15 @@ definitions: properties: things: "$ref": "#/definitions/Two" + required: + - things Two: description: "test two" properties: testThing: "$ref": "#/definitions/One" + required: + - testThing Three: description: "test three" properties: @@ -26,26 +30,40 @@ definitions: "$ref": "#/definitions/Seven" yester: "$ref": "#/definitions/Seven" + required: + - tester + - bester + - yester Four: description: "test four" properties: lemons: "$ref": "#/definitions/Nine" + required: + - lemons Five: properties: rice: "$ref": "#/definitions/Six" + required: + - rice Six: properties: mints: "$ref": "#/definitions/Nine" + required: + - mints Seven: properties: wow: "$ref": "#/definitions/Three" + required: + - wow Nine: description: done. Ten: properties: yeah: - "$ref": "#/definitions/Ten" \ No newline at end of file + "$ref": "#/definitions/Ten" + required: + - yeah diff --git a/test_specs/swagger-invalid-recursive-model.yaml b/test_specs/swagger-invalid-recursive-model.yaml new file mode 100644 index 0000000..efe4ca9 --- /dev/null +++ b/test_specs/swagger-invalid-recursive-model.yaml @@ -0,0 +1,99 @@ +schemes: + - https +securityDefinitions: + api_token: + type: apiKey + in: header + name: Swagger-Token +definitions: + BaseModel: + description: BaseModel is the top-level definition in this example + type: object + properties: + directChildren: + description: A nested array of direct recursive models. + type: array + items: + $ref: '#/definitions/DirectRecursiveModel' + indirectChildren: + description: A nested array of indirect recursive models. + type: array + items: + $ref: '#/definitions/IndirectRecursiveModelOne' + additionalProperties: false + required: + - directChildren + - indirectChildren + DirectRecursiveModel: + description: DirectRecursiveModel is a nested model which can optionally contain itself. + type: object + properties: + children: + description: A nested array of direct recursive models. + type: array + items: + $ref: '#/definitions/DirectRecursiveModel' + additionalProperties: false + required: + - children + IndirectRecursiveModelOne: + description: IndirectRecursiveModelOne is a nested model which can optionally contain IndirectRecursiveModelTwo. + type: object + properties: + children: + description: A nested array of indirect recursive models. + type: array + items: + $ref: '#/definitions/IndirectRecursiveModelTwo' + additionalProperties: false + required: + - children + IndirectRecursiveModelTwo: + description: IndirectRecursiveModelTwo is a nested model which can optionally contain IndirectRecursiveModelOne. + type: object + properties: + children: + description: A nested array of indirect recursive models. + type: array + items: + $ref: '#/definitions/IndirectRecursiveModelOne' + additionalProperties: false + required: + - children +security: + - api_token: [] +produces: + - application/json +paths: + /baseModels: + post: + parameters: + - in: body + name: BaseModel + description: '' + required: true + schema: + $ref: '#/definitions/BaseModel' + responses: + '201': + schema: + $ref: '#/definitions/BaseModel' + description: Resource + '400': + description: Schema mismatch + '404': + description: Resource does not exist + '422': + description: Unprocessable + operationId: createBaseModel + description: Create BaseModel allows you to create a new BaseModel + summary: Create BaseModel +consumes: + - application/json +host: api.app.example.com +info: + title: Invalid Recursive Definition Example + version: '1.0' + description: This example contains recursive model definitions which are invalid because their "children", "directChildren", and "indirectChildren" properties are all marked as required. +swagger: '2.0' +basePath: / diff --git a/test_specs/swagger-valid-recursive-model.yaml b/test_specs/swagger-valid-recursive-model.yaml new file mode 100644 index 0000000..b8898f2 --- /dev/null +++ b/test_specs/swagger-valid-recursive-model.yaml @@ -0,0 +1,93 @@ +schemes: + - https +securityDefinitions: + api_token: + type: apiKey + in: header + name: Swagger-Token +definitions: + BaseModel: + description: BaseModel is the top-level definition in this example + type: object + properties: + directChildren: + description: A nested array of direct recursive models. + type: array + items: + $ref: '#/definitions/DirectRecursiveModel' + indirectChildren: + description: A nested array of indirect recursive models. + type: array + items: + $ref: '#/definitions/IndirectRecursiveModelOne' + additionalProperties: false + required: + - directChildren + - indirectChildren + DirectRecursiveModel: + description: DirectRecursiveModel is a nested model which can optionally contain itself. + type: object + properties: + children: + description: A nested array of direct recursive models. + type: array + items: + $ref: '#/definitions/DirectRecursiveModel' + additionalProperties: false + IndirectRecursiveModelOne: + description: IndirectRecursiveModelOne is a nested model which can optionally contain IndirectRecursiveModelTwo. + type: object + properties: + children: + description: A nested array of indirect recursive models. + type: array + items: + $ref: '#/definitions/IndirectRecursiveModelTwo' + additionalProperties: false + IndirectRecursiveModelTwo: + description: IndirectRecursiveModelTwo is a nested model which can optionally contain IndirectRecursiveModelOne. + type: object + properties: + children: + description: A nested array of indirect recursive models. + type: array + items: + $ref: '#/definitions/IndirectRecursiveModelOne' + additionalProperties: false +security: + - api_token: [] +produces: + - application/json +paths: + /baseModels: + post: + parameters: + - in: body + name: BaseModel + description: '' + required: true + schema: + $ref: '#/definitions/BaseModel' + responses: + '201': + schema: + $ref: '#/definitions/BaseModel' + description: Resource + '400': + description: Schema mismatch + '404': + description: Resource does not exist + '422': + description: Unprocessable + operationId: createBaseModel + description: Create BaseModel allows you to create a new BaseModel + summary: Create BaseModel +consumes: + - application/json +host: api.app.example.com +info: + title: Valid Recursive Definition Example + version: '1.0' + description: This example contains a recursive model definition which is valid because DirectRecursiveModel's "children" property is optional. +swagger: '2.0' +basePath: /