fix!: fixed handling of additionalProperties to handle the bool/json-schema nature better

This commit is contained in:
Tristan Cartledge
2023-10-03 14:33:44 +00:00
committed by quobix
parent 8b90e9a808
commit 8531113e17
8 changed files with 316 additions and 382 deletions

View File

@@ -63,7 +63,7 @@ type Schema struct {
// in 3.1 UnevaluatedProperties can be a Schema or a boolean // in 3.1 UnevaluatedProperties can be a Schema or a boolean
// https://github.com/pb33f/libopenapi/issues/118 // https://github.com/pb33f/libopenapi/issues/118
UnevaluatedProperties *DynamicValue[*SchemaProxy, *bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"` UnevaluatedProperties *DynamicValue[*SchemaProxy, bool] `json:"unevaluatedProperties,omitempty" yaml:"unevaluatedProperties,omitempty"`
// in 3.1 Items can be a Schema or a boolean // in 3.1 Items can be a Schema or a boolean
Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"` Items *DynamicValue[*SchemaProxy, bool] `json:"items,omitempty" yaml:"items,omitempty"`
@@ -72,35 +72,35 @@ type Schema struct {
Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"` Anchor string `json:"$anchor,omitempty" yaml:"$anchor,omitempty"`
// Compatible with all versions // Compatible with all versions
Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"` Not *SchemaProxy `json:"not,omitempty" yaml:"not,omitempty"`
Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"` Properties map[string]*SchemaProxy `json:"properties,omitempty" yaml:"properties,omitempty"`
Title string `json:"title,omitempty" yaml:"title,omitempty"` Title string `json:"title,omitempty" yaml:"title,omitempty"`
MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"` MultipleOf *float64 `json:"multipleOf,omitempty" yaml:"multipleOf,omitempty"`
Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"` Maximum *float64 `json:"maximum,omitempty" yaml:"maximum,omitempty"`
Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"` Minimum *float64 `json:"minimum,omitempty" yaml:"minimum,omitempty"`
MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"` MaxLength *int64 `json:"maxLength,omitempty" yaml:"maxLength,omitempty"`
MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"` MinLength *int64 `json:"minLength,omitempty" yaml:"minLength,omitempty"`
Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"` Pattern string `json:"pattern,omitempty" yaml:"pattern,omitempty"`
Format string `json:"format,omitempty" yaml:"format,omitempty"` Format string `json:"format,omitempty" yaml:"format,omitempty"`
MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"` MaxItems *int64 `json:"maxItems,omitempty" yaml:"maxItems,omitempty"`
MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"` MinItems *int64 `json:"minItems,omitempty" yaml:"minItems,omitempty"`
UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"` UniqueItems *bool `json:"uniqueItems,omitempty" yaml:"uniqueItems,omitempty"`
MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"` MaxProperties *int64 `json:"maxProperties,omitempty" yaml:"maxProperties,omitempty"`
MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"` MinProperties *int64 `json:"minProperties,omitempty" yaml:"minProperties,omitempty"`
Required []string `json:"required,omitempty" yaml:"required,omitempty"` Required []string `json:"required,omitempty" yaml:"required,omitempty"`
Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"` Enum []any `json:"enum,omitempty" yaml:"enum,omitempty"`
AdditionalProperties any `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"` AdditionalProperties *DynamicValue[*SchemaProxy, bool] `json:"additionalProperties,omitempty" yaml:"additionalProperties,renderZero,omitempty"`
Description string `json:"description,omitempty" yaml:"description,omitempty"` Description string `json:"description,omitempty" yaml:"description,omitempty"`
Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"` Default any `json:"default,omitempty" yaml:"default,renderZero,omitempty"`
Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"` Const any `json:"const,omitempty" yaml:"const,renderZero,omitempty"`
Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"` Nullable *bool `json:"nullable,omitempty" yaml:"nullable,omitempty"`
ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 ReadOnly bool `json:"readOnly,omitempty" yaml:"readOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30 WriteOnly bool `json:"writeOnly,omitempty" yaml:"writeOnly,omitempty"` // https://github.com/pb33f/libopenapi/issues/30
XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"` XML *XML `json:"xml,omitempty" yaml:"xml,omitempty"`
ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"` ExternalDocs *ExternalDoc `json:"externalDocs,omitempty" yaml:"externalDocs,omitempty"`
Example any `json:"example,omitempty" yaml:"example,omitempty"` Example any `json:"example,omitempty" yaml:"example,omitempty"`
Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"` Deprecated *bool `json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
Extensions map[string]any `json:"-" yaml:"-"` Extensions map[string]any `json:"-" yaml:"-"`
low *base.Schema low *base.Schema
// Parent Proxy refers back to the low level SchemaProxy that is proxying this schema. // Parent Proxy refers back to the low level SchemaProxy that is proxying this schema.
@@ -211,29 +211,22 @@ func NewSchema(schema *base.Schema) *Schema {
Value: schema.UnevaluatedItems.Value, Value: schema.UnevaluatedItems.Value,
}) })
} }
// check if unevaluated properties is a schema
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsA() { var unevaluatedProperties *DynamicValue[*SchemaProxy, bool]
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{ if !schema.UnevaluatedProperties.IsEmpty() {
A: NewSchemaProxy( if schema.UnevaluatedProperties.Value.IsA() {
&lowmodel.NodeReference[*base.SchemaProxy]{ unevaluatedProperties = &DynamicValue[*SchemaProxy, bool]{
A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.UnevaluatedProperties.ValueNode, ValueNode: schema.UnevaluatedProperties.ValueNode,
Value: schema.UnevaluatedProperties.Value.A, Value: schema.UnevaluatedProperties.Value.A,
}, KeyNode: schema.UnevaluatedProperties.KeyNode,
), }),
N: 0, }
} else {
unevaluatedProperties = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.UnevaluatedProperties.Value.B}
} }
} }
s.UnevaluatedProperties = unevaluatedProperties
// check if unevaluated properties is a bool
if !schema.UnevaluatedProperties.IsEmpty() && schema.UnevaluatedProperties.Value.IsB() {
s.UnevaluatedProperties = &DynamicValue[*SchemaProxy, *bool]{
B: schema.UnevaluatedProperties.Value.B,
N: 1,
}
}
if !schema.UnevaluatedProperties.IsEmpty() {
}
s.Pattern = schema.Pattern.Value s.Pattern = schema.Pattern.Value
s.Format = schema.Format.Value s.Format = schema.Format.Value
@@ -248,19 +241,23 @@ func NewSchema(schema *base.Schema) *Schema {
s.Type = append(s.Type, schema.Type.Value.B[i].Value) s.Type = append(s.Type, schema.Type.Value.B[i].Value)
} }
} }
if schema.AdditionalProperties.Value != nil {
if addPropSchema, ok := schema.AdditionalProperties.Value.(*base.SchemaProxy); ok {
s.AdditionalProperties = NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
KeyNode: schema.AdditionalProperties.KeyNode,
ValueNode: schema.AdditionalProperties.ValueNode,
Value: addPropSchema,
})
} else {
// TODO: check for slice and map types and unpack correctly.
s.AdditionalProperties = schema.AdditionalProperties.Value var additionalProperties *DynamicValue[*SchemaProxy, bool]
if !schema.AdditionalProperties.IsEmpty() {
if schema.AdditionalProperties.Value.IsA() {
additionalProperties = &DynamicValue[*SchemaProxy, bool]{
A: NewSchemaProxy(&lowmodel.NodeReference[*base.SchemaProxy]{
ValueNode: schema.AdditionalProperties.ValueNode,
Value: schema.AdditionalProperties.Value.A,
KeyNode: schema.AdditionalProperties.KeyNode,
}),
}
} else {
additionalProperties = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.AdditionalProperties.Value.B}
} }
} }
s.AdditionalProperties = additionalProperties
s.Description = schema.Description.Value s.Description = schema.Description.Value
s.Default = schema.Default.Value s.Default = schema.Default.Value
s.Const = schema.Const.Value s.Const = schema.Const.Value
@@ -423,7 +420,8 @@ func NewSchema(schema *base.Schema) *Schema {
Value: schema.Items.Value.A, Value: schema.Items.Value.A,
KeyNode: schema.Items.KeyNode, KeyNode: schema.Items.KeyNode,
}, },
)} ),
}
} else { } else {
items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B} items = &DynamicValue[*SchemaProxy, bool]{N: 1, B: schema.Items.Value.B}
} }

View File

@@ -111,7 +111,6 @@ func TestNewSchemaProxyRender(t *testing.T) {
rice: rice:
$ref: '#/components/schemas/rice'` $ref: '#/components/schemas/rice'`
assert.Equal(t, desired, strings.TrimSpace(string(rend))) assert.Equal(t, desired, strings.TrimSpace(string(rend)))
} }
func TestNewSchemaProxy_WithObject(t *testing.T) { func TestNewSchemaProxy_WithObject(t *testing.T) {
@@ -217,10 +216,7 @@ properties:
type: number type: number
description: a number description: a number
example: "2" example: "2"
additionalProperties: additionalProperties: false
- chicken
- nugget
- soup
somethingB: somethingB:
type: object type: object
exclusiveMinimum: true exclusiveMinimum: true
@@ -241,8 +237,7 @@ properties:
attribute: true attribute: true
x-pizza: love x-pizza: love
additionalProperties: additionalProperties:
why: yes type: string
thatIs: true
additionalProperties: true additionalProperties: true
required: required:
- them - them
@@ -315,12 +310,12 @@ $anchor: anchor`
assert.Equal(t, "anchor", compiled.Anchor) assert.Equal(t, "anchor", compiled.Anchor)
wentLow := compiled.GoLow() wentLow := compiled.GoLow()
assert.Equal(t, 129, wentLow.AdditionalProperties.ValueNode.Line) assert.Equal(t, 125, wentLow.AdditionalProperties.ValueNode.Line)
assert.NotNil(t, compiled.GoLowUntyped()) assert.NotNil(t, compiled.GoLowUntyped())
// now render it out! // now render it out!
schemaBytes, _ := compiled.Render() schemaBytes, _ := compiled.Render()
assert.Len(t, schemaBytes, 3494) assert.Len(t, schemaBytes, 3417)
} }
func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) { func TestSchemaObjectWithAllOfSequenceOrder(t *testing.T) {
@@ -504,7 +499,7 @@ required: [cake, fish]`
assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B) assert.Equal(t, float64(334), compiled.Properties["somethingB"].Schema().ExclusiveMaximum.B)
assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2) assert.Len(t, compiled.Properties["somethingB"].Schema().Properties["somethingBProp"].Schema().Type, 2)
assert.Equal(t, "nice", compiled.AdditionalProperties.(*SchemaProxy).Schema().Description) assert.Equal(t, "nice", compiled.AdditionalProperties.A.Schema().Description)
wentLow := compiled.GoLow() wentLow := compiled.GoLow()
assert.Equal(t, 97, wentLow.AdditionalProperties.ValueNode.Line) assert.Equal(t, 97, wentLow.AdditionalProperties.ValueNode.Line)
@@ -556,7 +551,6 @@ func TestSchemaProxy_GoLow(t *testing.T) {
spNil := NewSchemaProxy(nil) spNil := NewSchemaProxy(nil)
assert.Nil(t, spNil.GoLow()) assert.Nil(t, spNil.GoLow())
assert.Nil(t, spNil.GoLowUntyped()) assert.Nil(t, spNil.GoLowUntyped())
} }
func getHighSchema(t *testing.T, yml string) *Schema { func getHighSchema(t *testing.T, yml string) *Schema {
@@ -836,7 +830,6 @@ allOf:
// now render it out, it should be identical. // now render it out, it should be identical.
schemaBytes, _ := compiled.Render() schemaBytes, _ := compiled.Render()
assert.Equal(t, testSpec, string(schemaBytes)) assert.Equal(t, testSpec, string(schemaBytes))
} }
func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) { func TestNewSchemaProxy_RenderSchemaWithMultipleObjectTypes(t *testing.T) {
@@ -934,8 +927,7 @@ func TestNewSchemaProxy_RenderSchemaEnsurePropertyOrdering(t *testing.T) {
attribute: true attribute: true
x-pizza: love x-pizza: love
additionalProperties: additionalProperties:
why: yes type: string
thatIs: true
additionalProperties: true additionalProperties: true
xml: xml:
name: XML Thing` name: XML Thing`
@@ -989,60 +981,6 @@ func TestNewSchemaProxy_RenderSchemaCheckDiscriminatorMappingOrder(t *testing.T)
assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes))) assert.Equal(t, testSpec, strings.TrimSpace(string(schemaBytes)))
} }
func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSlice(t *testing.T) {
testSpec := `additionalProperties:
- one
- two
- miss a few
- ninety nine
- hundred`
var compNode yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
sp := new(lowbase.SchemaProxy)
err := sp.Build(nil, compNode.Content[0], nil)
assert.NoError(t, err)
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
Value: sp,
ValueNode: compNode.Content[0],
}
schemaProxy := NewSchemaProxy(&lowproxy)
compiled := schemaProxy.Schema()
// now render it out, it should be identical.
schemaBytes, _ := compiled.Render()
assert.Len(t, schemaBytes, 91)
}
func TestNewSchemaProxy_RenderSchemaCheckAdditionalPropertiesSliceMap(t *testing.T) {
testSpec := `additionalProperties:
- nice: cake
- yummy: beer
- hot: coffee`
var compNode yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
sp := new(lowbase.SchemaProxy)
err := sp.Build(nil, compNode.Content[0], nil)
assert.NoError(t, err)
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
Value: sp,
ValueNode: compNode.Content[0],
}
schemaProxy := NewSchemaProxy(&lowproxy)
compiled := schemaProxy.Schema()
// now render it out, it should be identical.
schemaBytes, _ := compiled.Render()
assert.Len(t, schemaBytes, 75)
}
func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) { func TestNewSchemaProxy_CheckDefaultBooleanFalse(t *testing.T) {
testSpec := `default: false` testSpec := `default: false`
@@ -1192,8 +1130,7 @@ unevaluatedProperties: true
` `
highSchema := getHighSchema(t, yml) highSchema := getHighSchema(t, yml)
value := true assert.True(t, highSchema.UnevaluatedProperties.B)
assert.EqualValues(t, &value, highSchema.UnevaluatedProperties.B)
} }
func TestUnevaluatedPropertiesBoolean_False(t *testing.T) { func TestUnevaluatedPropertiesBoolean_False(t *testing.T) {
@@ -1203,6 +1140,54 @@ unevaluatedProperties: false
` `
highSchema := getHighSchema(t, yml) highSchema := getHighSchema(t, yml)
value := false assert.False(t, highSchema.UnevaluatedProperties.B)
assert.EqualValues(t, &value, highSchema.UnevaluatedProperties.B) }
func TestUnevaluatedPropertiesBoolean_Unset(t *testing.T) {
yml := `
type: number
`
highSchema := getHighSchema(t, yml)
assert.Nil(t, highSchema.UnevaluatedProperties)
}
func TestAdditionalProperties(t *testing.T) {
testSpec := `type: object
properties:
additionalPropertiesSimpleSchema:
type: object
additionalProperties:
type: string
additionalPropertiesBool:
type: object
additionalProperties: true
additionalPropertiesAnyOf:
type: object
additionalProperties:
anyOf:
- type: string
- type: array
items:
type: string
`
var compNode yaml.Node
_ = yaml.Unmarshal([]byte(testSpec), &compNode)
sp := new(lowbase.SchemaProxy)
err := sp.Build(nil, compNode.Content[0], nil)
assert.NoError(t, err)
lowproxy := low.NodeReference[*lowbase.SchemaProxy]{
Value: sp,
ValueNode: compNode.Content[0],
}
schemaProxy := NewSchemaProxy(&lowproxy)
compiled := schemaProxy.Schema()
assert.Equal(t, []string{"string"}, compiled.Properties["additionalPropertiesSimpleSchema"].Schema().AdditionalProperties.A.Schema().Type)
assert.Equal(t, true, compiled.Properties["additionalPropertiesBool"].Schema().AdditionalProperties.B)
assert.Equal(t, []string{"string"}, compiled.Properties["additionalPropertiesAnyOf"].Schema().AdditionalProperties.A.Schema().AnyOf[0].Schema().Type)
} }

View File

@@ -220,7 +220,7 @@ func TestNewDocument_Components_Schemas(t *testing.T) {
d := h.Components.Schemas["Drink"] d := h.Components.Schemas["Drink"]
assert.Len(t, d.Schema().Required, 2) assert.Len(t, d.Schema().Required, 2)
assert.True(t, d.Schema().AdditionalProperties.(bool)) assert.True(t, d.Schema().AdditionalProperties.B)
assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName) assert.Equal(t, "drinkType", d.Schema().Discriminator.PropertyName)
assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"]) assert.Equal(t, "some value", d.Schema().Discriminator.Mapping["drink"])
assert.Equal(t, 516, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line) assert.Equal(t, 516, d.Schema().Discriminator.GoLow().PropertyName.ValueNode.Line)
@@ -377,7 +377,6 @@ func testBurgerShop(t *testing.T, h *Document, checkLines bool) {
assert.Equal(t, 310, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line) assert.Equal(t, 310, okResp.Links["LocateBurger"].GoLow().OperationId.ValueNode.Line)
assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line) assert.Equal(t, 118, burgersOp.Post.Security[0].GoLow().Requirements.ValueNode.Line)
} }
} }
func TestStripeAsDoc(t *testing.T) { func TestStripeAsDoc(t *testing.T) {
@@ -435,7 +434,6 @@ func TestDigitalOceanAsDocFromSHA(t *testing.T) {
d := NewDocument(lowDoc) d := NewDocument(lowDoc)
assert.NotNil(t, d) assert.NotNil(t, d)
assert.Equal(t, 183, len(d.Paths.PathItems)) assert.Equal(t, 183, len(d.Paths.PathItems))
} }
func TestPetstoreAsDoc(t *testing.T) { func TestPetstoreAsDoc(t *testing.T) {
@@ -463,7 +461,6 @@ func TestCircularReferencesDoc(t *testing.T) {
} }
func TestDocument_MarshalYAML(t *testing.T) { func TestDocument_MarshalYAML(t *testing.T) {
// create a new document // create a new document
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
@@ -477,11 +474,9 @@ func TestDocument_MarshalYAML(t *testing.T) {
highDoc := NewDocument(lDoc) highDoc := NewDocument(lDoc)
testBurgerShop(t, highDoc, false) testBurgerShop(t, highDoc, false)
} }
func TestDocument_MarshalIndention(t *testing.T) { func TestDocument_MarshalIndention(t *testing.T) {
data, _ := os.ReadFile("../../../test_specs/single-definition.yaml") data, _ := os.ReadFile("../../../test_specs/single-definition.yaml")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
@@ -495,11 +490,9 @@ func TestDocument_MarshalIndention(t *testing.T) {
rendered = highDoc.RenderWithIndention(4) rendered = highDoc.RenderWithIndention(4)
assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered))) assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered)))
} }
func TestDocument_MarshalIndention_Error(t *testing.T) { func TestDocument_MarshalIndention_Error(t *testing.T) {
data, _ := os.ReadFile("../../../test_specs/single-definition.yaml") data, _ := os.ReadFile("../../../test_specs/single-definition.yaml")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
@@ -513,11 +506,9 @@ func TestDocument_MarshalIndention_Error(t *testing.T) {
rendered = highDoc.RenderWithIndention(4) rendered = highDoc.RenderWithIndention(4)
assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered))) assert.NotEqual(t, string(data), strings.TrimSpace(string(rendered)))
} }
func TestDocument_MarshalJSON(t *testing.T) { func TestDocument_MarshalJSON(t *testing.T) {
data, _ := os.ReadFile("../../../test_specs/petstorev3.json") data, _ := os.ReadFile("../../../test_specs/petstorev3.json")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
@@ -537,7 +528,6 @@ func TestDocument_MarshalJSON(t *testing.T) {
} }
func TestDocument_MarshalYAMLInline(t *testing.T) { func TestDocument_MarshalYAMLInline(t *testing.T) {
// create a new document // create a new document
initTest() initTest()
h := NewDocument(lowDoc) h := NewDocument(lowDoc)
@@ -551,11 +541,9 @@ func TestDocument_MarshalYAMLInline(t *testing.T) {
highDoc := NewDocument(lDoc) highDoc := NewDocument(lDoc)
testBurgerShop(t, highDoc, false) testBurgerShop(t, highDoc, false)
} }
func TestDocument_MarshalYAML_TestRefs(t *testing.T) { func TestDocument_MarshalYAML_TestRefs(t *testing.T) {
// create a new document // create a new document
yml := `openapi: 3.1.0 yml := `openapi: 3.1.0
paths: paths:
@@ -633,7 +621,6 @@ components:
} }
func TestDocument_MarshalYAML_TestParamRefs(t *testing.T) { func TestDocument_MarshalYAML_TestParamRefs(t *testing.T) {
// create a new document // create a new document
yml := `openapi: 3.1.0 yml := `openapi: 3.1.0
paths: paths:
@@ -686,7 +673,6 @@ components:
} }
func TestDocument_MarshalYAML_TestModifySchemas(t *testing.T) { func TestDocument_MarshalYAML_TestModifySchemas(t *testing.T) {
// create a new document // create a new document
yml := `openapi: 3.1.0 yml := `openapi: 3.1.0
components: components:

View File

@@ -3,7 +3,6 @@ package base
import ( import (
"crypto/sha256" "crypto/sha256"
"fmt" "fmt"
"reflect"
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
@@ -100,7 +99,7 @@ type Schema struct {
PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] PatternProperties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
PropertyNames low.NodeReference[*SchemaProxy] PropertyNames low.NodeReference[*SchemaProxy]
UnevaluatedItems low.NodeReference[*SchemaProxy] UnevaluatedItems low.NodeReference[*SchemaProxy]
UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]] UnevaluatedProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]
Anchor low.NodeReference[string] Anchor low.NodeReference[string]
// Compatible with all versions // Compatible with all versions
@@ -121,7 +120,7 @@ type Schema struct {
Enum low.NodeReference[[]low.ValueReference[any]] Enum low.NodeReference[[]low.ValueReference[any]]
Not low.NodeReference[*SchemaProxy] Not low.NodeReference[*SchemaProxy]
Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]] Properties low.NodeReference[map[low.KeyReference[string]]low.ValueReference[*SchemaProxy]]
AdditionalProperties low.NodeReference[any] AdditionalProperties low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]
Description low.NodeReference[string] Description low.NodeReference[string]
ContentEncoding low.NodeReference[string] ContentEncoding low.NodeReference[string]
ContentMediaType low.NodeReference[string] ContentMediaType low.NodeReference[string]
@@ -189,53 +188,7 @@ func (s *Schema) Hash() [32]byte {
d = append(d, fmt.Sprint(s.MinProperties.Value)) d = append(d, fmt.Sprint(s.MinProperties.Value))
} }
if !s.AdditionalProperties.IsEmpty() { if !s.AdditionalProperties.IsEmpty() {
d = append(d, low.GenerateHashString(s.AdditionalProperties.Value))
// check type of properties, if we have a low level map, we need to hash the values in a repeatable
// order.
to := reflect.TypeOf(s.AdditionalProperties.Value)
vo := reflect.ValueOf(s.AdditionalProperties.Value)
var values []string
switch to.Kind() {
case reflect.Slice:
for i := 0; i < vo.Len(); i++ {
vn := vo.Index(i).Interface()
if jh, ok := vn.(low.HasValueUnTyped); ok {
vn = jh.GetValueUntyped()
fg := reflect.TypeOf(vn)
gf := reflect.ValueOf(vn)
if fg.Kind() == reflect.Map {
for _, ky := range gf.MapKeys() {
hu := ky.Interface()
values = append(values, fmt.Sprintf("%s:%s", hu, low.GenerateHashString(gf.MapIndex(ky).Interface())))
}
continue
}
values = append(values, fmt.Sprintf("%d:%s", i, low.GenerateHashString(vn)))
}
}
sort.Strings(values)
d = append(d, strings.Join(values, "||"))
case reflect.Map:
for _, k := range vo.MapKeys() {
var x string
var l int
var v any
// extract key
if o, ok := k.Interface().(low.HasKeyNode); ok {
x = o.GetKeyNode().Value
l = o.GetKeyNode().Line
v = vo.MapIndex(k).Interface().(low.HasValueNodeUntyped).GetValueNode().Value
}
values = append(values, fmt.Sprintf("%d:%s:%s", l, x, low.GenerateHashString(v)))
}
sort.Strings(values)
d = append(d, strings.Join(values, "||"))
default:
d = append(d, low.GenerateHashString(s.AdditionalProperties.Value))
}
} }
if !s.Description.IsEmpty() { if !s.Description.IsEmpty() {
d = append(d, fmt.Sprint(s.Description.Value)) d = append(d, fmt.Sprint(s.Description.Value))
@@ -667,77 +620,24 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
} }
_, addPLabel, addPNode := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content) // check additionalProperties type for schema or bool
if addPNode != nil { addPropsIsBool := false
if utils.IsNodeMap(addPNode) || utils.IsNodeArray(addPNode) { addPropsBoolValue := true
// check if this is a reference, or an inline schema. _, addPLabel, addPValue := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content)
isRef, _, _ := utils.IsNodeRefValue(addPNode) if addPValue != nil {
var sp *SchemaProxy if utils.IsNodeBoolValue(addPValue) {
// now check if this object has a 'type' if so, it's a schema, if not... it's a random addPropsIsBool = true
// object, and we should treat it as a raw map. addPropsBoolValue, _ = strconv.ParseBool(addPValue.Value)
if _, v := utils.FindKeyNodeTop(TypeLabel, addPNode.Content); v != nil {
sp = &SchemaProxy{
kn: addPLabel,
vn: addPNode,
idx: idx,
}
}
if isRef {
_, vn := utils.FindKeyNodeTop("$ref", addPNode.Content)
sp = &SchemaProxy{
kn: addPLabel,
vn: addPNode,
idx: idx,
isReference: true,
referenceLookup: vn.Value,
}
}
// if this is a reference, or a schema, we're done.
if sp != nil {
s.AdditionalProperties = low.NodeReference[any]{Value: sp, KeyNode: addPLabel, ValueNode: addPNode}
} else {
// if this is a map, collect all the keys and values.
if utils.IsNodeMap(addPNode) {
addProps := make(map[low.KeyReference[string]]low.ValueReference[any])
var label string
for g := range addPNode.Content {
if g%2 == 0 {
label = addPNode.Content[g].Value
continue
} else {
addProps[low.KeyReference[string]{Value: label, KeyNode: addPNode.Content[g-1]}] = low.ValueReference[any]{Value: addPNode.Content[g].Value, ValueNode: addPNode.Content[g]}
}
}
s.AdditionalProperties = low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode}
}
// if the node is an array, extract everything into a trackable structure
if utils.IsNodeArray(addPNode) {
var addProps []low.ValueReference[any]
// if this is an array or maps, encode the map items correctly.
for i := range addPNode.Content {
if utils.IsNodeMap(addPNode.Content[i]) {
var prop map[string]any
_ = addPNode.Content[i].Decode(&prop)
addProps = append(addProps,
low.ValueReference[any]{Value: prop, ValueNode: addPNode.Content[i]})
} else {
addProps = append(addProps,
low.ValueReference[any]{Value: addPNode.Content[i].Value, ValueNode: addPNode.Content[i]})
}
}
s.AdditionalProperties = low.NodeReference[any]{Value: addProps, KeyNode: addPLabel, ValueNode: addPNode}
}
}
} }
if utils.IsNodeBoolValue(addPNode) { }
b, _ := strconv.ParseBool(addPNode.Value) if addPropsIsBool {
s.AdditionalProperties = low.NodeReference[any]{Value: b, KeyNode: addPLabel, ValueNode: addPNode} s.AdditionalProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
Value: &SchemaDynamicValue[*SchemaProxy, bool]{
B: addPropsBoolValue,
N: 1,
},
KeyNode: addPLabel,
ValueNode: addPValue,
} }
} }
@@ -827,9 +727,9 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
} }
if unevalIsBool { if unevalIsBool {
s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]{ s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
Value: &SchemaDynamicValue[*SchemaProxy, *bool]{ Value: &SchemaDynamicValue[*SchemaProxy, bool]{
B: &unevalBoolValue, B: unevalBoolValue,
N: 1, N: 1,
}, },
KeyNode: unevalLabel, KeyNode: unevalLabel,
@@ -838,7 +738,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy] var allOf, anyOf, oneOf, prefixItems []low.ValueReference[*SchemaProxy]
var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties low.ValueReference[*SchemaProxy] var items, not, contains, sif, selse, sthen, propertyNames, unevalItems, unevalProperties, addProperties low.ValueReference[*SchemaProxy]
_, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content) _, allOfLabel, allOfValue := utils.FindKeyNodeFullTop(AllOfLabel, root.Content)
_, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content) _, anyOfLabel, anyOfValue := utils.FindKeyNodeFullTop(AnyOfLabel, root.Content)
@@ -852,6 +752,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
_, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content) _, propNamesLabel, propNamesValue := utils.FindKeyNodeFullTop(PropertyNamesLabel, root.Content)
_, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content) _, unevalItemsLabel, unevalItemsValue := utils.FindKeyNodeFullTop(UnevaluatedItemsLabel, root.Content)
_, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content) _, unevalPropsLabel, unevalPropsValue := utils.FindKeyNodeFullTop(UnevaluatedPropertiesLabel, root.Content)
_, addPropsLabel, addPropsValue := utils.FindKeyNodeFullTop(AdditionalPropertiesLabel, root.Content)
errorChan := make(chan error) errorChan := make(chan error)
allOfChan := make(chan schemaProxyBuildResult) allOfChan := make(chan schemaProxyBuildResult)
@@ -867,6 +768,7 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
propNamesChan := make(chan schemaProxyBuildResult) propNamesChan := make(chan schemaProxyBuildResult)
unevalItemsChan := make(chan schemaProxyBuildResult) unevalItemsChan := make(chan schemaProxyBuildResult)
unevalPropsChan := make(chan schemaProxyBuildResult) unevalPropsChan := make(chan schemaProxyBuildResult)
addPropsChan := make(chan schemaProxyBuildResult)
totalBuilds := countSubSchemaItems(allOfValue) + totalBuilds := countSubSchemaItems(allOfValue) +
countSubSchemaItems(anyOfValue) + countSubSchemaItems(anyOfValue) +
@@ -921,6 +823,10 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
totalBuilds++ totalBuilds++
go buildSchema(unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx) go buildSchema(unevalPropsChan, unevalPropsLabel, unevalPropsValue, errorChan, idx)
} }
if !addPropsIsBool && addPropsValue != nil {
totalBuilds++
go buildSchema(addPropsChan, addPropsLabel, addPropsValue, errorChan, idx)
}
completeCount := 0 completeCount := 0
for completeCount < totalBuilds { for completeCount < totalBuilds {
@@ -966,6 +872,9 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
case r := <-unevalPropsChan: case r := <-unevalPropsChan:
completeCount++ completeCount++
unevalProperties = r.v unevalProperties = r.v
case r := <-addPropsChan:
completeCount++
addProperties = r.v
} }
} }
@@ -1056,14 +965,23 @@ func (s *Schema) Build(root *yaml.Node, idx *index.SpecIndex) error {
} }
} }
if !unevalIsBool && !unevalProperties.IsEmpty() { if !unevalIsBool && !unevalProperties.IsEmpty() {
s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, *bool]]{ s.UnevaluatedProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
Value: &SchemaDynamicValue[*SchemaProxy, *bool]{ Value: &SchemaDynamicValue[*SchemaProxy, bool]{
A: unevalProperties.Value, A: unevalProperties.Value,
}, },
KeyNode: unevalPropsLabel, KeyNode: unevalPropsLabel,
ValueNode: unevalPropsValue, ValueNode: unevalPropsValue,
} }
} }
if !addPropsIsBool && !addProperties.IsEmpty() {
s.AdditionalProperties = low.NodeReference[*SchemaDynamicValue[*SchemaProxy, bool]]{
Value: &SchemaDynamicValue[*SchemaProxy, bool]{
A: addProperties.Value,
},
KeyNode: addPropsLabel,
ValueNode: addPropsValue,
}
}
return nil return nil
} }
@@ -1219,8 +1137,7 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
v: *r.res, v: *r.res,
} }
} }
} } else if utils.IsNodeArray(valueNode) {
if utils.IsNodeArray(valueNode) {
refBuilds := 0 refBuilds := 0
results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content)) results := make([]*low.ValueReference[*SchemaProxy], len(valueNode.Content))
@@ -1261,6 +1178,8 @@ func buildSchema(schemas chan schemaProxyBuildResult, labelNode, valueNode *yaml
v: *r, v: *r,
} }
} }
} else {
errors <- fmt.Errorf("build schema failed: unexpected node type: %s, line %d, col %d", valueNode.Tag, valueNode.Line, valueNode.Column)
} }
} }
} }

View File

@@ -169,7 +169,7 @@ func Test_Schema(t *testing.T) {
schErr := sch.Build(rootNode.Content[0], nil) schErr := sch.Build(rootNode.Content[0], nil)
assert.NoError(t, schErr) assert.NoError(t, schErr)
assert.Equal(t, "something object", sch.Description.Value) assert.Equal(t, "something object", sch.Description.Value)
assert.True(t, sch.AdditionalProperties.Value.(bool)) assert.True(t, sch.AdditionalProperties.Value.B)
assert.Len(t, sch.Properties.Value, 2) assert.Len(t, sch.Properties.Value, 2)
v := sch.FindProperty("somethingB") v := sch.FindProperty("somethingB")
@@ -1172,30 +1172,7 @@ func TestExtractSchema_AdditionalPropertiesAsSchema(t *testing.T) {
res, err := ExtractSchema(idxNode.Content[0], idx) res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.A.Schema())
assert.Nil(t, err)
}
func TestExtractSchema_AdditionalPropertiesAsSchemaSlice(t *testing.T) {
yml := `components:
schemas:
Something:
additionalProperties:
- nice: rice`
var iNode yaml.Node
mErr := yaml.Unmarshal([]byte(yml), &iNode)
assert.NoError(t, mErr)
idx := index.NewSpecIndex(&iNode)
yml = `$ref: '#/components/schemas/Something'`
var idxNode yaml.Node
_ = yaml.Unmarshal([]byte(yml), &idxNode)
res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.([]low.ValueReference[interface{}]))
assert.Nil(t, err) assert.Nil(t, err)
} }
@@ -1244,7 +1221,7 @@ func TestExtractSchema_AdditionalProperties_Ref(t *testing.T) {
_ = yaml.Unmarshal([]byte(yml), &idxNode) _ = yaml.Unmarshal([]byte(yml), &idxNode)
res, err := ExtractSchema(idxNode.Content[0], idx) res, err := ExtractSchema(idxNode.Content[0], idx)
assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.(*SchemaProxy).Schema()) assert.NotNil(t, res.Value.Schema().AdditionalProperties.Value.A.Schema())
assert.Nil(t, err) assert.Nil(t, err)
} }
@@ -1378,7 +1355,7 @@ func TestSchema_Hash_Equal(t *testing.T) {
uniqueItems: 1 uniqueItems: 1
maxProperties: 10 maxProperties: 10
minProperties: 1 minProperties: 1
additionalProperties: anything additionalProperties: true
description: milky description: milky
contentEncoding: rubber shoes contentEncoding: rubber shoes
contentMediaType: paper tiger contentMediaType: paper tiger
@@ -1420,7 +1397,7 @@ func TestSchema_Hash_Equal(t *testing.T) {
uniqueItems: 1 uniqueItems: 1
maxProperties: 10 maxProperties: 10
minProperties: 1 minProperties: 1
additionalProperties: anything additionalProperties: true
description: milky description: milky
contentEncoding: rubber shoes contentEncoding: rubber shoes
contentMediaType: paper tiger contentMediaType: paper tiger
@@ -1611,7 +1588,7 @@ func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsTrue(t *testing.T) {
res, _ := ExtractSchema(idxNode.Content[0], idx) res, _ := ExtractSchema(idxNode.Content[0], idx)
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB()) assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB())
assert.True(t, *res.Value.Schema().UnevaluatedProperties.Value.B) assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.B)
assert.Equal(t, "571bd1853c22393131e2dcadce86894da714ec14968895c8b7ed18154b2be8cd", assert.Equal(t, "571bd1853c22393131e2dcadce86894da714ec14968895c8b7ed18154b2be8cd",
low.GenerateHashString(res.Value.Schema().UnevaluatedProperties.Value)) low.GenerateHashString(res.Value.Schema().UnevaluatedProperties.Value))
@@ -1636,7 +1613,7 @@ func TestSchema_UnevaluatedPropertiesAsBool_DefinedAsFalse(t *testing.T) {
res, _ := ExtractSchema(idxNode.Content[0], idx) res, _ := ExtractSchema(idxNode.Content[0], idx)
assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB()) assert.True(t, res.Value.Schema().UnevaluatedProperties.Value.IsB())
assert.False(t, *res.Value.Schema().UnevaluatedProperties.Value.B) assert.False(t, res.Value.Schema().UnevaluatedProperties.Value.B)
} }
func TestSchema_UnevaluatedPropertiesAsBool_Undefined(t *testing.T) { func TestSchema_UnevaluatedPropertiesAsBool_Undefined(t *testing.T) {

View File

@@ -6,7 +6,6 @@ import (
"testing" "testing"
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low/base"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
@@ -52,7 +51,6 @@ func BenchmarkCreateDocument_Circular(b *testing.B) {
} }
func BenchmarkCreateDocument_k8s(b *testing.B) { func BenchmarkCreateDocument_k8s(b *testing.B) {
data, _ := os.ReadFile("../../../test_specs/k8s.json") data, _ := os.ReadFile("../../../test_specs/k8s.json")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
@@ -69,7 +67,6 @@ func BenchmarkCreateDocument_k8s(b *testing.B) {
} }
func TestCircularReferenceError(t *testing.T) { func TestCircularReferenceError(t *testing.T) {
data, _ := os.ReadFile("../../../test_specs/circular-tests.yaml") data, _ := os.ReadFile("../../../test_specs/circular-tests.yaml")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
circDoc, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ circDoc, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
@@ -81,7 +78,6 @@ func TestCircularReferenceError(t *testing.T) {
} }
func TestCircularReference_IgnoreArray(t *testing.T) { func TestCircularReference_IgnoreArray(t *testing.T) {
spec := `openapi: 3.1.0 spec := `openapi: 3.1.0
components: components:
schemas: schemas:
@@ -110,7 +106,6 @@ components:
} }
func TestCircularReference_IgnorePoly(t *testing.T) { func TestCircularReference_IgnorePoly(t *testing.T) {
spec := `openapi: 3.1.0 spec := `openapi: 3.1.0
components: components:
schemas: schemas:
@@ -168,7 +163,6 @@ func BenchmarkCreateDocument_Petstore(b *testing.B) {
} }
func TestCreateDocumentStripe(t *testing.T) { func TestCreateDocumentStripe(t *testing.T) {
data, _ := os.ReadFile("../../../test_specs/stripe.yaml") data, _ := os.ReadFile("../../../test_specs/stripe.yaml")
info, _ := datamodel.ExtractSpecInfo(data) info, _ := datamodel.ExtractSpecInfo(data)
d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{ d, err := CreateDocumentFromConfig(info, &datamodel.DocumentConfiguration{
@@ -303,7 +297,6 @@ func TestCreateDocument_Tags(t *testing.T) {
// this is why we will need a higher level API to this model, this looks cool and all, but dude. // this is why we will need a higher level API to this model, this looks cool and all, but dude.
assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"]) assert.Equal(t, "now?", extension.Value.(map[string]interface{})["ok"].([]interface{})[0].(map[string]interface{})["what"])
} }
} }
/// tag2 /// tag2
@@ -313,7 +306,6 @@ func TestCreateDocument_Tags(t *testing.T) {
assert.Equal(t, "https://pb33f.io", doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value) assert.Equal(t, "https://pb33f.io", doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value)
assert.NotEmpty(t, doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value) assert.NotEmpty(t, doc.Tags.Value[1].Value.ExternalDocs.Value.URL.Value)
assert.Len(t, doc.Tags.Value[1].Value.Extensions, 0) assert.Len(t, doc.Tags.Value[1].Value.Extensions, 0)
} }
func TestCreateDocument_Paths(t *testing.T) { func TestCreateDocument_Paths(t *testing.T) {
@@ -438,7 +430,6 @@ func TestCreateDocument_Paths(t *testing.T) {
assert.NotNil(t, servers) assert.NotNil(t, servers)
assert.Len(t, servers, 1) assert.Len(t, servers, 1)
assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value) assert.Equal(t, "https://pb33f.io", servers[0].Value.URL.Value)
} }
func TestCreateDocument_Components_Schemas(t *testing.T) { func TestCreateDocument_Components_Schemas(t *testing.T) {
@@ -464,7 +455,6 @@ func TestCreateDocument_Components_Schemas(t *testing.T) {
p := fries.Value.Schema().FindProperty("favoriteDrink") p := fries.Value.Schema().FindProperty("favoriteDrink")
assert.Equal(t, "a frosty cold beverage can be coke or sprite", assert.Equal(t, "a frosty cold beverage can be coke or sprite",
p.Value.Schema().Description.Value) p.Value.Schema().Description.Value)
} }
func TestCreateDocument_Components_SecuritySchemes(t *testing.T) { func TestCreateDocument_Components_SecuritySchemes(t *testing.T) {
@@ -493,7 +483,6 @@ func TestCreateDocument_Components_SecuritySchemes(t *testing.T) {
readScope = oAuth.Flows.Value.AuthorizationCode.Value.FindScope("write:burgers") readScope = oAuth.Flows.Value.AuthorizationCode.Value.FindScope("write:burgers")
assert.NotNil(t, readScope) assert.NotNil(t, readScope)
assert.Equal(t, "modify burgers and stuff", readScope.Value) assert.Equal(t, "modify burgers and stuff", readScope.Value)
} }
func TestCreateDocument_Components_Responses(t *testing.T) { func TestCreateDocument_Components_Responses(t *testing.T) {
@@ -506,7 +495,6 @@ func TestCreateDocument_Components_Responses(t *testing.T) {
assert.NotNil(t, dressingResponse.Value) assert.NotNil(t, dressingResponse.Value)
assert.Equal(t, "all the dressings for a burger.", dressingResponse.Value.Description.Value) assert.Equal(t, "all the dressings for a burger.", dressingResponse.Value.Description.Value)
assert.Len(t, dressingResponse.Value.Content.Value, 1) assert.Len(t, dressingResponse.Value.Content.Value, 1)
} }
func TestCreateDocument_Components_Examples(t *testing.T) { func TestCreateDocument_Components_Examples(t *testing.T) {
@@ -593,7 +581,6 @@ func TestCreateDocument_Component_Discriminator(t *testing.T) {
assert.Nil(t, dsc.FindMappingValue("don't exist")) assert.Nil(t, dsc.FindMappingValue("don't exist"))
assert.NotNil(t, doc.GetExternalDocs()) assert.NotNil(t, doc.GetExternalDocs())
assert.Nil(t, doc.FindSecurityRequirement("scooby doo")) assert.Nil(t, doc.FindSecurityRequirement("scooby doo"))
} }
func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) { func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
@@ -601,11 +588,8 @@ func TestCreateDocument_CheckAdditionalProperties_Schema(t *testing.T) {
components := doc.Components.Value components := doc.Components.Value
d := components.FindSchema("Dressing") d := components.FindSchema("Dressing")
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
if n, ok := d.Value.Schema().AdditionalProperties.Value.(*base.SchemaProxy); ok {
assert.Equal(t, "something in here.", n.Schema().Description.Value) assert.True(t, d.Value.Schema().AdditionalProperties.Value.IsA(), "should be a schema")
} else {
assert.Fail(t, "should be a schema")
}
} }
func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) { func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) {
@@ -613,7 +597,7 @@ func TestCreateDocument_CheckAdditionalProperties_Bool(t *testing.T) {
components := doc.Components.Value components := doc.Components.Value
d := components.FindSchema("Drink") d := components.FindSchema("Drink")
assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value) assert.NotNil(t, d.Value.Schema().AdditionalProperties.Value)
assert.True(t, d.Value.Schema().AdditionalProperties.Value.(bool)) assert.True(t, d.Value.Schema().AdditionalProperties.Value.B)
} }
func TestCreateDocument_Components_Error(t *testing.T) { func TestCreateDocument_Components_Error(t *testing.T) {
@@ -667,7 +651,6 @@ components:
AllowRemoteReferences: false, AllowRemoteReferences: false,
}) })
assert.Len(t, err, 1) assert.Len(t, err, 1)
} }
func TestCreateDocument_Paths_Errors(t *testing.T) { func TestCreateDocument_Paths_Errors(t *testing.T) {
@@ -776,8 +759,8 @@ func TestCreateDocument_YamlAnchor(t *testing.T) {
assert.NotNil(t, jsonGet) assert.NotNil(t, jsonGet)
// Should this work? It doesn't // Should this work? It doesn't
//postJsonType := examplePath.GetValue().Post.GetValue().RequestBody.GetValue().FindContent("application/json") // postJsonType := examplePath.GetValue().Post.GetValue().RequestBody.GetValue().FindContent("application/json")
//assert.NotNil(t, postJsonType) // assert.NotNil(t, postJsonType)
} }
func ExampleCreateDocument() { func ExampleCreateDocument() {

View File

@@ -5,14 +5,14 @@ package model
import ( import (
"fmt" "fmt"
"golang.org/x/exp/slices"
"sort" "sort"
"sync" "sync"
"golang.org/x/exp/slices"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/pb33f/libopenapi/utils"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
@@ -24,16 +24,17 @@ import (
// PropertyChanges.Changes, and not in the AnyOfChanges property. // PropertyChanges.Changes, and not in the AnyOfChanges property.
type SchemaChanges struct { type SchemaChanges struct {
*PropertyChanges *PropertyChanges
DiscriminatorChanges *DiscriminatorChanges `json:"discriminator,omitempty" yaml:"discriminator,omitempty"` DiscriminatorChanges *DiscriminatorChanges `json:"discriminator,omitempty" yaml:"discriminator,omitempty"`
AllOfChanges []*SchemaChanges `json:"allOf,omitempty" yaml:"allOf,omitempty"` AllOfChanges []*SchemaChanges `json:"allOf,omitempty" yaml:"allOf,omitempty"`
AnyOfChanges []*SchemaChanges `json:"anyOf,omitempty" yaml:"anyOf,omitempty"` AnyOfChanges []*SchemaChanges `json:"anyOf,omitempty" yaml:"anyOf,omitempty"`
OneOfChanges []*SchemaChanges `json:"oneOf,omitempty" yaml:"oneOf,omitempty"` OneOfChanges []*SchemaChanges `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
NotChanges *SchemaChanges `json:"not,omitempty" yaml:"not,omitempty"` NotChanges *SchemaChanges `json:"not,omitempty" yaml:"not,omitempty"`
ItemsChanges *SchemaChanges `json:"items,omitempty" yaml:"items,omitempty"` ItemsChanges *SchemaChanges `json:"items,omitempty" yaml:"items,omitempty"`
SchemaPropertyChanges map[string]*SchemaChanges `json:"properties,omitempty" yaml:"properties,omitempty"` SchemaPropertyChanges map[string]*SchemaChanges `json:"properties,omitempty" yaml:"properties,omitempty"`
ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"` ExternalDocChanges *ExternalDocChanges `json:"externalDoc,omitempty" yaml:"externalDoc,omitempty"`
XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"` XMLChanges *XMLChanges `json:"xml,omitempty" yaml:"xml,omitempty"`
ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"` ExtensionChanges *ExtensionChanges `json:"extensions,omitempty" yaml:"extensions,omitempty"`
AdditionalPropertiesChanges *SchemaChanges `json:"additionalProperties,omitempty" yaml:"additionalProperties,omitempty"`
// 3.1 specifics // 3.1 specifics
IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"` IfChanges *SchemaChanges `json:"if,omitempty" yaml:"if,omitempty"`
@@ -102,6 +103,9 @@ func (s *SchemaChanges) GetAllChanges() []*Change {
if s.UnevaluatedPropertiesChanges != nil { if s.UnevaluatedPropertiesChanges != nil {
changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...) changes = append(changes, s.UnevaluatedPropertiesChanges.GetAllChanges()...)
} }
if s.AdditionalPropertiesChanges != nil {
changes = append(changes, s.AdditionalPropertiesChanges.GetAllChanges()...)
}
if s.SchemaPropertyChanges != nil { if s.SchemaPropertyChanges != nil {
for n := range s.SchemaPropertyChanges { for n := range s.SchemaPropertyChanges {
if s.SchemaPropertyChanges[n] != nil { if s.SchemaPropertyChanges[n] != nil {
@@ -185,6 +189,9 @@ func (s *SchemaChanges) TotalChanges() int {
if s.UnevaluatedPropertiesChanges != nil { if s.UnevaluatedPropertiesChanges != nil {
t += s.UnevaluatedPropertiesChanges.TotalChanges() t += s.UnevaluatedPropertiesChanges.TotalChanges()
} }
if s.AdditionalPropertiesChanges != nil {
t += s.AdditionalPropertiesChanges.TotalChanges()
}
if s.SchemaPropertyChanges != nil { if s.SchemaPropertyChanges != nil {
for n := range s.SchemaPropertyChanges { for n := range s.SchemaPropertyChanges {
if s.SchemaPropertyChanges[n] != nil { if s.SchemaPropertyChanges[n] != nil {
@@ -267,6 +274,9 @@ func (s *SchemaChanges) TotalBreakingChanges() int {
if s.UnevaluatedPropertiesChanges != nil { if s.UnevaluatedPropertiesChanges != nil {
t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges() t += s.UnevaluatedPropertiesChanges.TotalBreakingChanges()
} }
if s.AdditionalPropertiesChanges != nil {
t += s.AdditionalPropertiesChanges.TotalBreakingChanges()
}
if s.DependentSchemasChanges != nil { if s.DependentSchemasChanges != nil {
for n := range s.DependentSchemasChanges { for n := range s.DependentSchemasChanges {
t += s.DependentSchemasChanges[n].TotalBreakingChanges() t += s.DependentSchemasChanges[n].TotalBreakingChanges()
@@ -728,18 +738,36 @@ func checkSchemaPropertyChanges(
New: rSchema, New: rSchema,
}) })
// AdditionalProperties (only if not an object) // AdditionalProperties
if !utils.IsNodeMap(lSchema.AdditionalProperties.ValueNode) && if lSchema.AdditionalProperties.Value != nil && rSchema.AdditionalProperties.Value != nil {
!utils.IsNodeMap(rSchema.AdditionalProperties.ValueNode) { if lSchema.AdditionalProperties.Value.IsA() && rSchema.AdditionalProperties.Value.IsA() {
props = append(props, &PropertyCheck{ if !low.AreEqual(lSchema.AdditionalProperties.Value.A, rSchema.AdditionalProperties.Value.A) {
LeftNode: lSchema.AdditionalProperties.ValueNode, sc.AdditionalPropertiesChanges = CompareSchemas(lSchema.AdditionalProperties.Value.A, rSchema.AdditionalProperties.Value.A)
RightNode: rSchema.AdditionalProperties.ValueNode, }
Label: v3.AdditionalPropertiesLabel, } else {
Changes: changes, if lSchema.AdditionalProperties.Value.IsB() && rSchema.AdditionalProperties.Value.IsB() {
Breaking: false, if lSchema.AdditionalProperties.Value.B != rSchema.AdditionalProperties.Value.B {
Original: lSchema, CreateChange(changes, Modified, v3.AdditionalPropertiesLabel,
New: rSchema, lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, true,
}) lSchema.AdditionalProperties.Value.B, rSchema.AdditionalProperties.Value.B)
}
} else {
CreateChange(changes, Modified, v3.AdditionalPropertiesLabel,
lSchema.AdditionalProperties.ValueNode, rSchema.AdditionalProperties.ValueNode, true,
lSchema.AdditionalProperties.Value.B, rSchema.AdditionalProperties.Value.B)
}
}
}
// added AdditionalProperties
if lSchema.AdditionalProperties.Value == nil && rSchema.AdditionalProperties.Value != nil {
CreateChange(changes, ObjectAdded, v3.AdditionalPropertiesLabel,
nil, rSchema.AdditionalProperties.ValueNode, true, nil, rSchema.AdditionalProperties.Value)
}
// removed AdditionalProperties
if lSchema.AdditionalProperties.Value != nil && rSchema.AdditionalProperties.Value == nil {
CreateChange(changes, ObjectRemoved, v3.AdditionalPropertiesLabel,
lSchema.AdditionalProperties.ValueNode, nil, true, lSchema.AdditionalProperties.Value, nil)
} }
// Description // Description
@@ -1108,20 +1136,6 @@ func checkSchemaPropertyChanges(
// check extensions // check extensions
sc.ExtensionChanges = CompareExtensions(lSchema.Extensions, rSchema.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 // check core properties
CheckProperties(props) CheckProperties(props)
} }

View File

@@ -5,13 +5,14 @@ package model
import ( import (
"fmt" "fmt"
"testing"
"github.com/pb33f/libopenapi/datamodel" "github.com/pb33f/libopenapi/datamodel"
"github.com/pb33f/libopenapi/datamodel/low" "github.com/pb33f/libopenapi/datamodel/low"
"github.com/pb33f/libopenapi/datamodel/low/base" "github.com/pb33f/libopenapi/datamodel/low/base"
v2 "github.com/pb33f/libopenapi/datamodel/low/v2" v2 "github.com/pb33f/libopenapi/datamodel/low/v2"
v3 "github.com/pb33f/libopenapi/datamodel/low/v3" v3 "github.com/pb33f/libopenapi/datamodel/low/v3"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"testing"
) )
// These tests require full documents to be tested properly. schemas are perhaps the most complex // These tests require full documents to be tested properly. schemas are perhaps the most complex
@@ -158,7 +159,6 @@ components:
} }
func test_BuildDoc(l, r string) (*v3.Document, *v3.Document) { func test_BuildDoc(l, r string) (*v3.Document, *v3.Document) {
leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l)) leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l))
rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r)) rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r))
@@ -168,7 +168,6 @@ func test_BuildDoc(l, r string) (*v3.Document, *v3.Document) {
} }
func test_BuildDocv2(l, r string) (*v2.Swagger, *v2.Swagger) { func test_BuildDocv2(l, r string) (*v2.Swagger, *v2.Swagger) {
leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l)) leftInfo, _ := datamodel.ExtractSpecInfo([]byte(l))
rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r)) rightInfo, _ := datamodel.ExtractSpecInfo([]byte(r))
@@ -278,7 +277,6 @@ components:
assert.Equal(t, Modified, changes.Changes[0].ChangeType) assert.Equal(t, Modified, changes.Changes[0].ChangeType)
assert.Equal(t, v3.RefLabel, changes.Changes[0].Property) assert.Equal(t, v3.RefLabel, changes.Changes[0].Property)
assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].Original) assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].Original)
} }
func TestCompareSchemas_InlineToRef(t *testing.T) { func TestCompareSchemas_InlineToRef(t *testing.T) {
@@ -311,7 +309,6 @@ components:
assert.Equal(t, Modified, changes.Changes[0].ChangeType) assert.Equal(t, Modified, changes.Changes[0].ChangeType)
assert.Equal(t, v3.RefLabel, changes.Changes[0].Property) assert.Equal(t, v3.RefLabel, changes.Changes[0].Property)
assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].New) assert.Equal(t, "#/components/schemas/Yo", changes.Changes[0].New)
} }
func TestCompareSchemas_Identical(t *testing.T) { func TestCompareSchemas_Identical(t *testing.T) {
@@ -1222,6 +1219,93 @@ components:
assert.Equal(t, v3.UnevaluatedPropertiesLabel, changes.Changes[0].Property) assert.Equal(t, v3.UnevaluatedPropertiesLabel, changes.Changes[0].Property)
} }
func TestCompareSchemas_AdditionalProperties(t *testing.T) {
left := `openapi: 3.1
components:
schemas:
OK:
additionalProperties:
type: string`
right := `openapi: 3.1
components:
schemas:
OK:
additionalProperties:
type: int`
leftDoc, rightDoc := test_BuildDoc(left, right)
// extract left reference schema and non reference schema.
lSchemaProxy := leftDoc.Components.Value.FindSchema("OK").Value
rSchemaProxy := rightDoc.Components.Value.FindSchema("OK").Value
changes := CompareSchemas(lSchemaProxy, rSchemaProxy)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, 1, changes.AdditionalPropertiesChanges.PropertyChanges.TotalChanges())
}
func TestCompareSchemas_AdditionalProperties_Added(t *testing.T) {
left := `openapi: 3.1
components:
schemas:
OK:
type: string`
right := `openapi: 3.1
components:
schemas:
OK:
type: string
additionalProperties:
type: int`
leftDoc, rightDoc := test_BuildDoc(left, right)
// extract left reference schema and non reference schema.
lSchemaProxy := leftDoc.Components.Value.FindSchema("OK").Value
rSchemaProxy := rightDoc.Components.Value.FindSchema("OK").Value
changes := CompareSchemas(lSchemaProxy, rSchemaProxy)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.AdditionalPropertiesLabel, changes.Changes[0].Property)
}
func TestCompareSchemas_AdditionalProperties_Removed(t *testing.T) {
left := `openapi: 3.1
components:
schemas:
OK:
type: string`
right := `openapi: 3.1
components:
schemas:
OK:
type: string
additionalProperties:
type: int`
leftDoc, rightDoc := test_BuildDoc(left, right)
// extract left reference schema and non reference schema.
lSchemaProxy := leftDoc.Components.Value.FindSchema("OK").Value
rSchemaProxy := rightDoc.Components.Value.FindSchema("OK").Value
changes := CompareSchemas(rSchemaProxy, lSchemaProxy)
assert.NotNil(t, changes)
assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges())
assert.Equal(t, v3.AdditionalPropertiesLabel, changes.Changes[0].Property)
}
func TestCompareSchemas_UnevaluatedItems(t *testing.T) { func TestCompareSchemas_UnevaluatedItems(t *testing.T) {
left := `openapi: 3.1 left := `openapi: 3.1
components: components:
@@ -1611,7 +1695,6 @@ components:
assert.Equal(t, Modified, changes.AnyOfChanges[0].Changes[0].ChangeType) assert.Equal(t, Modified, changes.AnyOfChanges[0].Changes[0].ChangeType)
assert.Equal(t, "string", changes.AnyOfChanges[0].Changes[0].New) assert.Equal(t, "string", changes.AnyOfChanges[0].Changes[0].New)
assert.Equal(t, "bool", changes.AnyOfChanges[0].Changes[0].Original) assert.Equal(t, "bool", changes.AnyOfChanges[0].Changes[0].Original)
} }
func TestCompareSchemas_OneOfModifyAndAddItem(t *testing.T) { func TestCompareSchemas_OneOfModifyAndAddItem(t *testing.T) {
@@ -1848,7 +1931,6 @@ components:
assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType) assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType)
assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f", assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f",
low.HashToString(changes.Changes[0].NewObject.(*base.Discriminator).Hash())) low.HashToString(changes.Changes[0].NewObject.(*base.Discriminator).Hash()))
} }
func TestCompareSchemas_DiscriminatorRemove(t *testing.T) { func TestCompareSchemas_DiscriminatorRemove(t *testing.T) {
@@ -1881,7 +1963,6 @@ components:
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType) assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f", assert.Equal(t, "0e563831440581c713657dd857a0ec3af1bd7308a43bd3cae9184f61d61b288f",
low.HashToString(changes.Changes[0].OriginalObject.(*base.Discriminator).Hash())) low.HashToString(changes.Changes[0].OriginalObject.(*base.Discriminator).Hash()))
} }
func TestCompareSchemas_ExternalDocsChange(t *testing.T) { func TestCompareSchemas_ExternalDocsChange(t *testing.T) {
@@ -1948,7 +2029,6 @@ components:
assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType) assert.Equal(t, ObjectAdded, changes.Changes[0].ChangeType)
assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7", assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7",
low.HashToString(changes.Changes[0].NewObject.(*base.ExternalDoc).Hash())) low.HashToString(changes.Changes[0].NewObject.(*base.ExternalDoc).Hash()))
} }
func TestCompareSchemas_ExternalDocsRemove(t *testing.T) { func TestCompareSchemas_ExternalDocsRemove(t *testing.T) {
@@ -1981,7 +2061,6 @@ components:
assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType) assert.Equal(t, ObjectRemoved, changes.Changes[0].ChangeType)
assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7", assert.Equal(t, "2b7adf30f2ea3a7617ccf429a099617a9c03e8b5f3a23a89dba4b90f760010d7",
low.HashToString(changes.Changes[0].OriginalObject.(*base.ExternalDoc).Hash())) low.HashToString(changes.Changes[0].OriginalObject.(*base.ExternalDoc).Hash()))
} }
func TestCompareSchemas_AddExtension(t *testing.T) { func TestCompareSchemas_AddExtension(t *testing.T) {
@@ -2402,7 +2481,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges()) assert.Equal(t, 1, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_SchemaAdditionalPropertiesCheck(t *testing.T) { func TestCompareSchemas_SchemaAdditionalPropertiesCheck(t *testing.T) {
@@ -2432,7 +2510,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 0, changes.TotalBreakingChanges()) assert.Equal(t, 0, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_Schema_DeletePoly(t *testing.T) { func TestCompareSchemas_Schema_DeletePoly(t *testing.T) {
@@ -2466,7 +2543,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 1, changes.TotalBreakingChanges()) assert.Equal(t, 1, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_Schema_AddExamplesArray_AllOf(t *testing.T) { func TestCompareSchemas_Schema_AddExamplesArray_AllOf(t *testing.T) {
@@ -2499,7 +2575,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 0, changes.TotalBreakingChanges()) assert.Equal(t, 0, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_Schema_AddExampleMap_AllOf(t *testing.T) { func TestCompareSchemas_Schema_AddExampleMap_AllOf(t *testing.T) {
@@ -2566,7 +2641,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 0, changes.TotalBreakingChanges()) assert.Equal(t, 0, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_Schema_AddExamplesMap(t *testing.T) { func TestCompareSchemas_Schema_AddExamplesMap(t *testing.T) {
@@ -2601,7 +2675,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 0, changes.TotalBreakingChanges()) assert.Equal(t, 0, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_Schema_AddExamples(t *testing.T) { func TestCompareSchemas_Schema_AddExamples(t *testing.T) {
@@ -2661,7 +2734,6 @@ components:
assert.Equal(t, 1, changes.TotalChanges()) assert.Equal(t, 1, changes.TotalChanges())
assert.Len(t, changes.GetAllChanges(), 1) assert.Len(t, changes.GetAllChanges(), 1)
assert.Equal(t, 0, changes.TotalBreakingChanges()) assert.Equal(t, 0, changes.TotalBreakingChanges())
} }
func TestCompareSchemas_CheckIssue_170(t *testing.T) { func TestCompareSchemas_CheckIssue_170(t *testing.T) {