99.9 % coverage & full OpenAPI v3 support

A single line that tries to read an HTTP response body and fails is very hard to test without mocking, and the mock does not add value to a single line of code to check for an error that can rarely ever be triggered. Going to settle for 99.9% for now.
This commit is contained in:
Dave Shanley
2022-08-31 10:04:39 -04:00
parent 62a7c88631
commit 0de0c16c0c
6 changed files with 530 additions and 67 deletions

View File

@@ -0,0 +1,30 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package index
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestCircularReferenceResult_GenerateJourneyPath(t *testing.T) {
refs := []*Reference{
{Name: "chicken"},
{Name: "nuggets"},
{Name: "chicken"},
{Name: "soup"},
{Name: "chicken"},
{Name: "nuggets"},
{Name: "for"},
{Name: "me"},
{Name: "and"},
{Name: "you"},
}
cr := &CircularReferenceResult{Journey: refs}
assert.Equal(t, "chicken -> nuggets -> chicken -> soup -> "+
"chicken -> nuggets -> for -> me -> and -> you", cr.GenerateJourneyPath())
}

View File

@@ -454,6 +454,11 @@ func (index *SpecIndex) GetParametersNode() *yaml.Node {
return index.parametersNode return index.parametersNode
} }
// GetReferenceIndexErrors will return any errors that occurred when indexing references
func (index *SpecIndex) GetReferenceIndexErrors() []*IndexingError {
return index.refErrors
}
// GetOperationParametersIndexErrors any errors that occurred when indexing operation parameters // GetOperationParametersIndexErrors any errors that occurred when indexing operation parameters
func (index *SpecIndex) GetOperationParametersIndexErrors() []*IndexingError { func (index *SpecIndex) GetOperationParametersIndexErrors() []*IndexingError {
return index.operationParamErrors return index.operationParamErrors
@@ -1687,6 +1692,7 @@ func (index *SpecIndex) extractComponentSecuritySchemes(securitySchemesNode *yam
func (index *SpecIndex) performExternalLookup(uri []string, componentId string, func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference { lookupFunction ExternalLookupFunction, parent *yaml.Node) *Reference {
if len(uri) > 0 {
externalSpecIndex := index.externalSpecIndex[uri[0]] externalSpecIndex := index.externalSpecIndex[uri[0]]
var foundNode *yaml.Node var foundNode *yaml.Node
if externalSpecIndex == nil { if externalSpecIndex == nil {
@@ -1731,6 +1737,7 @@ func (index *SpecIndex) performExternalLookup(uri []string, componentId string,
} }
return ref return ref
} }
}
return nil return nil
} }
@@ -1895,10 +1902,6 @@ func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Nod
index.remoteLock.Unlock() index.remoteLock.Unlock()
} }
if parsedRemoteDocument == nil {
return nil, nil, fmt.Errorf("unable to parse remote reference: '%s'", uri[0])
}
// lookup item from reference by using a path query. // lookup item from reference by using a path query.
query := fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", ".")) query := fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))
@@ -1910,14 +1913,10 @@ func (index *SpecIndex) lookupRemoteReference(ref string) (*yaml.Node, *yaml.Nod
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
result, err := path.Find(parsedRemoteDocument) result, _ := path.Find(parsedRemoteDocument)
if err != nil {
return nil, nil, err
}
if len(result) == 1 { if len(result) == 1 {
return result[0], parsedRemoteDocument, nil return result[0], parsedRemoteDocument, nil
} }
return nil, nil, nil return nil, nil, nil
} }
@@ -1951,10 +1950,6 @@ func (index *SpecIndex) lookupFileReference(ref string) (*yaml.Node, *yaml.Node,
index.seenRemoteSources[file] = &remoteDoc index.seenRemoteSources[file] = &remoteDoc
} }
if parsedRemoteDocument == nil {
return nil, nil, fmt.Errorf("unable to parse file reference: '%s'", file)
}
// lookup item from reference by using a path query. // lookup item from reference by using a path query.
query := fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", ".")) query := fmt.Sprintf("$%s", strings.ReplaceAll(uri[1], "/", "."))

View File

@@ -1,9 +1,13 @@
// Copyright 2022 Princess B33f Heavy Industries / Dave Shanley
// SPDX-License-Identifier: MIT
package index package index
import ( import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
"io/ioutil" "io/ioutil"
"os"
"testing" "testing"
) )
@@ -430,5 +434,141 @@ paths:
index := NewSpecIndex(&rootNode) index := NewSpecIndex(&rootNode)
assert.Equal(t, 4, index.GetGlobalCallbacksCount()) assert.Equal(t, 4, index.GetGlobalCallbacksCount())
}
func TestSpecIndex_ExtractComponentsFromRefs(t *testing.T) {
yml := `components:
schemas:
pizza:
properties:
something:
$ref: '#/components/\schemas/\something'
something:
description: something`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := NewSpecIndex(&rootNode)
assert.Len(t, index.GetReferenceIndexErrors(), 1)
}
func TestSpecIndex_FindComponent(t *testing.T) {
yml := `components:
schemas:
pizza:
properties:
something:
$ref: '#/components/schemas/something'
something:
description: something`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := NewSpecIndex(&rootNode)
assert.Nil(t, index.FindComponent("I-do-not-exist", nil))
}
func TestSpecIndex_performExternalLookup(t *testing.T) {
yml := `components:
schemas:
pizza:
properties:
something:
$ref: '#/components/schemas/something'
something:
description: something`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := NewSpecIndex(&rootNode)
assert.Nil(t, index.performExternalLookup(nil, "unknown", nil, nil))
}
func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_Error(t *testing.T) {
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{}
_, _, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/$.....#[;]something")
assert.Error(t, err)
}
func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadFind(t *testing.T) {
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["https://no-hope-for-a-dope.com"] = &yaml.Node{}
a, b, err := index.lookupRemoteReference("https://no-hope-for-a-dope.com#/hey")
assert.NoError(t, err)
assert.Nil(t, a)
assert.Nil(t, b)
}
func TestSpecIndex_lookupRemoteReference_SeenSourceSimulation_BadJSON(t *testing.T) {
index := new(SpecIndex)
a, b, err := index.lookupRemoteReference("https://google.com#/hey")
assert.Error(t, err)
assert.Nil(t, a)
assert.Nil(t, b)
}
func TestSpecIndex_lookupFileReference_BadFileName(t *testing.T) {
index := new(SpecIndex)
_, _, err := index.lookupFileReference("not-a-reference")
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_SeenSourceSimulation_Error(t *testing.T) {
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
index.seenRemoteSources["magic-money-file.json"] = &yaml.Node{}
_, _, err := index.lookupFileReference("magic-money-file.json#something")
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_BadFile(t *testing.T) {
index := new(SpecIndex)
_, _, err := index.lookupFileReference("chickers.json#no-rice")
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_BadFileDataRead(t *testing.T) {
_ = ioutil.WriteFile("chickers.yaml", []byte("broke: the: thing: [again]"), 0664)
defer os.Remove("chickers.yaml")
index := new(SpecIndex)
_, _, err := index.lookupFileReference("chickers.yaml#no-rice")
assert.Error(t, err)
}
func TestSpecIndex_lookupFileReference_MultiRes(t *testing.T) {
_ = ioutil.WriteFile("embie.yaml", []byte("naughty:\n - puppy: dog\n - puppy: naughty\npuppy:\n - naughty: puppy"), 0664)
defer os.Remove("embie.yaml")
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
k, doc, err := index.lookupFileReference("embie.yaml#/.naughty")
assert.NoError(t, err)
assert.NotNil(t, doc)
assert.Nil(t, k)
}
func TestSpecIndex_lookupFileReference(t *testing.T) {
_ = ioutil.WriteFile("fox.yaml", []byte("good:\n - puppy: dog\n - puppy: forever-more"), 0664)
defer os.Remove("fox.yaml")
index := new(SpecIndex)
index.seenRemoteSources = make(map[string]*yaml.Node)
k, doc, err := index.lookupFileReference("fox.yaml#/good")
assert.NoError(t, err)
assert.NotNil(t, doc)
assert.NotNil(t, k)
} }

View File

@@ -41,6 +41,38 @@ func TestResolver_ResolveComponents_CircularSpec(t *testing.T) {
assert.NoError(t, err) assert.NoError(t, err)
} }
func TestResolver_CheckForCircularReferences(t *testing.T) {
circular, _ := ioutil.ReadFile("../test_specs/circular-tests.yaml")
var rootNode yaml.Node
yaml.Unmarshal(circular, &rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 3)
assert.Len(t, resolver.GetResolvingErrors(), 3)
assert.Len(t, resolver.GetCircularErrors(), 3)
_, err := yaml.Marshal(resolver.resolvedRoot)
assert.NoError(t, err)
}
func TestResolver_DeepJourney(t *testing.T) {
var journey []*index.Reference
for f := 0; f < 200; f++ {
journey = append(journey, nil)
}
index := index.NewSpecIndex(nil)
resolver := NewResolver(index)
assert.Nil(t, resolver.extractRelatives(nil, nil, journey, false))
}
func TestResolver_ResolveComponents_Stripe(t *testing.T) { func TestResolver_ResolveComponents_Stripe(t *testing.T) {
stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml") stripe, _ := ioutil.ReadFile("../test_specs/stripe.yaml")
@@ -60,6 +92,57 @@ func TestResolver_ResolveComponents_Stripe(t *testing.T) {
} }
func TestResolver_ResolveComponents_BurgerShop(t *testing.T) {
mixedref, _ := ioutil.ReadFile("../test_specs/burgershop.openapi.yaml")
var rootNode yaml.Node
yaml.Unmarshal(mixedref, &rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.Resolve()
assert.Len(t, circ, 0)
}
func TestResolver_ResolveComponents_PolyNonCircRef(t *testing.T) {
yml := `paths:
/hey:
get:
responses:
"200":
$ref: '#/components/schemas/crackers'
components:
schemas:
cheese:
description: cheese
anyOf:
items:
$ref: '#/components/schemas/crackers'
crackers:
description: crackers
allOf:
- $ref: '#/components/schemas/tea'
tea:
description: tea`
var rootNode yaml.Node
yaml.Unmarshal([]byte(yml), &rootNode)
index := index.NewSpecIndex(&rootNode)
resolver := NewResolver(index)
assert.NotNil(t, resolver)
circ := resolver.CheckForCircularReferences()
assert.Len(t, circ, 0)
}
func TestResolver_ResolveComponents_MixedRef(t *testing.T) { func TestResolver_ResolveComponents_MixedRef(t *testing.T) {
mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml") mixedref, _ := ioutil.ReadFile("../test_specs/mixedref-burgershop.openapi.yaml")

View File

@@ -30,6 +30,7 @@ const (
KebabCase KebabCase
ScreamingKebabCase ScreamingKebabCase
RegularCase RegularCase
UnknownCase
) )
// FindNodes will find a node based on JSONPath, it accepts raw yaml/json as input. // FindNodes will find a node based on JSONPath, it accepts raw yaml/json as input.
@@ -169,7 +170,7 @@ func FindFirstKeyNode(key string, nodes []*yaml.Node, depth int) (keyNode *yaml.
for i, v := range nodes { for i, v := range nodes {
if key != "" && key == v.Value { if key != "" && key == v.Value {
if i+1 >= len(nodes) { if i+1 >= len(nodes) {
return v, nodes[i] // next node is what we need. return v, nodes[i] // this is the node we need.
} }
return v, nodes[i+1] // next node is what we need. return v, nodes[i+1] // next node is what we need.
} }
@@ -239,21 +240,23 @@ func FindKeyNode(key string, nodes []*yaml.Node) (keyNode *yaml.Node, valueNode
return nil, nil return nil, nil
} }
// FindKeyNodeFull is an overloaded version of FindKeyNode. Thins version however returns keys, labels and values.
// generally different things are required from different node trees, so depending on what this function is looking at
// it will return different things.
func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) { func FindKeyNodeFull(key string, nodes []*yaml.Node) (keyNode *yaml.Node, labelNode *yaml.Node, valueNode *yaml.Node) {
for i, v := range nodes { for i := range nodes {
if i%2 == 0 && key == v.Value { if i%2 == 0 && key == nodes[i].Value {
return v, nodes[i], nodes[i+1] // next node is what we need. return nodes[i], nodes[i], nodes[i+1] // next node is what we need.
} }
} }
for _, v := range nodes { for _, v := range nodes {
for x, j := range v.Content { for x := range v.Content {
if key == j.Value { if key == v.Content[x].Value {
if IsNodeMap(v) { if IsNodeMap(v) {
if x+1 == len(v.Content) { if x+1 == len(v.Content) {
return v, v.Content[x], v.Content[x] return v, v.Content[x], v.Content[x]
} }
return v, v.Content[x], v.Content[x+1] // next node is what we need. return v, v.Content[x], v.Content[x+1]
} }
if IsNodeArray(v) { if IsNodeArray(v) {
return v, v.Content[x], v.Content[x] return v, v.Content[x], v.Content[x]
@@ -356,14 +359,6 @@ func IsNodeIntValue(node *yaml.Node) bool {
return node.Tag == "!!int" return node.Tag == "!!int"
} }
// IsNodeNullValue Checks of the input is null or not.
func IsNodeNullValue(node *yaml.Node) bool {
if node == nil {
return false
}
return node.Tag == "!!null"
}
// IsNodeFloatValue will check is a node is a float value. // IsNodeFloatValue will check is a node is a float value.
func IsNodeFloatValue(node *yaml.Node) bool { func IsNodeFloatValue(node *yaml.Node) bool {
if node == nil { if node == nil {
@@ -539,7 +534,7 @@ func ConvertCase(input string, convert Case) string {
func DetectCase(input string) Case { func DetectCase(input string) Case {
trim := strings.TrimSpace(input) trim := strings.TrimSpace(input)
if trim == "" { if trim == "" {
return -1 return UnknownCase
} }
pascalCase := regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$") pascalCase := regexp.MustCompile("^[A-Z][a-z]+(?:[A-Z][a-z]+)*$")

View File

@@ -30,7 +30,15 @@ func TestRenderCodeSnippet(t *testing.T) {
} }
rendered := RenderCodeSnippet(startNode, code, 1, 3) rendered := RenderCodeSnippet(startNode, code, 1, 3)
assert.Equal(t, "hey\nho\nlet's\n", rendered) assert.Equal(t, "hey\nho\nlet's\n", rendered)
}
func TestRenderCodeSnippet_BelowStart(t *testing.T) {
code := []string{"hey", "ho", "let's", "go!"}
startNode := &yaml.Node{
Line: 0,
}
rendered := RenderCodeSnippet(startNode, code, 1, 3)
assert.Equal(t, "hey\nho\nlet's\n", rendered)
} }
func TestFindNodes(t *testing.T) { func TestFindNodes(t *testing.T) {
@@ -221,6 +229,29 @@ func TestFindFirstKeyNode_NotFound(t *testing.T) {
assert.Nil(t, value) assert.Nil(t, value)
} }
func TestFindFirstKeyNode_TooDeep(t *testing.T) {
a, b := FindFirstKeyNode("", nil, 900)
assert.Nil(t, a)
assert.Nil(t, b)
}
func TestFindFirstKeyNode_ValueIsKey(t *testing.T) {
a := &yaml.Node{
Value: "chicken",
}
b := &yaml.Node{
Value: "nuggets",
Content: []*yaml.Node{a},
}
c, d := FindFirstKeyNode("nuggets", []*yaml.Node{b}, 0)
assert.NotNil(t, c)
assert.NotNil(t, d)
assert.Equal(t, c, d)
}
func TestFindFirstKeyNode_Map(t *testing.T) { func TestFindFirstKeyNode_Map(t *testing.T) {
nodes, _ := FindNodes(getPetstore(), "$") nodes, _ := FindNodes(getPetstore(), "$")
key, value := FindFirstKeyNode("pet", nodes, 0) key, value := FindFirstKeyNode("pet", nodes, 0)
@@ -252,6 +283,140 @@ func TestFindKeyNode(t *testing.T) {
assert.Equal(t, 47, k.Line) assert.Equal(t, 47, k.Line)
} }
func TestFindKeyNode_ValueIsKey(t *testing.T) {
a := &yaml.Node{
Value: "chicken",
}
b := &yaml.Node{
Tag: "!!map",
Value: "nuggets",
Content: []*yaml.Node{a},
}
c, d := FindKeyNode("nuggets", []*yaml.Node{b, a})
assert.Equal(t, "nuggets", c.Value)
assert.Equal(t, "chicken", d.Value)
e := &yaml.Node{
Value: "pizza",
}
f := &yaml.Node{
Value: "pie",
}
b.Content = append(b.Content, e, f)
c, d = FindKeyNode("pie", []*yaml.Node{b, a})
assert.Equal(t, "nuggets", c.Value)
assert.Equal(t, "pie", d.Value)
b.Tag = "!!seq"
c, d = FindKeyNode("pie", []*yaml.Node{b, a})
assert.Equal(t, "nuggets", c.Value)
assert.Equal(t, "pie", d.Value)
}
func TestFindExtensionNodes(t *testing.T) {
a := &yaml.Node{
Value: "x-coffee",
}
b := &yaml.Node{
Value: "required",
}
c := &yaml.Node{
Content: []*yaml.Node{a, b},
}
exts := FindExtensionNodes(c.Content)
assert.Len(t, exts, 1)
assert.Equal(t, "required", exts[0].Value.Value)
}
func TestFindKeyNodeFull(t *testing.T) {
a := &yaml.Node{
Value: "fish",
}
b := &yaml.Node{
Value: "paste",
}
c, d, e := FindKeyNodeFull("fish", []*yaml.Node{a, b})
assert.Equal(t, "fish", c.Value)
assert.Equal(t, "fish", d.Value)
assert.Equal(t, "paste", e.Value)
}
func TestFindKeyNodeFull_MapValueIsLastNode(t *testing.T) {
f := &yaml.Node{
Value: "cheese",
}
h := &yaml.Node{
Tag: "!!map",
Value: "deserts", // this is invalid btw, but helps with mechanical understanding
Content: []*yaml.Node{f},
}
c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h})
assert.Equal(t, "deserts", c.Value)
assert.Equal(t, "cheese", d.Value)
assert.Equal(t, "cheese", e.Value)
}
func TestFindKeyNodeFull_Map(t *testing.T) {
f := &yaml.Node{
Value: "cheese",
}
g := &yaml.Node{
Value: "cake",
}
h := &yaml.Node{
Tag: "!!map",
Value: "deserts", // this is invalid btw, but helps with mechanical understanding
Content: []*yaml.Node{f, g},
}
c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h})
assert.Equal(t, "deserts", c.Value)
assert.Equal(t, "cheese", d.Value)
assert.Equal(t, "cake", e.Value)
}
func TestFindKeyNodeFull_Array(t *testing.T) {
f := &yaml.Node{
Value: "cheese",
}
g := &yaml.Node{
Value: "cake",
}
h := &yaml.Node{
Tag: "!!seq",
Value: "deserts", // this is invalid btw, but helps with mechanical understanding
Content: []*yaml.Node{f, g},
}
c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{h})
assert.Equal(t, "deserts", c.Value)
assert.Equal(t, "cheese", d.Value)
assert.Equal(t, "cheese", e.Value)
}
func TestFindKeyNodeFull_Nothing(t *testing.T) {
c, d, e := FindKeyNodeFull("cheese", []*yaml.Node{})
assert.Nil(t, c)
assert.Nil(t, d)
assert.Nil(t, e)
}
func TestFindKeyNode_NotFound(t *testing.T) { func TestFindKeyNode_NotFound(t *testing.T) {
nodes, _ := FindNodes(getPetstore(), "$") nodes, _ := FindNodes(getPetstore(), "$")
k, v := FindKeyNode("I am not anything at all", nodes[0].Content) k, v := FindKeyNode("I am not anything at all", nodes[0].Content)
@@ -414,6 +579,12 @@ func TestConvertComponentIdIntoFriendlyPathSearch(t *testing.T) {
assert.Equal(t, "cake", segment) assert.Equal(t, "cake", segment)
} }
func TestConvertComponentIdIntoFriendlyPathSearch_WithRootSymbol(t *testing.T) {
segment, path := ConvertComponentIdIntoFriendlyPathSearch("/chicken/chips/pizza/cake")
assert.Equal(t, "$.chicken.chips.pizza['cake']", path)
assert.Equal(t, "cake", segment)
}
func TestConvertComponentIdIntoPath(t *testing.T) { func TestConvertComponentIdIntoPath(t *testing.T) {
segment, path := ConvertComponentIdIntoPath("#/chicken/chips/pizza/cake") segment, path := ConvertComponentIdIntoPath("#/chicken/chips/pizza/cake")
assert.Equal(t, "$.chicken.chips.pizza.cake", path) assert.Equal(t, "$.chicken.chips.pizza.cake", path)
@@ -439,3 +610,52 @@ func TestConvertCase(t *testing.T) {
assert.Equal(t, "CHICKEN-NUGGETS-CHICKEN-SOUP", ConvertCase(str1, ScreamingKebabCase)) assert.Equal(t, "CHICKEN-NUGGETS-CHICKEN-SOUP", ConvertCase(str1, ScreamingKebabCase))
assert.Equal(t, "CHICKEN_NUGGETS_CHICKEN_SOUP", ConvertCase(str1, ScreamingSnakeCase)) assert.Equal(t, "CHICKEN_NUGGETS_CHICKEN_SOUP", ConvertCase(str1, ScreamingSnakeCase))
} }
func TestConvertCase_NoInput(t *testing.T) {
assert.Empty(t, ConvertCase("", ScreamingKebabCase))
}
func TestDetectCase_NoInput(t *testing.T) {
assert.Equal(t, UnknownCase, DetectCase(""))
}
func TestIsNodeRefValue(t *testing.T) {
f := &yaml.Node{
Value: "$ref",
}
g := &yaml.Node{
Value: "'#/somewhere/out-there'",
}
h := &yaml.Node{
Tag: "!!map",
Content: []*yaml.Node{f, g},
}
ref, node, val := IsNodeRefValue(h)
assert.True(t, ref)
assert.Equal(t, "$ref", node.Value)
assert.Equal(t, "'#/somewhere/out-there'", val)
}
func TestIsNodeRefValue_False(t *testing.T) {
f := &yaml.Node{
Value: "woof",
}
g := &yaml.Node{
Value: "dog",
}
h := &yaml.Node{
Tag: "!!map",
Content: []*yaml.Node{f, g},
}
ref, node, val := IsNodeRefValue(h)
assert.False(t, ref)
assert.Nil(t, node)
assert.Empty(t, val)
}