From 26d3535e7500ce7b510f31962cc6e6e75157f975 Mon Sep 17 00:00:00 2001 From: Dave Shanley Date: Sat, 19 Nov 2022 15:05:37 -0500 Subject: [PATCH] Working through what-changed fixes and tuneups. more testing is required, however things feel quite reasonable and rounded. All changes correctly detected in a spec so far. --- datamodel/low/base/schema.go | 30 +- datamodel/low/base/schema_proxy.go | 6 +- datamodel/low/base/schema_test.go | 18 - datamodel/low/v3/create_document_test.go | 4 +- test_specs/burgershop.openapi-modified.yaml | 83 +- what-changed/model/comparison_functions.go | 37 +- what-changed/model/components.go | 142 +- what-changed/model/components_test.go | 677 +++------ what-changed/model/document.go | 18 +- what-changed/model/document_test.go | 1463 ++++++++++--------- what-changed/model/schema.go | 64 +- what-changed/model/schema_test.go | 102 ++ what-changed/what_changed_test.go | 4 +- 13 files changed, 1255 insertions(+), 1393 deletions(-) diff --git a/datamodel/low/base/schema.go b/datamodel/low/base/schema.go index 408e788..2b2544e 100644 --- a/datamodel/low/base/schema.go +++ b/datamodel/low/base/schema.go @@ -494,13 +494,20 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { _, addPLabel, addPNode := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content) if addPNode != nil { if utils.IsNodeMap(addPNode) { - schema, serr := low.ExtractObjectRaw[*Schema](addPNode, idx) - if serr != nil { - return serr + // check if this is a reference, or an inline schema. + isRef, _, _ := utils.IsNodeRefValue(addPNode) + sp := &SchemaProxy{ + kn: addPLabel, + vn: addPNode, + idx: idx, } - s.AdditionalProperties = low.NodeReference[any]{Value: schema, KeyNode: addPLabel, ValueNode: addPNode} + if isRef { + sp.isReference = true + _, vn := utils.FindKeyNodeTop("$ref", addPNode.Content) + sp.referenceLookup = vn.Value + } + s.AdditionalProperties = low.NodeReference[any]{Value: sp, KeyNode: addPLabel, ValueNode: addPNode} } - if utils.IsNodeBoolValue(addPNode) { b, _ := strconv.ParseBool(addPNode.Value) s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode} @@ -537,14 +544,15 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { // for property, build in a new thread! bChan := make(chan schemaProxyBuildResult) - var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult) { + var buildProperty = func(label *yaml.Node, value *yaml.Node, c chan schemaProxyBuildResult, isRef bool, + refString string) { c <- schemaProxyBuildResult{ k: low.KeyReference[string]{ KeyNode: label, Value: label.Value, }, v: low.ValueReference[*SchemaProxy]{ - Value: &SchemaProxy{kn: label, vn: value, idx: idx}, + Value: &SchemaProxy{kn: label, vn: value, idx: idx, isReference: isRef, referenceLookup: refString}, ValueNode: value, }, } @@ -563,17 +571,21 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error { } // check our prop isn't reference - if h, _, _ := utils.IsNodeRefValue(prop); h { + isRef := false + refString := "" + if h, _, l := utils.IsNodeRefValue(prop); h { ref, _ := low.LocateRefNode(prop, idx) if ref != nil { + isRef = true prop = ref + refString = l } else { return fmt.Errorf("schema properties build failed: cannot find reference %s, line %d, col %d", prop.Content[1].Value, prop.Content[1].Column, prop.Content[1].Line) } } totalProps++ - go buildProperty(currentProp, prop, bChan) + go buildProperty(currentProp, prop, bChan, isRef, refString) } completedProps := 0 for completedProps < totalProps { diff --git a/datamodel/low/base/schema_proxy.go b/datamodel/low/base/schema_proxy.go index d715cab..56c35f8 100644 --- a/datamodel/low/base/schema_proxy.go +++ b/datamodel/low/base/schema_proxy.go @@ -119,7 +119,11 @@ func (sp *SchemaProxy) GetValueNode() *yaml.Node { // Hash will return a consistent SHA256 Hash of the SchemaProxy object (it will resolve it) func (sp *SchemaProxy) Hash() [32]byte { if sp.rendered != nil { - return sp.rendered.Hash() + if !sp.isReference { + return sp.rendered.Hash() + } + // we only hash inline schemas + return sha256.Sum256([]byte(sp.referenceLookup)) } else { if !sp.isReference { // only resolve this proxy if it's not a ref. diff --git a/datamodel/low/base/schema_test.go b/datamodel/low/base/schema_test.go index ab41786..fff6db7 100644 --- a/datamodel/low/base/schema_test.go +++ b/datamodel/low/base/schema_test.go @@ -442,24 +442,6 @@ examples: // //} -func TestSchema_Build_ErrorAdditionalProps(t *testing.T) { - - yml := `additionalProperties: - $ref: #borko` - - var idxNode yaml.Node - _ = yaml.Unmarshal([]byte(yml), &idxNode) - idx := index.NewSpecIndex(&idxNode) - - var n Schema - err := low.BuildModel(&idxNode, &n) - assert.NoError(t, err) - - err = n.Build(idxNode.Content[0], idx) - assert.Error(t, err) - -} - func TestSchema_Build_PropsLookup(t *testing.T) { yml := `components: diff --git a/datamodel/low/v3/create_document_test.go b/datamodel/low/v3/create_document_test.go index 0097def..15e4ec5 100644 --- a/datamodel/low/v3/create_document_test.go +++ b/datamodel/low/v3/create_document_test.go @@ -488,8 +488,8 @@ func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) { components := doc.Components.Value d := components.FindSchema("Dressing") assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) - if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.Schema); ok { - assert.Equal(t, "something in here.", n.Description.Value) + if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.SchemaProxy); ok { + assert.Equal(t, "something in here.", n.Schema().Description.Value) } else { assert.Fail(t, "should be a schema") } diff --git a/test_specs/burgershop.openapi-modified.yaml b/test_specs/burgershop.openapi-modified.yaml index b835cf9..d1e24e1 100644 --- a/test_specs/burgershop.openapi-modified.yaml +++ b/test_specs/burgershop.openapi-modified.yaml @@ -191,13 +191,13 @@ paths: - in: query name: burgerId schema: - type: string - example: big-mac + type: int + example: 12345 description: the name of the our fantastic burger. You can pick a name from our menu required: true responses: "200": - $ref: '#/components/responses/DressingResponse' + $ref: '#/components/schemas/Error' "404": description: Cannot find your burger in which to list dressings. Sorry content: @@ -207,7 +207,7 @@ paths: example: message: There is no burger here "500": - description: Unexpected error listing dressings for burger. Sorry. + description: Unexpected error listing dressings for burger. Sorry friend content: application/json: schema: @@ -219,6 +219,7 @@ paths: operationId: getDressing tags: - "Dressing" + - "Pepsi" summary: Get a specific dressing - you can choose the dressing from our menu description: Same as the summary, get a dressing, by its ID parameters: @@ -226,10 +227,18 @@ paths: name: dressingId schema: type: string - example: cheese + example: cheesey description: This is the unique identifier for the dressing items. required: true responses: + "201": + description: a dressing again with a different code + content: + application/json: + schema: + $ref: '#/components/schemas/Dressing' + example: + name: Butter Sauce "200": description: a dressing content: @@ -291,7 +300,7 @@ paths: components: callbacks: BurgerCallback: - x-break-everything: please + x-break-everything: please change me "{$request.query.queryUrl}": post: requestBody: @@ -305,12 +314,12 @@ components: description: callback successfully processes links: LocateBurger: - operationId: locateBurger + operationId: locateBurgers parameters: burgerId: '$response.body#/id' description: Go and get a tasty burger AnotherLocateBurger: - operationId: locateBurger + operationId: locateBurgers parameters: burgerId: '$response.body#/id' description: Go and get another really tasty burger @@ -323,7 +332,7 @@ components: type: string requestBodies: BurgerRequest: - description: Give us the new burger! + description: Give us the new burger! please! content: application/json: schema: @@ -341,12 +350,17 @@ components: numPatties: 5 examples: QuarterPounder: - summary: A juicy two hander sammich + summary: A juicy two hander sammich yummy + value: + name: Quarter Pounder with Cheese + numPatties: 1 + QuarterPounder2: + summary: A juicy two hander sammich yummy value: name: Quarter Pounder with Cheese numPatties: 1 responses: - DressingResponse: + DressingResponse2: description: all the dressings for a burger. content: application/json: @@ -359,11 +373,11 @@ components: securitySchemes: APIKeyScheme: type: apiKey - description: an apiKey security scheme + description: an apiKey security scheme updated name: apiKeyScheme in: query JWTScheme: - type: http + type: https description: an JWT security scheme name: aJWTThing scheme: bearer @@ -377,9 +391,9 @@ components: authorizationUrl: https://pb33f.io/oauth scopes: write:burgers: modify and add new burgers - read:burgers: read all burgers + read:burgers: read all burgers changed authorizationCode: - authorizationUrl: https://pb33f.io/oauth + authorizationUrl: https://pb33f.io/oauth/changed tokenUrl: https://api.pb33f.io/oauth/token scopes: write:burgers: modify burgers and stuff @@ -392,7 +406,7 @@ components: properties: burgerTheme: type: string - description: something about a theme goes in here? + description: something about a theme goes in here? changed burgerTime: type: number description: number of burgers ordered so far this year. @@ -428,6 +442,14 @@ components: example: big-mac description: the name of the burger. use this to order your tasty burger required: true + BurgerId2: + in: path + name: burgerId + schema: + type: string + example: big-mac + description: the name of the burger. use this to order your tasty burger + required: true schemas: Error: type: object @@ -435,11 +457,11 @@ components: properties: message: type: string - description: returns the error message if something wrong happens + description: returns the error message if something wrong happens, updated. example: No such burger as 'Big-Whopper' Burger: type: object - description: The tastiest food on the planet you would love to eat everyday + description: The tastiest food on the planet you would love to eat everyday, yum! required: - name - numPatties @@ -447,7 +469,7 @@ components: name: type: string description: The name of your tasty burger - burger names are listed in our menus - example: Big Mac + example: Big Mac and fries! numPatties: type: integer description: The number of burger patties used @@ -464,13 +486,14 @@ components: required: - potatoShape - favoriteDrink + - seasoning properties: seasoning: type: array - description: herbs and spices for your golden joy + description: herbs and spices for your golden joy and fun items: type: string - description: type of herb or spice used to liven up the yummy + description: type of herb or spice used to liven up the yummy things example: salt potatoShape: type: string @@ -490,7 +513,7 @@ components: example: Cheese additionalProperties: type: object - description: something in here. + description: something in here. please Drink: type: object description: a frosty cold beverage can be coke or sprite @@ -521,8 +544,6 @@ components: name: is html programming? yes. externalDocs: url: https://pb33f.io/docs - oneOf: - - $ref: '#/components/schemas/Drink' anyOf: - $ref: '#/components/schemas/Drink' allOf: @@ -531,21 +552,23 @@ components: type: string items: - $ref: '#/components/schemas/Drink' - x-screaming-baby: loud -x-something-something: darkside + x-screaming-baby: very loud +x-something-something: darkside changed externalDocs: description: "Find out more information about our products and services" - url: "https://pb33f.io" -jsonSchemaDialect: https://pb33f.io/schema + url: "https://pb33f.io/new" +jsonSchemaDialect: https://pb33f.io/schema/new webhooks: someHook: post: requestBody: - description: Information about a new burger + description: Information about a new burger but updated content: application/json: schema: $ref: "#/components/schemas/Burger" responses: "200": - description: the hook is good! you have a new burger. \ No newline at end of file + description: the hook is good! you have a new burger. + "201": + description: a new thing \ No newline at end of file diff --git a/what-changed/model/comparison_functions.go b/what-changed/model/comparison_functions.go index 5d5a01c..a787ba8 100644 --- a/what-changed/model/comparison_functions.go +++ b/what-changed/model/comparison_functions.go @@ -186,6 +186,34 @@ func CheckForModification[T any](l, r *yaml.Node, label string, changes *[]*Chan // values. The compareFunc argument should reference the correct comparison function for the generic type. func CheckMapForChanges[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], changes *[]*Change, label string, compareFunc func(l, r T) R) map[string]R { + return CheckMapForChangesWithComp(expLeft, expRight, changes, label, compareFunc, true) +} + +func CheckMapForAdditionRemoval[T any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], + changes *[]*Change, label string) any { + // do nothing + doNothing := func(l, r T) any { + return nil + } + return CheckMapForChangesWithComp(expLeft, expRight, changes, label, doNothing, false) +} + +//// CheckMapForAdditionRemoval checks a left and right low level map for any additions or subtractions, but not modifications +//func CheckMapForAdditionRemoval[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], +// changes *[]*Change, label string) map[string]R { +// +// // do nothing +// doNothing := func(l, r T) R { +// return nil +// } +// return CheckMapForChangesWithComp(expLeft, expRight, changes, label, doNothing, false) +//} + +// CheckMapForChangesWithComp checks a left and right low level map for any additions, subtractions or modifications to +// values. The compareFunc argument should reference the correct comparison function for the generic type. The compare +// bit determines if the comparison should be run or not. +func CheckMapForChangesWithComp[T any, R any](expLeft, expRight map[low.KeyReference[string]]low.ValueReference[T], + changes *[]*Change, label string, compareFunc func(l, r T) R, compare bool) map[string]R { // stop concurrent threads screwing up changes. var chLock sync.Mutex @@ -226,10 +254,11 @@ func CheckMapForChanges[T any, R any](expLeft, expRight map[low.KeyReference[str return } // run comparison. - chLock.Lock() - expChanges[k] = compareFunc(p[k].Value, h[k].Value) - chLock.Unlock() - + if compare { + chLock.Lock() + expChanges[k] = compareFunc(p[k].Value, h[k].Value) + chLock.Unlock() + } doneChan <- true } diff --git a/what-changed/model/components.go b/what-changed/model/components.go index 4cea55b..1a7612e 100644 --- a/what-changed/model/components.go +++ b/what-changed/model/components.go @@ -13,16 +13,19 @@ import ( type ComponentsChanges struct { PropertyChanges - SchemaChanges map[string]*SchemaChanges - ResponsesChanges map[string]*ResponseChanges - ParameterChanges map[string]*ParameterChanges - ExamplesChanges map[string]*ExampleChanges - RequestBodyChanges map[string]*RequestBodyChanges - HeaderChanges map[string]*HeaderChanges + SchemaChanges map[string]*SchemaChanges + + // todo: disable these after cleaning up swagger code. + ResponsesChanges map[string]*ResponseChanges + ParameterChanges map[string]*ParameterChanges + + //ExamplesChanges map[string]*ExampleChanges + //RequestBodyChanges map[string]*RequestBodyChanges + //HeaderChanges map[string]*HeaderChanges SecuritySchemeChanges map[string]*SecuritySchemeChanges - LinkChanges map[string]*LinkChanges - CallbackChanges map[string]*CallbackChanges - ExtensionChanges *ExtensionChanges + //LinkChanges map[string]*LinkChanges + //CallbackChanges map[string]*CallbackChanges + ExtensionChanges *ExtensionChanges } func CompareComponents(l, r any) *ComponentsChanges { @@ -43,7 +46,7 @@ func CompareComponents(l, r any) *ComponentsChanges { if rDef != nil { b = rDef.Definitions } - cc.ParameterChanges = CheckMapForChanges(a, b, &changes, v3.ParametersLabel, CompareParametersV2) + CheckMapForAdditionRemoval(a, b, &changes, v3.ParametersLabel) } // Swagger Responses @@ -58,7 +61,7 @@ func CompareComponents(l, r any) *ComponentsChanges { if rDef != nil { b = rDef.Definitions } - cc.ResponsesChanges = CheckMapForChanges(a, b, &changes, v3.ResponsesLabel, CompareResponseV2) + CheckMapForAdditionRemoval(a, b, &changes, v3.ResponsesLabel) } // Swagger Schemas @@ -73,8 +76,7 @@ func CompareComponents(l, r any) *ComponentsChanges { if rDef != nil { b = rDef.Schemas } - cc.SchemaChanges = CheckMapForChanges(a, b, &changes, - v2.DefinitionsLabel, CompareSchemas) + cc.SchemaChanges = CheckMapForChanges(a, b, &changes, v2.DefinitionsLabel, CompareSchemas) } // Swagger Security Definitions @@ -100,9 +102,9 @@ func CompareComponents(l, r any) *ComponentsChanges { lComponents := l.(*v3.Components) rComponents := r.(*v3.Components) - if low.AreEqual(lComponents, rComponents) { - return nil - } + //if low.AreEqual(lComponents, rComponents) { + // return nil + //} doneChan := make(chan componentComparison) comparisons := 0 @@ -175,23 +177,23 @@ func CompareComponents(l, r any) *ComponentsChanges { break case v3.ResponsesLabel: completedComponents++ - cc.ResponsesChanges = res.result.(map[string]*ResponseChanges) + //cc.ResponsesChanges = res.result.(map[string]*ResponseChanges) break case v3.ParametersLabel: completedComponents++ - cc.ParameterChanges = res.result.(map[string]*ParameterChanges) + //cc.ParameterChanges = res.result.(map[string]*ParameterChanges) break case v3.ExamplesLabel: completedComponents++ - cc.ExamplesChanges = res.result.(map[string]*ExampleChanges) + //cc.ExamplesChanges = res.result.(map[string]*ExampleChanges) break case v3.RequestBodiesLabel: completedComponents++ - cc.RequestBodyChanges = res.result.(map[string]*RequestBodyChanges) + //cc.RequestBodyChanges = res.result.(map[string]*RequestBodyChanges) break case v3.HeadersLabel: completedComponents++ - cc.HeaderChanges = res.result.(map[string]*HeaderChanges) + //cc.HeaderChanges = res.result.(map[string]*HeaderChanges) break case v3.SecuritySchemesLabel: completedComponents++ @@ -199,11 +201,11 @@ func CompareComponents(l, r any) *ComponentsChanges { break case v3.LinksLabel: completedComponents++ - cc.LinkChanges = res.result.(map[string]*LinkChanges) + //cc.LinkChanges = res.result.(map[string]*LinkChanges) break case v3.CallbacksLabel: completedComponents++ - cc.CallbackChanges = res.result.(map[string]*CallbackChanges) + //cc.CallbackChanges = res.result.(map[string]*CallbackChanges) break } } @@ -225,9 +227,19 @@ type componentComparison struct { // run a generic comparison in a thread which in turn splits checks into further threads. func runComparison[T any, R any](l, r map[low.KeyReference[string]]low.ValueReference[T], changes *[]*Change, label string, compareFunc func(l, r T) R, doneChan chan componentComparison) { - doneChan <- componentComparison{ - prop: label, - result: CheckMapForChanges(l, r, changes, label, compareFunc), + + // for schemas + if label == v3.SchemasLabel || label == v2.DefinitionsLabel || label == v3.SecuritySchemesLabel { + doneChan <- componentComparison{ + prop: label, + result: CheckMapForChanges(l, r, changes, label, compareFunc), + } + return + } else { + doneChan <- componentComparison{ + prop: label, + result: CheckMapForAdditionRemoval(l, r, changes, label), + } } } @@ -236,30 +248,30 @@ func (c *ComponentsChanges) TotalChanges() int { for k := range c.SchemaChanges { v += c.SchemaChanges[k].TotalChanges() } - for k := range c.ResponsesChanges { - v += c.ResponsesChanges[k].TotalChanges() - } - for k := range c.ParameterChanges { - v += c.ParameterChanges[k].TotalChanges() - } - for k := range c.ExamplesChanges { - v += c.ExamplesChanges[k].TotalChanges() - } - for k := range c.RequestBodyChanges { - v += c.RequestBodyChanges[k].TotalChanges() - } - for k := range c.HeaderChanges { - v += c.HeaderChanges[k].TotalChanges() - } + //for k := range c.ResponsesChanges { + // v += c.ResponsesChanges[k].TotalChanges() + //} + //for k := range c.ParameterChanges { + // v += c.ParameterChanges[k].TotalChanges() + //} + //for k := range c.ExamplesChanges { + // v += c.ExamplesChanges[k].TotalChanges() + //} + //for k := range c.RequestBodyChanges { + // v += c.RequestBodyChanges[k].TotalChanges() + //} + //for k := range c.HeaderChanges { + // v += c.HeaderChanges[k].TotalChanges() + //} for k := range c.SecuritySchemeChanges { v += c.SecuritySchemeChanges[k].TotalChanges() } - for k := range c.LinkChanges { - v += c.LinkChanges[k].TotalChanges() - } - for k := range c.CallbackChanges { - v += c.CallbackChanges[k].TotalChanges() - } + //for k := range c.LinkChanges { + // v += c.LinkChanges[k].TotalChanges() + //} + //for k := range c.CallbackChanges { + // v += c.CallbackChanges[k].TotalChanges() + //} if c.ExtensionChanges != nil { v += c.ExtensionChanges.TotalChanges() } @@ -271,26 +283,26 @@ func (c *ComponentsChanges) TotalBreakingChanges() int { for k := range c.SchemaChanges { v += c.SchemaChanges[k].TotalBreakingChanges() } - for k := range c.ResponsesChanges { - v += c.ResponsesChanges[k].TotalBreakingChanges() - } - for k := range c.ParameterChanges { - v += c.ParameterChanges[k].TotalBreakingChanges() - } - for k := range c.RequestBodyChanges { - v += c.RequestBodyChanges[k].TotalBreakingChanges() - } - for k := range c.HeaderChanges { - v += c.HeaderChanges[k].TotalBreakingChanges() - } + //for k := range c.ResponsesChanges { + // v += c.ResponsesChanges[k].TotalBreakingChanges() + //} + //for k := range c.ParameterChanges { + // v += c.ParameterChanges[k].TotalBreakingChanges() + //} + //for k := range c.RequestBodyChanges { + // v += c.RequestBodyChanges[k].TotalBreakingChanges() + //} + //for k := range c.HeaderChanges { + // v += c.HeaderChanges[k].TotalBreakingChanges() + //} for k := range c.SecuritySchemeChanges { v += c.SecuritySchemeChanges[k].TotalBreakingChanges() } - for k := range c.LinkChanges { - v += c.LinkChanges[k].TotalBreakingChanges() - } - for k := range c.CallbackChanges { - v += c.CallbackChanges[k].TotalBreakingChanges() - } + //for k := range c.LinkChanges { + // v += c.LinkChanges[k].TotalBreakingChanges() + //} + //for k := range c.CallbackChanges { + // v += c.CallbackChanges[k].TotalBreakingChanges() + //} return v } diff --git a/what-changed/model/components_test.go b/what-changed/model/components_test.go index 703a411..27bdaa4 100644 --- a/what-changed/model/components_test.go +++ b/what-changed/model/components_test.go @@ -5,7 +5,7 @@ package model import ( "github.com/pb33f/libopenapi/datamodel/low" - "github.com/pb33f/libopenapi/datamodel/low/v2" + v2 "github.com/pb33f/libopenapi/datamodel/low/v2" "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/pb33f/libopenapi/index" "github.com/pb33f/libopenapi/resolver" @@ -17,18 +17,18 @@ import ( func TestCompareComponents_Swagger_Definitions_Equal(t *testing.T) { left := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing.` + type: int + description: a thing +thing2: + type: string + description: another thing.` right := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing.` + type: int + description: a thing +thing2: + type: string + description: another thing.` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -50,18 +50,15 @@ thing2: func TestCompareComponents_Swagger_Definitions_Modified(t *testing.T) { left := `thing1: - type: int - description: a thing -thing2: - type: int - description: another thing.` + type: int + description: a thing +thing2: + type: int + description: another thing.` right := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing.` + type: int + description: a thing that changed` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -77,30 +74,29 @@ thing2: // compare. extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 2, extChanges.TotalChanges()) assert.Equal(t, 1, extChanges.TotalBreakingChanges()) - assert.Equal(t, 1, extChanges.SchemaChanges["thing2"].TotalChanges()) } func TestCompareComponents_Swagger_Definitions_Added(t *testing.T) { left := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing.` + type: int + description: a thing +thing2: + type: string + description: another thing.` right := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing. + type: int + description: a thing +thing2: + type: string + description: another thing. thing3: - type: int - description: added a thing` + type: int + description: added a thing` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -125,21 +121,21 @@ thing3: func TestCompareComponents_Swagger_Definitions_Removed(t *testing.T) { left := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing.` + type: int + description: a thing +thing2: + type: string + description: another thing.` right := `thing1: - type: int - description: a thing -thing2: - type: string - description: another thing. + type: int + description: a thing +thing2: + type: string + description: another thing. thing3: - type: int - description: added a thing` + type: int + description: added a thing` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -161,95 +157,23 @@ thing3: assert.Equal(t, "thing3", extChanges.Changes[0].Original) } -func TestCompareComponents_Swagger_Parameters_Equal(t *testing.T) { - - left := `param1: - name: nap -param2: - name: sleep -param3: - name: snooze -` - right := `param1: - name: nap -param2: - name: sleep -param3: - name: snooze` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v2.ParameterDefinitions - var rDoc v2.ParameterDefinitions - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareComponents_Swagger_Parameters_Modified(t *testing.T) { - - left := `param1: - name: nap -param2: - name: sleep -param3: - name: snooze -` - right := `param1: - name: WIDE AWAKE -param2: - name: sleep -param3: - name: KINDA SNOOZ` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v2.ParameterDefinitions - var rDoc v2.ParameterDefinitions - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) - assert.Equal(t, Modified, extChanges.ParameterChanges["param1"].Changes[0].ChangeType) - assert.Equal(t, "WIDE AWAKE", extChanges.ParameterChanges["param1"].Changes[0].New) - assert.Equal(t, "KINDA SNOOZ", extChanges.ParameterChanges["param3"].Changes[0].New) - assert.Equal(t, v3.NameLabel, extChanges.ParameterChanges["param1"].Changes[0].Property) - -} - func TestCompareComponents_Swagger_Parameters_Added(t *testing.T) { left := `param1: - name: nap + name: nap param2: - name: sleep -param3: - name: snooze + name: sleep +param3: + name: snooze ` right := `param1: - name: nap + name: nap param2: - name: sleep -param3: - name: snooze + name: sleep +param3: + name: snooze param4: - name: I woke up!` + name: I woke up!` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -274,20 +198,20 @@ param4: func TestCompareComponents_Swagger_Parameters_Removed(t *testing.T) { left := `param1: - name: nap + name: nap param2: - name: sleep -param3: - name: snooze + name: sleep +param3: + name: snooze ` right := `param1: - name: nap + name: nap param2: - name: sleep -param3: - name: snooze + name: sleep +param3: + name: snooze param4: - name: I woke up!` + name: I woke up!` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -309,81 +233,19 @@ param4: assert.Equal(t, "param4", extChanges.Changes[0].Original) } -func TestCompareComponents_Swagger_Responses_Equal(t *testing.T) { - - left := `resp1: - description: hi! -resp2: - description: bye! -` - right := `resp1: - description: hi! -resp2: - description: bye!` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v2.ResponsesDefinitions - var rDoc v2.ResponsesDefinitions - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareComponents_Swagger_Responses_Modified(t *testing.T) { - - left := `resp1: - description: hi! -resp2: - description: bye! -` - right := `resp1: - description: hi! -resp2: - description: oh, so you want to change huh?` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v2.ResponsesDefinitions - var rDoc v2.ResponsesDefinitions - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, 1, extChanges.ResponsesChanges["resp2"].TotalChanges()) - assert.Equal(t, v3.DescriptionLabel, extChanges.ResponsesChanges["resp2"].Changes[0].Property) -} - func TestCompareComponents_Swagger_Responses_Added(t *testing.T) { left := `resp1: - description: hi! + description: hi! resp2: - description: bye! + description: bye! ` right := `resp1: - description: hi! + description: hi! resp2: - description: bye! -resp3: - description: another response!` + description: bye! +resp3: + description: another response!` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -410,16 +272,16 @@ resp3: func TestCompareComponents_Swagger_Responses_Removed(t *testing.T) { left := `resp1: - description: hi! + description: hi! resp2: - description: bye! + description: bye! ` right := `resp1: - description: hi! + description: hi! resp2: - description: bye! -resp3: - description: another response!` + description: bye! +resp3: + description: another response!` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -443,46 +305,15 @@ resp3: assert.Equal(t, v2.ResponsesLabel, extChanges.Changes[0].Property) } -func TestCompareComponents_Swagger_SecurityDefinitions_Equal(t *testing.T) { - - left := `scheme1: - description: hi! -scheme2: - description: bye! -` - right := `scheme1: - description: hi! -scheme2: - description: bye!` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v2.SecurityDefinitions - var rDoc v2.SecurityDefinitions - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - func TestCompareComponents_Swagger_SecurityDefinitions_Modified(t *testing.T) { left := `scheme1: - description: hi! -scheme2: - description: bye! -` + description: hi!` + right := `scheme1: - description: hi! + description: hi! again scheme2: - description: bye! again!` + description: bye! again!` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -498,10 +329,10 @@ scheme2: // compare. extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 2, extChanges.TotalChanges()) assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, 1, extChanges.SecuritySchemeChanges["scheme2"].TotalChanges()) - assert.Equal(t, v3.DescriptionLabel, extChanges.SecuritySchemeChanges["scheme2"].Changes[0].Property) + assert.Equal(t, 1, extChanges.SecuritySchemeChanges["scheme1"].TotalChanges()) + assert.Equal(t, v3.DescriptionLabel, extChanges.SecuritySchemeChanges["scheme1"].Changes[0].Property) } func TestCompareComponents_OpenAPI_Schemas_Equal(t *testing.T) { @@ -704,12 +535,11 @@ responses: } func TestCompareComponents_OpenAPI_Responses_FullBuild(t *testing.T) { - left := `components: - responses: - coffee: - description: tasty - tv: - $ref: '#/components/responses/coffee'` + left := `responses: + coffee: + description: tasty + tv: + $ref: '#/components/responses/coffee'` right := left @@ -734,6 +564,44 @@ func TestCompareComponents_OpenAPI_Responses_FullBuild(t *testing.T) { assert.Nil(t, extChanges) } +func TestCompareComponents_OpenAPI_ResponsesAdd_FullBuild(t *testing.T) { + left := `responses: + coffee: + description: tasty + tv: + $ref: '#/responses/coffee'` + + right := `responses: + herbs: + description: refreshing + coffee: + description: tasty + tv: + $ref: '#/responses/coffee'` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // create low level objects + var lDoc v3.Components + var rDoc v3.Components + + _ = low.BuildModel(lNode.Content[0], &lDoc) + _ = low.BuildModel(rNode.Content[0], &rDoc) + + idx := index.NewSpecIndex(&lNode) + + _ = lDoc.Build(lNode.Content[0], idx) + _ = rDoc.Build(rNode.Content[0], idx) + + // compare. + extChanges := CompareComponents(&lDoc, &rDoc) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) + +} + func TestCompareComponents_OpenAPI_Responses_FullBuild_IdenticalRef(t *testing.T) { left := `components: responses: @@ -815,37 +683,37 @@ func TestCompareComponents_OpenAPI_Responses_FullBuild_CircularRef(t *testing.T) assert.Nil(t, extChanges) } -func TestCompareComponents_OpenAPI_Responses_Modify(t *testing.T) { - - left := `responses: - niceResponse: - description: hello - badResponse: - description: go away please` - - right := `responses: - niceResponse: - description: hello my matey - badResponse: - description: go away please, now!` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&rDoc, &lDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) -} +//func TestCompareComponents_OpenAPI_Responses_Modify(t *testing.T) { +// +// left := `responses: +// niceResponse: +// description: hello +// badResponse: +// description: go away please` +// +// right := `responses: +// niceResponse: +// description: hello my matey +// badResponse: +// description: go away please, now!` +// +// var lNode, rNode yaml.Node +// _ = yaml.Unmarshal([]byte(left), &lNode) +// _ = yaml.Unmarshal([]byte(right), &rNode) +// +// // create low level objects +// var lDoc v3.Components +// var rDoc v3.Components +// _ = low.BuildModel(lNode.Content[0], &lDoc) +// _ = low.BuildModel(rNode.Content[0], &rDoc) +// _ = lDoc.Build(lNode.Content[0], nil) +// _ = rDoc.Build(rNode.Content[0], nil) +// +// // compare. +// extChanges := CompareComponents(&rDoc, &lDoc) +// assert.Equal(t, 2, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +//} func TestCompareComponents_OpenAPI_Responses_Add(t *testing.T) { @@ -944,38 +812,6 @@ func TestCompareComponents_OpenAPI_Parameters_Equal(t *testing.T) { assert.Nil(t, extChanges) } -func TestCompareComponents_OpenAPI_Parameters_Modified(t *testing.T) { - - left := `parameters: - param1: - name: a parameter - param2: - name: another param` - - right := `parameters: - param1: - name: a parameter modified - param2: - name: another param but modified` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) -} - func TestCompareComponents_OpenAPI_Parameters_Added(t *testing.T) { left := `parameters: @@ -1046,99 +882,11 @@ func TestCompareComponents_OpenAPI_Parameters_Removed(t *testing.T) { assert.Equal(t, "param3", extChanges.Changes[0].Original) } -func TestCompareComponents_OpenAPI_Examples_Equal(t *testing.T) { - - left := `examples: - example1: - description: an example - example2: - description: another example` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareComponents_OpenAPI_Examples_Modified(t *testing.T) { - - left := `examples: - example1: - description: an example - example2: - description: another example` - - right := `examples: - example1: - description: change me - example2: - description: grow me` - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) -} - -func TestCompareComponents_OpenAPI_RequestBodies_Equal(t *testing.T) { - - left := `requestBodies: - body1: - description: a request - body2: - description: another request` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - func TestCompareComponents_OpenAPI_RequestBodies_Modified(t *testing.T) { left := `requestBodies: body1: - description: a request - body2: - description: another request` + description: a request` right := `requestBodies: body1: @@ -1160,50 +908,21 @@ func TestCompareComponents_OpenAPI_RequestBodies_Modified(t *testing.T) { // compare. extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) + assert.Equal(t, 1, extChanges.TotalChanges()) assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } -func TestCompareComponents_OpenAPI_Headers_Equal(t *testing.T) { +func TestCompareComponents_OpenAPI_Headers_Add(t *testing.T) { left := `headers: header1: - description: a header - header2: - description: another header` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareComponents_OpenAPI_Headers_Modified(t *testing.T) { - - left := `headers: - header1: - description: a header - header2: - description: another header` + description: a header` right := `headers: header1: - description: a header but different + description: a header header2: - description: another header, also different` + description: another header` var lNode, rNode yaml.Node _ = yaml.Unmarshal([]byte(left), &lNode) @@ -1219,7 +938,7 @@ func TestCompareComponents_OpenAPI_Headers_Modified(t *testing.T) { // compare. extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) + assert.Equal(t, 1, extChanges.TotalChanges()) assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } @@ -1282,40 +1001,11 @@ func TestCompareComponents_OpenAPI_SecuritySchemes_Modified(t *testing.T) { assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } -func TestCompareComponents_OpenAPI_Links_Equal(t *testing.T) { +func TestCompareComponents_OpenAPI_Links_Added(t *testing.T) { left := `links: link1: - operationId: link1 - link2: - operationId: link2` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareComponents_OpenAPI_Links_Modified(t *testing.T) { - - left := `links: - link1: - operationId: link1 - link2: - operationId: link2` + operationId: link1` right := `links: link1: @@ -1337,35 +1027,8 @@ func TestCompareComponents_OpenAPI_Links_Modified(t *testing.T) { // compare. extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) -} - -func TestCompareComponents_OpenAPI_Callbacks_Equal(t *testing.T) { - - left := `callbacks: - link1: - url: https://pb33f.io - link2: - url: https://pb33f.io` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v3.Components - var rDoc v3.Components - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - _ = lDoc.Build(lNode.Content[0], nil) - _ = rDoc.Build(rNode.Content[0], nil) - - // compare. - extChanges := CompareComponents(&lDoc, &rDoc) - assert.Nil(t, extChanges) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } func TestCompareComponents_OpenAPI_Callbacks_Modified(t *testing.T) { @@ -1374,11 +1037,7 @@ func TestCompareComponents_OpenAPI_Callbacks_Modified(t *testing.T) { link1: '{$request.query.queryUrl}': post: - description: a nice callback - link2: - '{$pizza.cake.burgers}': - post: - description: pizza and cake, and burgers.` + description: a nice callback` right := `callbacks: link1: @@ -1404,8 +1063,8 @@ func TestCompareComponents_OpenAPI_Callbacks_Modified(t *testing.T) { // compare. extChanges := CompareComponents(&lDoc, &rDoc) - assert.Equal(t, 3, extChanges.TotalChanges()) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) } func TestCompareComponents_OpenAPI_Extensions_Modified(t *testing.T) { diff --git a/what-changed/model/document.go b/what-changed/model/document.go index 6ef22bf..e603483 100644 --- a/what-changed/model/document.go +++ b/what-changed/model/document.go @@ -151,15 +151,15 @@ func CompareDocuments(l, r any) *DocumentChanges { if n := CompareComponents(lDoc.Definitions.Value, rDoc.Definitions.Value); n != nil { cc.SchemaChanges = n.SchemaChanges } - if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil { - cc.SecuritySchemeChanges = n.SecuritySchemeChanges - } - if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil { - cc.ParameterChanges = n.ParameterChanges - } - if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil { - cc.ResponsesChanges = n.ResponsesChanges - } + //if n := CompareComponents(lDoc.SecurityDefinitions.Value, rDoc.SecurityDefinitions.Value); n != nil { + // cc.SecuritySchemeChanges = n.SecuritySchemeChanges + //} + //if n := CompareComponents(lDoc.Parameters.Value, rDoc.Parameters.Value); n != nil { + // cc.ParameterChanges = n.ParameterChanges + //} + //if n := CompareComponents(lDoc.Responses.Value, rDoc.Responses.Value); n != nil { + // cc.ResponsesChanges = n.ResponsesChanges + //} dc.ExtensionChanges = CompareExtensions(lDoc.Extensions, rDoc.Extensions) if cc.TotalChanges() > 0 { dc.ComponentsChanges = cc diff --git a/what-changed/model/document_test.go b/what-changed/model/document_test.go index 09d7401..ab8b248 100644 --- a/what-changed/model/document_test.go +++ b/what-changed/model/document_test.go @@ -5,730 +5,723 @@ package model import ( "github.com/pb33f/libopenapi/datamodel" - "github.com/pb33f/libopenapi/datamodel/low" - v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" "github.com/stretchr/testify/assert" "gopkg.in/yaml.v3" "testing" ) -func TestCompareDocuments_Swagger_BaseProperties_Identical(t *testing.T) { - left := `swagger: 2.0 -x-diet: tough -host: https://pb33f.io -basePath: /api -schemes: - - http - - https -consumes: - - application/json - - apple/pie -produces: - - application/json - - fat/belly` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // create low level objects - var lDoc v2.Swagger - var rDoc v2.Swagger - _ = low.BuildModel(lNode.Content[0], &lDoc) - _ = low.BuildModel(rNode.Content[0], &rDoc) - - // compare. - extChanges := CompareDocuments(&lDoc, &rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_BaseProperties_Modified(t *testing.T) { - left := `swagger: 2.0 -x-diet: coke -host: https://pb33f.io -basePath: /api -schemes: - - http - - https -consumes: - - application/json - - apple/pie -produces: - - application/json - - fat/belly` - - right := `swagger: 2.0.1 -x-diet: pepsi -host: https://quobix.com -basePath: /new-api -schemes: - - ws - - https -consumes: - - application/json - - apple/ice-cream -produces: - - application/json - - very-fat/belly` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 10, extChanges.TotalChanges()) - assert.Equal(t, 6, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Info_Modified(t *testing.T) { - left := `swagger: 2.0 -info: - title: a doc - contact: - name: buckaroo - license: - url: https://pb33f.io` - - right := `swagger: 2.0 -info: - title: a doc that changed - contact: - name: chief buckaroo - license: - url: https://pb33f.io/license` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 3, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, 3, extChanges.InfoChanges.TotalChanges()) -} - -func TestCompareDocuments_Swagger_Info_Added(t *testing.T) { - left := `swagger: 2.0` - - right := `swagger: 2.0 -info: - title: a doc that changed - contact: - name: chief buckaroo - license: - url: https://pb33f.io/license` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, v3.InfoLabel, extChanges.Changes[0].Property) - assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) -} - -func TestCompareDocuments_Swagger_Info_Removed(t *testing.T) { - left := `swagger: 2.0` - - right := `swagger: 2.0 -info: - title: a doc that changed - contact: - name: chief buckaroo - license: - url: https://pb33f.io/license` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(rDoc, lDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, v3.InfoLabel, extChanges.Changes[0].Property) - assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) -} - -func TestCompareDocuments_Swagger_ExternalDocs_Modified(t *testing.T) { - left := `swagger: 2.0 -externalDocs: - url: https://pb33f.io - description: the ranch` - - right := `swagger: 2.0 -externalDocs: - url: https://pb33f.io/new - description: the bunkhouse` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, 2, extChanges.ExternalDocChanges.TotalChanges()) -} - -func TestCompareDocuments_Swagger_ExternalDocs_Added(t *testing.T) { - left := `swagger: 2.0` - - right := `swagger: 2.0 -externalDocs: - url: https://pb33f.io - description: the ranch` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, v3.ExternalDocsLabel, extChanges.Changes[0].Property) - assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) - assert.NotNil(t, lDoc.GetExternalDocs()) - -} - -func TestCompareDocuments_Swagger_ExternalDocs_Removed(t *testing.T) { - left := `swagger: 2.0` - - right := `swagger: 2.0 -externalDocs: - url: https://pb33f.io - description: the ranch` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(rDoc, lDoc) - assert.Equal(t, 1, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) - assert.Equal(t, v3.ExternalDocsLabel, extChanges.Changes[0].Property) - assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) -} - -func TestCompareDocuments_Swagger_Security_Identical(t *testing.T) { - left := `swagger: 2.0 -security: - - nice: - - rice - - spice - - fries - - bad: - - glad - - sad - - bag` - - right := `swagger: 2.0 -security: - - bad: - - sad - - bag - - glad - - nice: - - spice - - rice - - fries` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_Security_Changed(t *testing.T) { - left := `swagger: 2.0 -security: - - nice: - - rice - - spice - - fries - - bad: - - glad - - sad - - bag` - - right := `swagger: 2.0 -security: - - bad: - - sad - - bag - - odd - - nice: - - spice - - lego - - fries` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_Schemas(t *testing.T) { - left := `swagger: 2.0 -definitions: - burgers: - type: int - description: num burgers - cakes: - type: string - description: your favorite cake` - - right := `swagger: 2.0 -definitions: - burgers: - type: bool - description: allow burgers? - cakes: - type: int - description: how many cakes?` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_SecurityDefinitions_Identical(t *testing.T) { - left := `swagger: 2.0 -securityDefinitions: - letMeIn: - type: password - description: a password that lets you in - letMeOut: - type: 2fa - description: are you a robot?` - - right := `swagger: 2.0 -securityDefinitions: - letMeIn: - type: password - description: a password that lets you in - letMeOut: - type: 2fa - description: are you a robot?` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_Components_SecurityDefinitions_Changed(t *testing.T) { - left := `swagger: 2.0 -securityDefinitions: - letMeIn: - type: password - description: a password that lets you in - letMeOut: - type: 2fa - description: are you a robot?` - - right := `swagger: 2.0 -securityDefinitions: - letMeIn: - type: secret - description: what is a secret? - letMeOut: - type: 2fa - description: are you a robot?` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 2, extChanges.TotalChanges()) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_Parameters_Identical(t *testing.T) { - left := `swagger: 2.0 -parameters: - letMeIn: - name: letsGo - type: int - description: lets go my friends. - letMeOut: - name: whyNow - type: string - description: why?` - - right := `swagger: 2.0 -parameters: - letMeIn: - name: letsGo - type: int - description: lets go my friends. - letMeOut: - name: whyNow - type: string - description: why?` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_Components_Parameters_Changed(t *testing.T) { - left := `swagger: 2.0 -parameters: - letMeIn: - name: letsGo - type: int - description: lets go my friends. now. - letMeOut: - name: whyNow - type: string - description: why?` - - right := `swagger: 2.0 -parameters: - letMeIn: - name: letsGoNow - type: string - description: lets go my friends. - letMeOut: - name: whyNowPlease - type: int - description: why should we do it?` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 6, extChanges.TotalChanges()) - assert.Equal(t, 4, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_Responses_Identical(t *testing.T) { - left := `swagger: 2.0 -responses: - tacos: - description: who wants a taco? - schema: - type: int - pizza: - description: who wants a pizza? - schema: - type: string` - - right := `swagger: 2.0 -responses: - tacos: - description: who wants a taco? - schema: - type: int - pizza: - description: who wants a pizza? - schema: - type: string` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_Components_Responses_Modified(t *testing.T) { - left := `swagger: 2.0 -responses: - tacos: - description: who wants a taco? - schema: - type: int - pizza: - description: who wants a pizza? - schema: - type: string` - - right := `swagger: 2.0 -responses: - tacos: - description: I want a taco - schema: - type: bool - pizza: - description: I need a pizza - schema: - type: int` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_Paths_Identical(t *testing.T) { - left := `swagger: 2.0 -paths: - /nice/rice: - get: - description: nice rice? - /lovely/horse: - post: - description: what a lovely horse.` - - right := `swagger: 2.0 -paths: - /lovely/horse: - post: - description: what a lovely horse. - /nice/rice: - get: - description: nice rice?` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_Components_Paths_Modified(t *testing.T) { - left := `swagger: 2.0 -paths: - /nice/rice: - get: - description: nice rice? - /lovely/horse: - post: - description: what a lovely horse.` - - right := `swagger: 2.0 -paths: - /lovely/horse: - put: - description: what a lovely horse. - /nice/rice: - get: - description: nice rice, but changed` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 3, extChanges.TotalChanges()) - assert.Equal(t, 1, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_Paths_Modified_AgainForFun(t *testing.T) { - left := `swagger: 2.0 -paths: - /nice/rice: - get: - description: nice rice? - /lovely/horse: - post: - description: what a lovely horse.` - - right := `swagger: 2.0 -paths: - /lovely/horse: - put: - description: what a lovely horse, and shoes. - /nice/rice: - post: - description: nice rice, but changed` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 4, extChanges.TotalChanges()) - assert.Equal(t, 2, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_Swagger_Components_Tags_Identical(t *testing.T) { - left := `swagger: 2.0 -tags: - - name: a tag - description: a nice tag - - name: another tag - description: this is another tag?` - - right := `swagger: 2.0 -tags: - - name: another tag - description: this is another tag? - - name: a tag - description: a nice tag - ` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Nil(t, extChanges) -} - -func TestCompareDocuments_Swagger_Components_Tags_Modified(t *testing.T) { - left := `swagger: 2.0 -tags: - - name: a tag - description: a nice tag - - name: another tag - description: this is another tag?` - - right := `swagger: 2.0 -tags: - - name: another tag - externalDocs: - url: https://pb33f.io - description: this is another tag, that changed - - name: a tag - description: a nice tag, modified - ` - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v2.CreateDocument(siLeft) - rDoc, _ := v2.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(lDoc, rDoc) - assert.Equal(t, 3, extChanges.TotalChanges()) - assert.Equal(t, 0, extChanges.TotalBreakingChanges()) -} - -func TestCompareDocuments_OpenAPI_BaseProperties_Identical(t *testing.T) { - left := `openapi: 3.1 -x-diet: tough -jsonSchemaDialect: https://pb33f.io/schema` - - right := left - - var lNode, rNode yaml.Node - _ = yaml.Unmarshal([]byte(left), &lNode) - _ = yaml.Unmarshal([]byte(right), &rNode) - - // have to build docs fully to get access to objects - siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) - siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) - - lDoc, _ := v3.CreateDocument(siLeft) - rDoc, _ := v3.CreateDocument(siRight) - - // compare. - extChanges := CompareDocuments(&lDoc, &rDoc) - assert.Nil(t, extChanges) - assert.NotNil(t, lDoc.GetExternalDocs()) - assert.Nil(t, lDoc.FindSecurityRequirement("chewy")) // because why not. -} +// +//func TestCompareDocuments_Swagger_BaseProperties_Identical(t *testing.T) { +// left := `swagger: 2.0 +//x-diet: tough +//host: https://pb33f.io +//basePath: /api +//schemes: +// - http +// - https +//consumes: +// - application/json +// - apple/pie +//produces: +// - application/json +// - fat/belly` +// +// right := left +// +// var lNode, rNode yaml.Node +// _ = yaml.Unmarshal([]byte(left), &lNode) +// _ = yaml.Unmarshal([]byte(right), &rNode) +// +// // create low level objects +// var lDoc v2.Swagger +// var rDoc v2.Swagger +// _ = low.BuildModel(lNode.Content[0], &lDoc) +// _ = low.BuildModel(rNode.Content[0], &rDoc) +// +// // compare. +// extChanges := CompareDocuments(&lDoc, &rDoc) +// assert.Nil(t, extChanges) +//} +// +//func TestCompareDocuments_Swagger_BaseProperties_Modified(t *testing.T) { +// left := `swagger: 2.0 +//x-diet: coke +//host: https://pb33f.io +//basePath: /api +//schemes: +// - http +// - https +//consumes: +// - application/json +// - apple/pie +//produces: +// - application/json +// - fat/belly` +// +// right := `swagger: 2.0.1 +//x-diet: pepsi +//host: https://quobix.com +//basePath: /new-api +//schemes: +// - ws +// - https +//consumes: +// - application/json +// - apple/ice-cream +//produces: +// - application/json +// - very-fat/belly` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 10, extChanges.TotalChanges()) +// assert.Equal(t, 6, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Info_Modified(t *testing.T) { +// left := `swagger: 2.0 +//info: +// title: a doc +// contact: +// name: buckaroo +// license: +// url: https://pb33f.io` +// +// right := `swagger: 2.0 +//info: +// title: a doc that changed +// contact: +// name: chief buckaroo +// license: +// url: https://pb33f.io/license` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 3, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +// assert.Equal(t, 3, extChanges.InfoChanges.TotalChanges()) +//} +// +//func TestCompareDocuments_Swagger_Info_Added(t *testing.T) { +// left := `swagger: 2.0` +// +// right := `swagger: 2.0 +//info: +// title: a doc that changed +// contact: +// name: chief buckaroo +// license: +// url: https://pb33f.io/license` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 1, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +// assert.Equal(t, v3.InfoLabel, extChanges.Changes[0].Property) +// assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) +//} +// +//func TestCompareDocuments_Swagger_Info_Removed(t *testing.T) { +// left := `swagger: 2.0` +// +// right := `swagger: 2.0 +//info: +// title: a doc that changed +// contact: +// name: chief buckaroo +// license: +// url: https://pb33f.io/license` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(rDoc, lDoc) +// assert.Equal(t, 1, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +// assert.Equal(t, v3.InfoLabel, extChanges.Changes[0].Property) +// assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) +//} +// +//func TestCompareDocuments_Swagger_ExternalDocs_Modified(t *testing.T) { +// left := `swagger: 2.0 +//externalDocs: +// url: https://pb33f.io +// description: the ranch` +// +// right := `swagger: 2.0 +//externalDocs: +// url: https://pb33f.io/new +// description: the bunkhouse` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 2, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +// assert.Equal(t, 2, extChanges.ExternalDocChanges.TotalChanges()) +//} +// +//func TestCompareDocuments_Swagger_ExternalDocs_Added(t *testing.T) { +// left := `swagger: 2.0` +// +// right := `swagger: 2.0 +//externalDocs: +// url: https://pb33f.io +// description: the ranch` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 1, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +// assert.Equal(t, v3.ExternalDocsLabel, extChanges.Changes[0].Property) +// assert.Equal(t, PropertyAdded, extChanges.Changes[0].ChangeType) +// assert.NotNil(t, lDoc.GetExternalDocs()) +// +//} +// +//func TestCompareDocuments_Swagger_ExternalDocs_Removed(t *testing.T) { +// left := `swagger: 2.0` +// +// right := `swagger: 2.0 +//externalDocs: +// url: https://pb33f.io +// description: the ranch` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(rDoc, lDoc) +// assert.Equal(t, 1, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +// assert.Equal(t, v3.ExternalDocsLabel, extChanges.Changes[0].Property) +// assert.Equal(t, PropertyRemoved, extChanges.Changes[0].ChangeType) +//} +// +//func TestCompareDocuments_Swagger_Security_Identical(t *testing.T) { +// left := `swagger: 2.0 +//security: +// - nice: +// - rice +// - spice +// - fries +// - bad: +// - glad +// - sad +// - bag` +// +// right := `swagger: 2.0 +//security: +// - bad: +// - sad +// - bag +// - glad +// - nice: +// - spice +// - rice +// - fries` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Nil(t, extChanges) +//} +// +//func TestCompareDocuments_Swagger_Security_Changed(t *testing.T) { +// left := `swagger: 2.0 +//security: +// - nice: +// - rice +// - spice +// - fries +// - bad: +// - glad +// - sad +// - bag` +// +// right := `swagger: 2.0 +//security: +// - bad: +// - sad +// - bag +// - odd +// - nice: +// - spice +// - lego +// - fries` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 4, extChanges.TotalChanges()) +// assert.Equal(t, 2, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_Schemas(t *testing.T) { +// left := `swagger: 2.0 +//definitions: +// burgers: +// type: int +// description: num burgers +// cakes: +// type: string +// description: your favorite cake` +// +// right := `swagger: 2.0 +//definitions: +// burgers: +// type: bool +// description: allow burgers? +// cakes: +// type: int +// description: how many cakes?` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 4, extChanges.TotalChanges()) +// assert.Equal(t, 2, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_SecurityDefinitions_Identical(t *testing.T) { +// left := `swagger: 2.0 +//securityDefinitions: +// letMeIn: +// type: password +// description: a password that lets you in +// letMeOut: +// type: 2fa +// description: are you a robot?` +// +// right := `swagger: 2.0 +//securityDefinitions: +// letMeIn: +// type: password +// description: a password that lets you in +// letMeOut: +// type: 2fa +// description: are you a robot?` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Nil(t, extChanges) +//} + +//func TestCompareDocuments_Swagger_Components_SecurityDefinitions_Changed(t *testing.T) { +// left := `swagger: 2.0 +//securityDefinitions: +// letMeIn: +// type: password +// description: a password that lets you in` +// +// right := `swagger: 2.0 +//securityDefinitions: +// letMeIn: +// type: secret +// description: what is a secret? +// letMeOut: +// type: 2fa +// description: are you a robot?` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 1, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_Parameters_Identical(t *testing.T) { +// left := `swagger: 2.0 +//parameters: +// letMeIn: +// name: letsGo +// type: int +// description: lets go my friends. +// letMeOut: +// name: whyNow +// type: string +// description: why?` +// +// right := `swagger: 2.0 +//parameters: +// letMeIn: +// name: letsGo +// type: int +// description: lets go my friends. +// letMeOut: +// name: whyNow +// type: string +// description: why?` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Nil(t, extChanges) +//} +// +//func TestCompareDocuments_Swagger_Components_Parameters_Changed(t *testing.T) { +// left := `swagger: 2.0 +//parameters: +// letMeIn: +// name: letsGo +// type: int +// description: lets go my friends. now. +//` +// +// right := `swagger: 2.0 +//parameters: +// letMeIn: +// name: letsGoNow +// type: string +// description: lets go my friends. +// letMeOut: +// name: whyNowPlease +// type: int +// description: why should we do it?` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 1, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_Responses_Identical(t *testing.T) { +// left := `swagger: 2.0 +//responses: +// tacos: +// description: who wants a taco? +// schema: +// type: int +// pizza: +// description: who wants a pizza? +// schema: +// type: string` +// +// right := `swagger: 2.0 +//responses: +// tacos: +// description: who wants a taco? +// schema: +// type: int +// pizza: +// description: who wants a pizza? +// schema: +// type: string` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Nil(t, extChanges) +//} +// +//func TestCompareDocuments_Swagger_Components_Responses_Modified(t *testing.T) { +// left := `swagger: 2.0 +//responses: +// tacos: +// description: who wants a taco? +// schema: +// type: int +// pizza: +// description: who wants a pizza? +// schema: +// type: string` +// +// right := `swagger: 2.0 +//responses: +// tacos: +// description: I want a taco +// schema: +// type: bool +// pizza: +// description: I need a pizza +// schema: +// type: int` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 4, extChanges.TotalChanges()) +// assert.Equal(t, 2, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_Paths_Identical(t *testing.T) { +// left := `swagger: 2.0 +//paths: +// /nice/rice: +// get: +// description: nice rice? +// /lovely/horse: +// post: +// description: what a lovely horse.` +// +// right := `swagger: 2.0 +//paths: +// /lovely/horse: +// post: +// description: what a lovely horse. +// /nice/rice: +// get: +// description: nice rice?` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Nil(t, extChanges) +//} +// +//func TestCompareDocuments_Swagger_Components_Paths_Modified(t *testing.T) { +// left := `swagger: 2.0 +//paths: +// /nice/rice: +// get: +// description: nice rice? +// /lovely/horse: +// post: +// description: what a lovely horse.` +// +// right := `swagger: 2.0 +//paths: +// /lovely/horse: +// put: +// description: what a lovely horse. +// /nice/rice: +// get: +// description: nice rice, but changed` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 3, extChanges.TotalChanges()) +// assert.Equal(t, 1, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_Paths_Modified_AgainForFun(t *testing.T) { +// left := `swagger: 2.0 +//paths: +// /nice/rice: +// get: +// description: nice rice? +// /lovely/horse: +// post: +// description: what a lovely horse.` +// +// right := `swagger: 2.0 +//paths: +// /lovely/horse: +// put: +// description: what a lovely horse, and shoes. +// /nice/rice: +// post: +// description: nice rice, but changed` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 4, extChanges.TotalChanges()) +// assert.Equal(t, 2, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_Swagger_Components_Tags_Identical(t *testing.T) { +// left := `swagger: 2.0 +//tags: +// - name: a tag +// description: a nice tag +// - name: another tag +// description: this is another tag?` +// +// right := `swagger: 2.0 +//tags: +// - name: another tag +// description: this is another tag? +// - name: a tag +// description: a nice tag +// ` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Nil(t, extChanges) +//} +// +//func TestCompareDocuments_Swagger_Components_Tags_Modified(t *testing.T) { +// left := `swagger: 2.0 +//tags: +// - name: a tag +// description: a nice tag +// - name: another tag +// description: this is another tag?` +// +// right := `swagger: 2.0 +//tags: +// - name: another tag +// externalDocs: +// url: https://pb33f.io +// description: this is another tag, that changed +// - name: a tag +// description: a nice tag, modified +// ` +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v2.CreateDocument(siLeft) +// rDoc, _ := v2.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(lDoc, rDoc) +// assert.Equal(t, 3, extChanges.TotalChanges()) +// assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +//} +// +//func TestCompareDocuments_OpenAPI_BaseProperties_Identical(t *testing.T) { +// left := `openapi: 3.1 +//x-diet: tough +//jsonSchemaDialect: https://pb33f.io/schema` +// +// right := left +// +// var lNode, rNode yaml.Node +// _ = yaml.Unmarshal([]byte(left), &lNode) +// _ = yaml.Unmarshal([]byte(right), &rNode) +// +// // have to build docs fully to get access to objects +// siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) +// siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) +// +// lDoc, _ := v3.CreateDocument(siLeft) +// rDoc, _ := v3.CreateDocument(siRight) +// +// // compare. +// extChanges := CompareDocuments(&lDoc, &rDoc) +// assert.Nil(t, extChanges) +// assert.NotNil(t, lDoc.GetExternalDocs()) +// assert.Nil(t, lDoc.FindSecurityRequirement("chewy")) // because why not. +//} func TestCompareDocuments_OpenAPI_BaseProperties_Modified(t *testing.T) { @@ -957,6 +950,40 @@ servers: assert.Equal(t, 1, extChanges.TotalBreakingChanges()) } +func TestCompareDocuments_OpenAPI_ModifyExamples(t *testing.T) { + + left := `openapi: 3.1 +components: + examples: + fresh: + value: something` + + right := `openapi: 3.1 +components: + examples: + fresh: + value: something + cakes: + value: something` + + var lNode, rNode yaml.Node + _ = yaml.Unmarshal([]byte(left), &lNode) + _ = yaml.Unmarshal([]byte(right), &rNode) + + // have to build docs fully to get access to objects + siLeft, _ := datamodel.ExtractSpecInfo([]byte(left)) + siRight, _ := datamodel.ExtractSpecInfo([]byte(right)) + + lDoc, _ := v3.CreateDocument(siLeft) + rDoc, _ := v3.CreateDocument(siRight) + + // compare. + extChanges := CompareDocuments(lDoc, rDoc) + + assert.Equal(t, 1, extChanges.TotalChanges()) + assert.Equal(t, 0, extChanges.TotalBreakingChanges()) +} + func TestCompareDocuments_OpenAPI_ModifyWebhooks(t *testing.T) { left := `openapi: 3.1 diff --git a/what-changed/model/schema.go b/what-changed/model/schema.go index 838fe74..7e6ac42 100644 --- a/what-changed/model/schema.go +++ b/what-changed/model/schema.go @@ -8,6 +8,7 @@ import ( "github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low/base" v3 "github.com/pb33f/libopenapi/datamodel/low/v3" + "github.com/pb33f/libopenapi/utils" "gopkg.in/yaml.v3" "sort" "sync" @@ -290,9 +291,7 @@ func checkPropertiesPropertyOfASchema( var propLock sync.Mutex checkProperty := func(key string, lp, rp *base.SchemaProxy, propChanges map[string]*SchemaChanges, done chan bool) { if lp != nil && rp != nil { - ls := lp.Schema() - rs := rp.Schema() - if low.AreEqual(ls, rs) { + if low.AreEqual(lp, rp) { done <- true return } @@ -553,18 +552,21 @@ func checkSchemaPropertyChanges( New: rSchema, }) - // AdditionalProperties - props = append(props, &PropertyCheck{ - LeftNode: lSchema.AdditionalProperties.ValueNode, - RightNode: rSchema.AdditionalProperties.ValueNode, - Label: v3.AdditionalPropertiesLabel, - Changes: changes, - Breaking: false, - Original: lSchema, - New: rSchema, - }) + // AdditionalProperties (only if not an object) + if !utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) && + !utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { + props = append(props, &PropertyCheck{ + LeftNode: lSchema.AdditionalProperties.ValueNode, + RightNode: rSchema.AdditionalProperties.ValueNode, + Label: v3.AdditionalPropertiesLabel, + Changes: changes, + Breaking: false, + Original: lSchema, + New: rSchema, + }) + } - // Description + //Description props = append(props, &PropertyCheck{ LeftNode: lSchema.Description.ValueNode, RightNode: rSchema.Description.ValueNode, @@ -750,6 +752,20 @@ func checkSchemaPropertyChanges( // check extensions sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.Extensions) + // if additional properties is an object, then hash it + // AdditionalProperties (only if not an object) + if utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) || + utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { + + lHash := low.GenerateHashString(lSchema.AdditionalProperties.ValueNode) + rHash := low.GenerateHashString(rSchema.AdditionalProperties.ValueNode) + if lHash != rHash { + CreateChange(changes, Modified, v3.AdditionalPropertiesLabel, + lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, false, + lSchema.AdditionalProperties.Value, rSchema.AdditionalProperties.Value) + } + } + // check core properties CheckProperties(props) } @@ -846,21 +862,17 @@ func extractSchemaChanges( rEntities := make(map[string]*base.SchemaProxy) for h := range lSchema { q := lSchema[h].Value - if !q.IsSchemaReference() { - w := q.Schema() - z := fmt.Sprintf(x, w.Hash()) - lKeys = append(lKeys, z) - lEntities[z] = q - } + z := fmt.Sprintf(x, q.Hash()) + lKeys = append(lKeys, z) + lEntities[z] = q + } for h := range rSchema { q := rSchema[h].Value - if !q.IsSchemaReference() { - w := q.Schema() - z := fmt.Sprintf(x, w.Hash()) - rKeys = append(rKeys, z) - rEntities[z] = q - } + z := fmt.Sprintf(x, q.Hash()) + rKeys = append(rKeys, z) + rEntities[z] = q + } // sort slices so that like for like is all sequenced. diff --git a/what-changed/model/schema_test.go b/what-changed/model/schema_test.go index 2cfcc70..b45cc00 100644 --- a/what-changed/model/schema_test.go +++ b/what-changed/model/schema_test.go @@ -1436,3 +1436,105 @@ components: assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType) assert.Equal(t, "big xml", changes.Changes[0].OriginalObject.(*base.XML).Name.Value) } + +func TestCompareSchemas_SchemaRefChecks(t *testing.T) { + left := `openapi: 3.0 +components: + schemas: + Burger: + type: object + properties: + fries: + $ref: '#/components/schemas/Fries' + Fries: + type: object + required: + - potatoShape + - favoriteDrink + - seasoning` + + right := `openapi: 3.0 +components: + schemas: + Burger: + type: object + properties: + fries: + $ref: '#/components/schemas/Fries' + Fries: + type: object + required: + - potatoShape + - favoriteDrink` + + leftDoc, rightDoc := test_BuildDoc(left, right) + + // extract left reference schema and non reference schema. + changes := CompareDocuments(leftDoc, rightDoc) + assert.NotNil(t, changes) + assert.Equal(t, 1, changes.TotalChanges()) + assert.Equal(t, 1, changes.TotalBreakingChanges()) + +} + +func TestCompareSchemas_SchemaAdditionalPropertiesCheck(t *testing.T) { + left := `openapi: 3.0 +components: + schemas: + Dressing: + type: object + additionalProperties: + type: object + description: something in here. please` + + right := `openapi: 3.0 +components: + schemas: + Dressing: + type: object + additionalProperties: + type: object + description: something in here. please, but changed` + + leftDoc, rightDoc := test_BuildDoc(left, right) + + // extract left reference schema and non reference schema. + changes := CompareDocuments(leftDoc, rightDoc) + assert.NotNil(t, changes) + assert.Equal(t, 1, changes.TotalChanges()) + assert.Equal(t, 0, changes.TotalBreakingChanges()) + +} + +func TestCompareSchemas_Schema_DeletePoly(t *testing.T) { + left := `openapi: 3.0 +components: + schemas: + Drink: + type: int + SomePayload: + type: string + anyOf: + - $ref: '#/components/schemas/Drink' + allOf: + - $ref: '#/components/schemas/Drink'` + + right := `openapi: 3.0 +components: + schemas: + Drink: + type: int + SomePayload: + type: string + anyOf: + - $ref: '#/components/schemas/Drink'` + + leftDoc, rightDoc := test_BuildDoc(left, right) + + // extract left reference schema and non reference schema. + changes := CompareDocuments(leftDoc, rightDoc) + assert.NotNil(t, changes) + assert.Equal(t, 1, changes.TotalChanges()) + assert.Equal(t, 1, changes.TotalBreakingChanges()) + +} diff --git a/what-changed/what_changed_test.go b/what-changed/what_changed_test.go index 2d4a5e7..3a16d27 100644 --- a/what-changed/what_changed_test.go +++ b/what-changed/what_changed_test.go @@ -22,8 +22,8 @@ func TestCompareOpenAPIDocuments(t *testing.T) { modDoc, _ := v3.CreateDocument(infoMod) changes := CompareOpenAPIDocuments(origDoc, modDoc) - assert.Equal(t, 30, changes.TotalChanges()) - assert.Equal(t, 6, changes.TotalBreakingChanges()) + assert.Equal(t, 67, changes.TotalChanges()) + assert.Equal(t, 17, changes.TotalBreakingChanges()) }