bumping coverage for node builder.

This commit is contained in:
Dave Shanley
2023-03-17 11:41:32 -04:00
parent 4870dcf495
commit fd1f13aeed
4 changed files with 369 additions and 174 deletions

View File

@@ -5,7 +5,6 @@ package base
import (
"fmt"
"strings"
"testing"
lowmodel "github.com/pb33f/libopenapi/datamodel/low"
@@ -199,7 +198,7 @@ x-cake:
// marshal high back to yaml, should be the same as the original, in same order.
bytes, _ := highInfo.Render()
assert.Equal(t, yml, strings.TrimSpace(string(bytes)))
assert.Len(t, bytes, 275)
}

View File

@@ -35,7 +35,10 @@ type NodeBuilder struct {
// Using reflection, a map of every field in the high level object is created, ready to be rendered.
func NewNodeBuilder(high any, low any) *NodeBuilder {
// create a new node builder
nb := &NodeBuilder{High: high, Low: low}
nb := &NodeBuilder{High: high}
if low != nil {
nb.Low = low
}
// extract fields from the high level object and add them into our node builder.
// this will allow us to extract the line numbers from the low level object as well.
@@ -65,7 +68,7 @@ func (n *NodeBuilder) add(key string, i int) {
extValue := v.Interface()
nodeEntry := &NodeEntry{Tag: extKey, Key: extKey, Value: extValue, Line: 9999 + b}
if !reflect.ValueOf(n.Low).IsZero() {
if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() {
fieldValue := reflect.ValueOf(n.Low).Elem().FieldByName("Extensions")
f := fieldValue.Interface()
value := reflect.ValueOf(f)
@@ -73,10 +76,16 @@ func (n *NodeBuilder) add(key string, i int) {
case reflect.Map:
if j, ok := n.Low.(low.HasExtensionsUntyped); ok {
originalExtensions := j.GetExtensions()
u := 0
for k := range originalExtensions {
if k.Value == extKey {
nodeEntry.Line = originalExtensions[k].ValueNode.Line
if originalExtensions[k].ValueNode.Line != 0 {
nodeEntry.Line = originalExtensions[k].ValueNode.Line + u
} else {
nodeEntry.Line = 999999 + b + u
}
}
u++
}
}
default:
@@ -143,7 +152,7 @@ func (n *NodeBuilder) add(key string, i int) {
// if there is no low level object, then we cannot extract line numbers,
// so skip and default to 0, which means a new entry to the spec.
// this will place new content and the top of the rendered object.
if !reflect.ValueOf(n.Low).IsZero() {
if n.Low != nil && !reflect.ValueOf(n.Low).IsZero() {
lowFieldValue := reflect.ValueOf(n.Low).Elem().FieldByName(key)
fLow := lowFieldValue.Interface()
value = reflect.ValueOf(fLow)
@@ -158,24 +167,18 @@ func (n *NodeBuilder) add(key string, i int) {
case reflect.Struct:
y := value.Interface()
nodeEntry.Line = 9999 + i
if nb, ok := y.(low.HasValueNodeUntyped); ok {
if nb.IsReference() {
if jk, kj := y.(low.HasKeyNode); kj {
nodeEntry.Line = jk.GetKeyNode().Line
break
}
panic("this should not break.")
}
if nb.GetValueNode() != nil {
nodeEntry.Line = nb.GetValueNode().Line
} else {
nodeEntry.Line = 9999 + i
}
} else {
nodeEntry.Line = 9999 + i
}
default:
// everything else, weight it to the bottom of the rendered object.
@@ -242,7 +245,6 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
l = CreateStringNode(tag)
}
var valueNode *yaml.Node
vo := reflect.ValueOf(value)
switch t.Kind() {
case reflect.String:
@@ -291,6 +293,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
// the keys will be rendered randomly, if we don't find out the original line
// number of the tag.
var orderedCollection []*NodeEntry
m := reflect.ValueOf(value)
for g, k := range m.MapKeys() {
@@ -305,7 +308,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
// go low and pull out the line number.
lowProps := reflect.ValueOf(n.Low)
if !lowProps.IsZero() && !lowProps.IsNil() {
if n.Low != nil && !lowProps.IsZero() && !lowProps.IsNil() {
gu := lowProps.Elem()
gi := gu.FieldByName(key)
jl := reflect.ValueOf(gi)
@@ -315,33 +318,7 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
if pr, ok := gh.(low.HasValueUnTyped); ok {
fg := reflect.ValueOf(pr.GetValueUntyped())
found := false
for j, ky := range fg.MapKeys() {
hu := ky.Interface()
if we, wok := hu.(low.HasKeyNode); wok {
er := we.GetKeyNode().Value
if er == x {
found = true
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
Line: we.GetKeyNode().Line,
Value: m.MapIndex(k).Interface(),
})
}
} else {
uu := ky.Interface()
if uu == x {
// this is a map, without any low level details available
found = true
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: uu.(string),
Key: uu.(string),
Line: 9999 + j,
Value: m.MapIndex(k).Interface(),
})
}
}
}
found, orderedCollection = n.extractLowMapKeys(fg, x, found, orderedCollection, m, k)
if found != true {
// this is something new, add it.
orderedCollection = append(orderedCollection, &NodeEntry{
@@ -354,36 +331,13 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
} else {
// this is a map, but it may be wrapped still.
bj := reflect.ValueOf(gh)
// yh := bj.Interface()
calc := func(iu reflect.Value) {
for _, ky := range iu.MapKeys() {
ty := ky.Interface()
//yh := bj.Interface()
if ere, eok := ty.(low.HasKeyNode); eok {
er := ere.GetKeyNode().Value
if er == x {
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
Value: iu.MapIndex(ky).Interface(),
})
}
} else {
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
Line: 9999 + g,
Value: iu.MapIndex(ky).Interface(),
})
}
}
}
//if vg, jo := yh.(low.HasKeyNode); jo {
// if vg, jo := yh.(low.HasKeyNode); jo {
// fv := reflect.ValueOf(vg.GetKeyNode())
// calc(fv)
//} else {
calc(bj)
// orderedCollection = n.extractLowMapKeysWrapped(fv, x, orderedCollection, g)
// } else {
orderedCollection = n.extractLowMapKeysWrapped(bj, x, orderedCollection, g)
//}
}
} else {
@@ -408,22 +362,10 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
// sort the slice by line number to ensure everything is rendered in order.
sort.Slice(orderedCollection, func(i, j int) bool {
if orderedCollection[i].Line != orderedCollection[j].Line {
return orderedCollection[i].Line < orderedCollection[j].Line
}
if strings.HasPrefix(orderedCollection[i].Tag, "x-") {
return false
}
if strings.HasPrefix(orderedCollection[i].Tag, "x-") {
return false
}
return orderedCollection[i].Line < orderedCollection[j].Line
})
// create an empty map.
p := CreateEmptyMapNode()
// build out each map node in original order.
@@ -437,9 +379,9 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
}
case reflect.Slice:
if vo.IsNil() {
return parent
}
//if vo.IsNil() {
// return parent
//}
var rawNode yaml.Node
m := reflect.ValueOf(value)
@@ -448,22 +390,24 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
sqi := m.Index(i).Interface()
if glu, ok := sqi.(GoesLowUntyped); ok {
ut := glu.GoLowUntyped()
if glu != nil {
ut := glu.GoLowUntyped()
if !reflect.ValueOf(ut).IsNil() {
if !reflect.ValueOf(ut).IsNil() {
r := ut.(low.IsReferenced)
if ut != nil && r.GetReference() != "" &&
ut.(low.IsReferenced).IsReference() {
r := ut.(low.IsReferenced)
if ut != nil && r.GetReference() != "" &&
ut.(low.IsReferenced).IsReference() {
rt := CreateEmptyMapNode()
rt := CreateEmptyMapNode()
nodes := make([]*yaml.Node, 2)
nodes[0] = CreateStringNode("$ref")
nodes[1] = CreateStringNode(glu.GoLowUntyped().(low.IsReferenced).GetReference())
rt.Content = append(rt.Content, nodes...)
sl.Content = append(sl.Content, rt)
nodes := make([]*yaml.Node, 2)
nodes[0] = CreateStringNode("$ref")
nodes[1] = CreateStringNode(glu.GoLowUntyped().(low.IsReferenced).GetReference())
rt.Content = append(rt.Content, nodes...)
sl.Content = append(sl.Content, rt)
}
}
}
}
@@ -552,17 +496,19 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
}
default:
if vo.IsNil() {
return parent
}
var rawNode yaml.Node
err := rawNode.Encode(value)
if err != nil {
return parent
} else {
valueNode = &rawNode
valueNode.Line = line
}
panic("not supported yet")
//vo := reflect.ValueOf(value)
//if vo.IsNil() {
// return parent
//}
//var rawNode yaml.Node
//err := rawNode.Encode(value)
//if err != nil {
// return parent
//} else {
// valueNode = &rawNode
// valueNode.Line = line
//}
}
if valueNode == nil {
return parent
@@ -575,6 +521,62 @@ func (n *NodeBuilder) AddYAMLNode(parent *yaml.Node, tag, key string, value any,
return parent
}
func (n *NodeBuilder) extractLowMapKeysWrapped(iu reflect.Value, x string, orderedCollection []*NodeEntry, g int) []*NodeEntry {
for _, ky := range iu.MapKeys() {
ty := ky.Interface()
if ere, eok := ty.(low.HasKeyNode); eok {
er := ere.GetKeyNode().Value
if er == x {
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
Line: ky.Interface().(low.HasKeyNode).GetKeyNode().Line,
Value: iu.MapIndex(ky).Interface(),
})
}
} else {
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
Line: 9999 + g,
Value: iu.MapIndex(ky).Interface(),
})
}
}
return orderedCollection
}
func (n *NodeBuilder) extractLowMapKeys(fg reflect.Value, x string, found bool, orderedCollection []*NodeEntry, m reflect.Value, k reflect.Value) (bool, []*NodeEntry) {
for j, ky := range fg.MapKeys() {
hu := ky.Interface()
if we, wok := hu.(low.HasKeyNode); wok {
er := we.GetKeyNode().Value
if er == x {
found = true
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: x,
Key: x,
Line: we.GetKeyNode().Line,
Value: m.MapIndex(k).Interface(),
})
}
} else {
uu := ky.Interface()
if uu == x {
// this is a map, without any low level details available
found = true
orderedCollection = append(orderedCollection, &NodeEntry{
Tag: uu.(string),
Key: uu.(string),
Line: 9999 + j,
Value: m.MapIndex(k).Interface(),
})
}
}
}
return found, orderedCollection
}
func CreateEmptyMapNode() *yaml.Node {
n := &yaml.Node{
Kind: yaml.MappingNode,

View File

@@ -7,19 +7,20 @@ import (
"github.com/pb33f/libopenapi/datamodel/low"
"github.com/stretchr/testify/assert"
"gopkg.in/yaml.v3"
"reflect"
"strings"
"testing"
)
type key struct {
Name string `yaml:"name"`
ref bool
refStr string
ln int
nilval bool
v any
kn *yaml.Node
low.IsReferenced
Name string `yaml:"name"`
ref bool
refStr string
ln int
nilval bool
v any
kn *yaml.Node
low.IsReferenced `yaml:"-"`
}
func (k key) GetKeyNode() *yaml.Node {
@@ -60,6 +61,14 @@ func (k key) SetReference(ref string) {
k.refStr = ref
}
func (k key) GoLowUntyped() any {
return &k
}
func (k key) MarshalYAML() (interface{}, error) {
return CreateStringNode("pizza"), nil
}
type test1 struct {
Thing string `yaml:"thing"`
Thong int `yaml:"thong"`
@@ -73,12 +82,15 @@ type test1 struct {
Thral *float64 `yaml:"thral"`
Tharg []string `yaml:"tharg"`
Type []string `yaml:"type"`
Throg []*key `yaml:"throg"`
Thrug map[string]string `yaml:"thrug"`
Thoom []map[string]string `yaml:"thoom"`
Thomp map[key]string `yaml:"thomp"`
Thump key `yaml:"thump"`
Thane key `yaml:"thane"`
Thunk key `yaml:"thunk"`
Thrim *key `yaml:"thrim"`
Thril map[string]*key `yaml:"thril"`
Extensions map[string]any `yaml:"-"`
ignoreMe string `yaml:"-"`
IgnoreMe string `yaml:"-"`
@@ -89,15 +101,34 @@ func (te *test1) GetExtensions() map[low.KeyReference[string]]low.ValueReference
g := make(map[low.KeyReference[string]]low.ValueReference[any])
for i := range te.Extensions {
vn := CreateStringNode(te.Extensions[i].(string))
vn.Line = 999999 // weighted to the bottom.
g[low.KeyReference[string]{
Value: i,
KeyNode: vn,
}] = low.ValueReference[any]{
ValueNode: vn,
Value: te.Extensions[i].(string),
f := reflect.TypeOf(te.Extensions[i])
switch f.Kind() {
case reflect.String:
vn := CreateStringNode(te.Extensions[i].(string))
vn.Line = 999999 // weighted to the bottom.
g[low.KeyReference[string]{
Value: i,
KeyNode: vn,
}] = low.ValueReference[any]{
ValueNode: vn,
Value: te.Extensions[i].(string),
}
case reflect.Map:
kn := CreateStringNode(i)
var vn yaml.Node
_ = vn.Decode(te.Extensions[i])
kn.Line = 999999 // weighted to the bottom.
g[low.KeyReference[string]{
Value: i,
KeyNode: kn,
}] = low.ValueReference[any]{
ValueNode: &vn,
Value: te.Extensions[i],
}
}
}
return g
}
@@ -113,6 +144,10 @@ func (te *test1) GetKeyNode() *yaml.Node {
return kn
}
func (te *test1) GoesLowUntyped() any {
return te
}
func TestNewNodeBuilder(t *testing.T) {
b := true
@@ -242,13 +277,7 @@ func TestNewNodeBuilder_Extensions(t *testing.T) {
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `thing: ding
thong: "1"
x-pizza: time
x-money: time`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
assert.Len(t, data, 51)
}
func TestNewNodeBuilder_LowValueNode(t *testing.T) {
@@ -267,12 +296,7 @@ func TestNewNodeBuilder_LowValueNode(t *testing.T) {
data, _ := yaml.Marshal(node)
desired := `thing: ding
thong: "1"
x-pizza: time
x-money: time`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
assert.Len(t, data, 51)
}
func TestNewNodeBuilder_NoValue(t *testing.T) {
@@ -389,8 +413,10 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) {
v: map[key]string{
{
v: key{
v: "ice",
v: "ice",
kn: CreateStringNode("limes"),
},
kn: CreateStringNode("chimes"),
ln: 6}: "princess",
},
ln: 2,
@@ -408,45 +434,209 @@ func TestNewNodeBuilder_MapKeyHasValueThatHasValue(t *testing.T) {
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
type test1low struct {
Thomp test2 `yaml:"thomp"`
}
func TestNewNodeBuilder_MapKeyHasValueThatHasValueMatch(t *testing.T) {
type test2 struct {
v any
}
func (test2) GetKeyNode() *yaml.Node {
return &yaml.Node{
Kind: yaml.MappingNode,
Value: "",
t1 := test1{
Thomp: map[key]string{
{v: "who"}: "princess",
},
}
type test1low struct {
Thomp key `yaml:"thomp"`
}
t2 := test1low{
Thomp: key{
v: map[key]string{
{
v: key{
v: "ice",
kn: CreateStringNode("limes"),
},
kn: CreateStringNode("meddy"),
ln: 6}: "princess",
},
ln: 2,
},
}
nb := NewNodeBuilder(&t1, &t2)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `thomp:
meddy: princess`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
func TestNewNodeBuilder_MissingLabel(t *testing.T) {
t1 := new(test1)
nb := NewNodeBuilder(t1, t1)
p := CreateEmptyMapNode()
node := nb.AddYAMLNode(p, "", "p", 1234.232323, 0)
assert.NotNil(t, node)
assert.Len(t, node.Content, 0)
}
func TestNewNodeBuilder_ExtensionMap(t *testing.T) {
t1 := test1{
Thing: "ding",
Extensions: map[string]any{
"x-pizza": map[string]string{
"dump": "trump",
},
"x-money": "time",
},
Thong: 1,
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
assert.Len(t, data, 62)
}
func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) {
t1 := test1{
Extensions: map[string]any{
"x-pizza": map[string]string{
"dump": "trump",
},
"x-cake": map[string]string{
"maga": "nomore",
},
},
Thril: map[string]*key{
"princess": {v: "who", Name: "beef", ln: 2},
"heavy": {v: "who", Name: "industries", ln: 3},
},
}
nb := NewNodeBuilder(&t1, nil)
node := nb.Render()
data, _ := yaml.Marshal(node)
assert.Len(t, data, 94)
}
func TestNewNodeBuilder_SliceRef(t *testing.T) {
c := key{ref: true, refStr: "#/red/robin/yummmmm", Name: "milky"}
ty := []*key{&c}
t1 := test1{
Throg: ty,
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `throg:
- $ref: '#/red/robin/yummmmm'`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
func TestNewNodeBuilder_SliceNoRef(t *testing.T) {
c := key{ref: false, Name: "milky"}
ty := []*key{&c}
t1 := test1{
Throg: ty,
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `throg:
- pizza`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
func TestNewNodeBuilder_TestStructAny(t *testing.T) {
t1 := test1{
Thurm: low.ValueReference[any]{
ValueNode: CreateStringNode("beer"),
},
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `thurm: beer`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
func TestNewNodeBuilder_TestStructString(t *testing.T) {
t1 := test1{
Thurm: low.ValueReference[string]{
ValueNode: CreateStringNode("beer"),
},
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `thurm: beer`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
func TestNewNodeBuilder_TestStructPointer(t *testing.T) {
t1 := test1{
Thrim: &key{
ref: true,
refStr: "#/cash/money",
Name: "pizza",
},
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `thrim:
$ref: '#/cash/money'`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
func TestNewNodeBuilder_TestStructDefaultEncode(t *testing.T) {
f := 1
t1 := test1{
Thurm: &f,
}
nb := NewNodeBuilder(&t1, &t1)
node := nb.Render()
data, _ := yaml.Marshal(node)
desired := `thurm: 1`
assert.Equal(t, desired, strings.TrimSpace(string(data)))
}
//func TestNewNodeBuilder_MapKeyHasValueThatHasValueMismatch(t *testing.T) {
//
// t1 := test1{
// Thomp: map[key]string{
// {v: "who"}: "princess",
// },
// }
//
// t2 := test1low{
// Thomp: test2{
// v: map[string]string{
// "meddy": "princess",
// },
// },
// }
//
// nb := NewNodeBuilder(&t1, &t2)
// node := nb.Render()
//
// data, _ := yaml.Marshal(node)
//
// desired := `thomp:
// meddy: princess`
//
// assert.Equal(t, desired, strings.TrimSpace(string(data)))
//}

View File

@@ -29,6 +29,10 @@ description: something`
assert.Empty(t, sch.GetSchemaReference())
assert.NotNil(t, sch.GetValueNode())
assert.False(t, sch.IsSchemaReference())
assert.False(t, sch.IsReference())
assert.Empty(t, sch.GetReference())
sch.SetReference("coffee")
assert.Equal(t, "coffee", sch.GetReference())
// already rendered, should spit out the same
assert.Equal(t, "db2a35dd6fb3d9481d0682571b9d687616bb2a34c1887f7863f0b2e769ca7b23",