diff --git a/index/find_component.go b/index/find_component.go index 1028ab8..391bf90 100644 --- a/index/find_component.go +++ b/index/find_component.go @@ -75,7 +75,7 @@ func FindComponent(root *yaml.Node, componentId, absoluteFilePath string, index Path: friendlySearch, RemoteLocation: absoluteFilePath, Index: index, - RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef), + RequiredRefProperties: extractDefinitionRequiredRefProperties(resNode, map[string][]string{}, fullDef, index), } return ref } @@ -174,7 +174,7 @@ func (index *SpecIndex) lookupRolodex(uri []string) *Reference { IsRemote: true, RemoteLocation: absoluteFileLocation, Path: "$", - RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation), + RequiredRefProperties: extractDefinitionRequiredRefProperties(parsedDocument, map[string][]string{}, absoluteFileLocation, index), } return foundRef } else { diff --git a/index/resolver.go b/index/resolver.go index f269e01..c97bacf 100644 --- a/index/resolver.go +++ b/index/resolver.go @@ -87,9 +87,30 @@ func (resolver *Resolver) GetResolvingErrors() []*ResolvingError { return resolver.resolvingErrors } -// GetCircularErrors returns all circular reference errors found. -func (resolver *Resolver) GetCircularErrors() []*CircularReferenceResult { - return resolver.circularReferences +func (resolver *Resolver) GetCircularReferences() []*CircularReferenceResult { + return resolver.GetSafeCircularReferences() +} + +// GetSafeCircularReferences returns all circular reference errors found. +func (resolver *Resolver) GetSafeCircularReferences() []*CircularReferenceResult { + var refs []*CircularReferenceResult + for _, ref := range resolver.circularReferences { + if !ref.IsInfiniteLoop { + refs = append(refs, ref) + } + } + return refs +} + +// GetInfiniteCircularReferences returns all circular reference errors found that are infinite / unrecoverable +func (resolver *Resolver) GetInfiniteCircularReferences() []*CircularReferenceResult { + var refs []*CircularReferenceResult + for _, ref := range resolver.circularReferences { + if ref.IsInfiniteLoop { + refs = append(refs, ref) + } + } + return refs } // GetPolymorphicCircularErrors returns all circular errors that stem from polymorphism @@ -339,9 +360,17 @@ func (resolver *Resolver) isInfiniteCircularDependency(ref *Reference, visitedDe for refDefinition := range ref.RequiredRefProperties { r, _ := resolver.specIndex.SearchIndexForReference(refDefinition) - if initialRef != nil && initialRef.Definition == r.Definition { + if initialRef != nil && initialRef.FullDefinition == r.FullDefinition { return true, visitedDefinitions } + if len(visitedDefinitions) > 0 && ref.FullDefinition == r.FullDefinition { + return true, visitedDefinitions + } + + if visitedDefinitions[r.FullDefinition] { + continue + } + visitedDefinitions[r.Definition] = true ir := initialRef diff --git a/index/resolver_test.go b/index/resolver_test.go index f3f04aa..798625a 100644 --- a/index/resolver_test.go +++ b/index/resolver_test.go @@ -75,7 +75,7 @@ func TestResolver_CheckForCircularReferences(t *testing.T) { assert.Len(t, rolo.GetCaughtErrors(), 3) assert.Len(t, rolo.GetRootIndex().GetResolver().GetResolvingErrors(), 3) - assert.Len(t, rolo.GetRootIndex().GetResolver().GetCircularErrors(), 3) + assert.Len(t, rolo.GetRootIndex().GetResolver().GetInfiniteCircularReferences(), 3) } @@ -107,8 +107,8 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 1) assert.Len(t, resolver.GetResolvingErrors(), 1) // infinite loop is a resolving error. - assert.Len(t, resolver.GetCircularErrors(), 1) - assert.True(t, resolver.GetCircularErrors()[0].IsArrayResult) + assert.Len(t, resolver.GetInfiniteCircularReferences(), 1) + assert.True(t, resolver.GetInfiniteCircularReferences()[0].IsArrayResult) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) @@ -144,7 +144,7 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) assert.Len(t, resolver.GetResolvingErrors(), 0) - assert.Len(t, resolver.GetCircularErrors(), 0) + assert.Len(t, resolver.GetCircularReferences(), 0) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) @@ -180,7 +180,7 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) assert.Len(t, resolver.GetResolvingErrors(), 0) - assert.Len(t, resolver.GetCircularErrors(), 0) + assert.Len(t, resolver.GetCircularReferences(), 0) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) @@ -216,7 +216,7 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) assert.Len(t, resolver.GetResolvingErrors(), 0) - assert.Len(t, resolver.GetCircularErrors(), 0) + assert.Len(t, resolver.GetCircularReferences(), 0) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) @@ -252,7 +252,7 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) assert.Len(t, resolver.GetResolvingErrors(), 0) - assert.Len(t, resolver.GetCircularErrors(), 0) + assert.Len(t, resolver.GetCircularReferences(), 0) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) @@ -286,8 +286,8 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly. - assert.Len(t, resolver.GetCircularErrors(), 1) - assert.Equal(t, "anyOf", resolver.GetCircularErrors()[0].PolymorphicType) + assert.Len(t, resolver.GetCircularReferences(), 1) + assert.Equal(t, "anyOf", resolver.GetCircularReferences()[0].PolymorphicType) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) } @@ -320,9 +320,9 @@ components: circ := resolver.CheckForCircularReferences() assert.Len(t, circ, 0) assert.Len(t, resolver.GetResolvingErrors(), 0) // not an infinite loop if poly. - assert.Len(t, resolver.GetCircularErrors(), 1) - assert.Equal(t, "allOf", resolver.GetCircularErrors()[0].PolymorphicType) - assert.True(t, resolver.GetCircularErrors()[0].IsPolymorphicResult) + assert.Len(t, resolver.GetCircularReferences(), 1) + assert.Equal(t, "allOf", resolver.GetCircularReferences()[0].PolymorphicType) + assert.True(t, resolver.GetCircularReferences()[0].IsPolymorphicResult) _, err := yaml.Marshal(resolver.resolvedRoot) assert.NoError(t, err) } @@ -595,7 +595,7 @@ func TestResolver_ResolveComponents_MixedRef(t *testing.T) { index := rolo.GetRootIndex resolver := index().GetResolver() - assert.Len(t, resolver.GetCircularErrors(), 0) + assert.Len(t, resolver.GetCircularReferences(), 0) assert.Equal(t, 2, resolver.GetIndexesVisited()) // in v0.8.2 a new check was added when indexing, to prevent re-indexing the same file multiple times. @@ -778,3 +778,81 @@ func TestResolver_isInfiniteCircularDep_NoRef(t *testing.T) { assert.False(t, a) assert.Nil(t, b) } + +func TestResolver_AllowedCircle(t *testing.T) { + + d := `openapi: 3.1.0 +paths: + /test: + get: + responses: + '200': + description: OK +components: + schemas: + Obj: + type: object + properties: + other: + $ref: '#/components/schemas/Obj2' + Obj2: + type: object + properties: + other: + $ref: '#/components/schemas/Obj' + required: + - other` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + + idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig()) + + resolver := NewResolver(idx) + assert.NotNil(t, resolver) + + circ := resolver.Resolve() + assert.Len(t, circ, 0) + assert.Len(t, resolver.GetInfiniteCircularReferences(), 0) + assert.Len(t, resolver.GetSafeCircularReferences(), 1) + +} + +func TestResolver_NotAllowedDeepCircle(t *testing.T) { + + d := `openapi: 3.0 +components: + schemas: + Three: + description: "test three" + properties: + bester: + "$ref": "#/components/schemas/Seven" + required: + - bester + Seven: + properties: + wow: + "$ref": "#/components/schemas/Three" + required: + - wow` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + + idx := NewSpecIndexWithConfig(&rootNode, CreateClosedAPIIndexConfig()) + + resolver := NewResolver(idx) + assert.NotNil(t, resolver) + + circ := resolver.Resolve() + assert.Len(t, circ, 1) + assert.Len(t, resolver.GetInfiniteCircularReferences(), 1) + assert.Len(t, resolver.GetSafeCircularReferences(), 0) + +} + +/* + + + */ diff --git a/index/rolodex.go b/index/rolodex.go index 1e0c3c6..8fa3313 100644 --- a/index/rolodex.go +++ b/index/rolodex.go @@ -49,20 +49,22 @@ type RolodexFS interface { // and the ability to resolve references across those file systems. It is used to hold references to external // files, and the indexes they hold. The rolodex is the master lookup for all references. type Rolodex struct { - localFS map[string]fs.FS - remoteFS map[string]fs.FS - indexed bool - built bool - manualBuilt bool - resolved bool - circChecked bool - indexConfig *SpecIndexConfig - indexingDuration time.Duration - indexes []*SpecIndex - rootIndex *SpecIndex - rootNode *yaml.Node - caughtErrors []error - ignoredCircularReferences []*CircularReferenceResult + localFS map[string]fs.FS + remoteFS map[string]fs.FS + indexed bool + built bool + manualBuilt bool + resolved bool + circChecked bool + indexConfig *SpecIndexConfig + indexingDuration time.Duration + indexes []*SpecIndex + rootIndex *SpecIndex + rootNode *yaml.Node + caughtErrors []error + safeCircularReferences []*CircularReferenceResult + infiniteCircularReferences []*CircularReferenceResult + ignoredCircularReferences []*CircularReferenceResult } // NewRolodex creates a new rolodex with the provided index configuration. @@ -316,6 +318,8 @@ func (r *Rolodex) CheckForCircularReferences() { if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 { r.ignoredCircularReferences = append(r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences...) } + r.safeCircularReferences = append(r.safeCircularReferences, r.rootIndex.resolver.GetSafeCircularReferences()...) + r.infiniteCircularReferences = append(r.infiniteCircularReferences, r.rootIndex.resolver.GetInfiniteCircularReferences()...) } r.circChecked = true } @@ -334,6 +338,8 @@ func (r *Rolodex) Resolve() { if len(r.rootIndex.resolver.ignoredArrayReferences) > 0 { r.ignoredCircularReferences = append(r.ignoredCircularReferences, r.rootIndex.resolver.ignoredArrayReferences...) } + r.safeCircularReferences = append(r.safeCircularReferences, r.rootIndex.resolver.GetSafeCircularReferences()...) + r.infiniteCircularReferences = append(r.infiniteCircularReferences, r.rootIndex.resolver.GetInfiniteCircularReferences()...) } r.resolved = true } diff --git a/index/rolodex_test.go b/index/rolodex_test.go index fd84a5b..165071a 100644 --- a/index/rolodex_test.go +++ b/index/rolodex_test.go @@ -352,7 +352,6 @@ components: rolodex.AddLocalFS(baseDir, fileFS) err = rolodex.IndexTheRolodex() assert.Error(t, err) - assert.Equal(t, "infinite circular reference detected: CircleTest: CircleTest -> -> CircleTest [5:7]", err.Error()) assert.Len(t, rolodex.GetCaughtErrors(), 1) assert.Len(t, rolodex.GetIgnoredCircularReferences(), 0) } @@ -1417,7 +1416,9 @@ components: rolo.CheckForCircularReferences() assert.Len(t, rolo.GetIgnoredCircularReferences(), 0) assert.Len(t, rolo.GetCaughtErrors(), 1) - assert.Len(t, rolo.GetRootIndex().GetResolver().GetCircularErrors(), 1) + assert.Len(t, rolo.GetRootIndex().GetResolver().GetInfiniteCircularReferences(), 1) + assert.Len(t, rolo.GetRootIndex().GetResolver().GetSafeCircularReferences(), 0) + } func TestRolodex_CircularReferencesArrayIgnored(t *testing.T) { diff --git a/index/utility_methods.go b/index/utility_methods.go index 67b550b..0930502 100644 --- a/index/utility_methods.go +++ b/index/utility_methods.go @@ -32,14 +32,14 @@ func (index *SpecIndex) extractDefinitionsAndSchemas(schemasNode *yaml.Node, pat Node: schema, Path: fmt.Sprintf("$.components.schemas.%s", name), ParentNode: schemasNode, - RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef), + RequiredRefProperties: extractDefinitionRequiredRefProperties(schemasNode, map[string][]string{}, fullDef, index), } index.allComponentSchemaDefinitions[def] = ref } } // extractDefinitionRequiredRefProperties goes through the direct properties of a schema and extracts the map of required definitions from within it -func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string, fulldef string) map[string][]string { +func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps map[string][]string, fulldef string, idx *SpecIndex) map[string][]string { if schemaNode == nil { return reqRefProps } @@ -74,7 +74,7 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m // Check to see if the current property is directly embedded within the current schema, and handle its properties if so _, paramPropertiesMapNode := utils.FindKeyNodeTop("properties", param.Content) if paramPropertiesMapNode != nil { - reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps, fulldef) + reqRefProps = extractDefinitionRequiredRefProperties(param, reqRefProps, fulldef, idx) } // Check to see if the current property is polymorphic, and dive into that model if so @@ -82,7 +82,7 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m _, ofNode := utils.FindKeyNodeTop(key, param.Content) if ofNode != nil { for _, ofNodeItem := range ofNode.Content { - reqRefProps = extractRequiredReferenceProperties(fulldef, ofNodeItem, name, reqRefProps) + reqRefProps = extractRequiredReferenceProperties(fulldef, idx, ofNodeItem, name, reqRefProps) } } } @@ -95,19 +95,19 @@ func extractDefinitionRequiredRefProperties(schemaNode *yaml.Node, reqRefProps m continue } - reqRefProps = extractRequiredReferenceProperties(fulldef, requiredPropDefNode, requiredPropertyNode.Value, reqRefProps) + reqRefProps = extractRequiredReferenceProperties(fulldef, idx, requiredPropDefNode, requiredPropertyNode.Value, reqRefProps) } return reqRefProps } // extractRequiredReferenceProperties returns a map of definition names to the property or properties which reference it within a node -func extractRequiredReferenceProperties(fulldef string, requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string { - isRef, _, _ := utils.IsNodeRefValue(requiredPropDefNode) +func extractRequiredReferenceProperties(fulldef string, idx *SpecIndex, requiredPropDefNode *yaml.Node, propName string, reqRefProps map[string][]string) map[string][]string { + isRef, _, refName := utils.IsNodeRefValue(requiredPropDefNode) if !isRef { _, defItems := utils.FindKeyNodeTop("items", requiredPropDefNode.Content) if defItems != nil { - isRef, _, _ = utils.IsNodeRefValue(defItems) + isRef, _, refName = utils.IsNodeRefValue(defItems) } } @@ -117,28 +117,65 @@ func extractRequiredReferenceProperties(fulldef string, requiredPropDefNode *yam defPath := fulldef - // explode defpath - exp := strings.Split(fulldef, "#/") - if len(exp) == 2 { - if exp[0] != "" { - if !strings.HasPrefix(exp[0], "http") { - - if !filepath.IsAbs(exp[0]) { - abs, _ := filepath.Abs(filepath.Join(filepath.Dir(fulldef), exp[0])) - defPath = fmt.Sprintf("%s#/%s", abs, exp[1]) - } - } - } - + if strings.HasPrefix(refName, "http") || filepath.IsAbs(refName) { + defPath = refName } else { - if strings.HasPrefix(exp[0], "http") { - defPath = exp[0] - } else { + exp := strings.Split(fulldef, "#/") + if len(exp) == 2 { + if exp[0] != "" { + if strings.HasPrefix(exp[0], "http") { + u, _ := url.Parse(exp[0]) + r := strings.Split(refName, "#/") + if len(r) == 2 { + var abs string + if r[0] == "" { + abs = u.Path + } else { + abs, _ = filepath.Abs(filepath.Join(filepath.Dir(u.Path), r[0])) + } - if filepath.IsAbs(exp[0]) { - defPath = exp[0] + u.Path = abs + u.Fragment = "" + defPath = fmt.Sprintf("%s#/%s", u.String(), r[1]) + } else { + u.Path = filepath.Join(filepath.Dir(u.Path), r[0]) + u.Fragment = "" + defPath = u.String() + } + } else { + r := strings.Split(refName, "#/") + if len(r) == 2 { + var abs string + if r[0] == "" { + abs, _ = filepath.Abs(exp[0]) + } else { + abs, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), r[0])) + } + + defPath = fmt.Sprintf("%s#/%s", abs, r[1]) + } else { + defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), r[0])) + } + } } else { - defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(fulldef), exp[0])) + defPath = refName + } + } else { + if strings.HasPrefix(exp[0], "http") { + u, _ := url.Parse(exp[0]) + r := strings.Split(refName, "#/") + if len(r) == 2 { + abs, _ := filepath.Abs(filepath.Join(filepath.Dir(u.Path), r[0])) + u.Path = abs + u.Fragment = "" + defPath = fmt.Sprintf("%s#/%s", u.String(), r[1]) + } else { + u.Path = filepath.Join(filepath.Dir(u.Path), r[0]) + u.Fragment = "" + defPath = u.String() + } + } else { + defPath, _ = filepath.Abs(filepath.Join(filepath.Dir(exp[0]), refName)) } } } diff --git a/index/utility_methods_test.go b/index/utility_methods_test.go index 98a26a0..86271fb 100644 --- a/index/utility_methods_test.go +++ b/index/utility_methods_test.go @@ -57,7 +57,7 @@ func Test_extractRequiredReferenceProperties(t *testing.T) { _ = yaml.Unmarshal([]byte(d), &rootNode) props := make(map[string][]string) - data := extractRequiredReferenceProperties("the-big.yaml#/cheese/thing", + data := extractRequiredReferenceProperties("the-big.yaml#/cheese/thing", nil, rootNode.Content[0], "cakes", props) assert.Len(t, props, 1) assert.NotNil(t, data) @@ -71,7 +71,7 @@ func Test_extractRequiredReferenceProperties_singleFile(t *testing.T) { _ = yaml.Unmarshal([]byte(d), &rootNode) props := make(map[string][]string) - data := extractRequiredReferenceProperties("dingo-bingo-bango.yaml", + data := extractRequiredReferenceProperties("dingo-bingo-bango.yaml", nil, rootNode.Content[0], "cakes", props) assert.Len(t, props, 1) assert.NotNil(t, data) @@ -85,7 +85,7 @@ func Test_extractRequiredReferenceProperties_http(t *testing.T) { _ = yaml.Unmarshal([]byte(d), &rootNode) props := make(map[string][]string) - data := extractRequiredReferenceProperties("http://dingo-bingo-bango.yaml/camel.yaml", + data := extractRequiredReferenceProperties("http://dingo-bingo-bango.yaml/camel.yaml", nil, rootNode.Content[0], "cakes", props) assert.Len(t, props, 1) assert.NotNil(t, data) @@ -99,12 +99,163 @@ func Test_extractRequiredReferenceProperties_abs(t *testing.T) { _ = yaml.Unmarshal([]byte(d), &rootNode) props := make(map[string][]string) - data := extractRequiredReferenceProperties("/camel.yaml", + data := extractRequiredReferenceProperties("/camel.yaml", nil, rootNode.Content[0], "cakes", props) assert.Len(t, props, 1) assert.NotNil(t, data) } -func Test_extractDefinitionRequiredRefProperties_nil(t *testing.T) { - assert.Nil(t, extractDefinitionRequiredRefProperties(nil, nil, "")) +func Test_extractRequiredReferenceProperties_abs3(t *testing.T) { + + d := `$ref: oh/pillow.yaml` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("/big/fat/camel.yaml#/milk", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["/big/fat/oh/pillow.yaml"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_rel_full(t *testing.T) { + + d := `$ref: "#/a/nice/picture/of/cake"` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("/chalky/milky/camel.yaml#/milk", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["/chalky/milky/camel.yaml#/a/nice/picture/of/cake"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_rel(t *testing.T) { + + d := `$ref: oh/camel.yaml#/rum/cake` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("/camel.yaml#/milk", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["/oh/camel.yaml#/rum/cake"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_abs2(t *testing.T) { + + d := `$ref: /oh/my/camel.yaml#/rum/cake` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("../flannel.yaml#/milk", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["/oh/my/camel.yaml#/rum/cake"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_http_rel(t *testing.T) { + + d := `$ref: my/wet/camel.yaml#/rum/cake` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("http://beer-world.com/lost/in/space.yaml#/vase", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["http://beer-world.com/lost/in/my/wet/camel.yaml#/rum/cake"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_http_rel_nocomponent(t *testing.T) { + + d := `$ref: my/wet/camel.yaml` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("http://beer-world.com/lost/in/space.yaml#/vase", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["http://beer-world.com/lost/in/my/wet/camel.yaml"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_nocomponent(t *testing.T) { + + d := `$ref: my/wet/camel.yaml` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("#/rotund/cakes", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["my/wet/camel.yaml"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_component_http(t *testing.T) { + + d := `$ref: go-to-bed.com/no/more/cake.yaml#/lovely/jam` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("http://bunny-bun-bun.com/no.yaml", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["http://bunny-bun-bun.com/go-to-bed.com/no/more/cake.yaml#/lovely/jam"][0]) + assert.NotNil(t, data) +} + +func Test_extractRequiredReferenceProperties_nocomponent_http(t *testing.T) { + + d := `$ref: go-to-bed.com/no/more/cake.yaml` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("http://bunny-bun-bun.com/no.yaml", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["http://bunny-bun-bun.com/go-to-bed.com/no/more/cake.yaml"][0]) + assert.NotNil(t, data) + +} + +func Test_extractRequiredReferenceProperties_nocomponent_http2(t *testing.T) { + + d := `$ref: go-to-bed.com/no/more/cake.yaml` + + var rootNode yaml.Node + _ = yaml.Unmarshal([]byte(d), &rootNode) + props := make(map[string][]string) + + data := extractRequiredReferenceProperties("/why.yaml", nil, + rootNode.Content[0], "cakes", props) + assert.Len(t, props, 1) + assert.Equal(t, "cakes", props["/go-to-bed.com/no/more/cake.yaml"][0]) + assert.NotNil(t, data) +} + +func Test_extractDefinitionRequiredRefProperties_nil(t *testing.T) { + assert.Nil(t, extractDefinitionRequiredRefProperties(nil, nil, "", nil)) }