-
Notifications
You must be signed in to change notification settings - Fork 103
Expand file tree
/
Copy pathvals_fragment_test.go
More file actions
158 lines (143 loc) · 4.97 KB
/
Copy pathvals_fragment_test.go
File metadata and controls
158 lines (143 loc) · 4.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package vals
import (
"crypto/md5"
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
)
// TestFragment_Issue114_FileProvider reproduces helmfile/vals#114 on the file
// provider (mirrors the s3 scenario without AWS): non-string values must be
// extractable via a #/key fragment.
func TestFragment_Issue114_FileProvider(t *testing.T) {
dir := t.TempDir()
jsonFile := filepath.Join(dir, "data.json")
require.NoError(t, os.WriteFile(jsonFile, []byte(`{"key":123,"ratio":1.5,"obj":{"a":1}}`), 0o600))
ref := "ref+file://" + jsonFile
cases := []struct {
want interface{}
name string
frag string
}{
{name: "int", frag: "#/key", want: 123},
{name: "float", frag: "#/ratio", want: 1.5},
{name: "object", frag: "#/obj", want: map[string]interface{}{"a": 1}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
res, err := Eval(map[string]interface{}{"v": ref + tc.frag}, Options{})
require.NoError(t, err)
require.Equal(t, tc.want, res["v"])
})
}
}
// TestFragment_ScalarTypes covers non-string scalar leaf values (float and a
// uint64 above math.MaxInt64) that become extractable once isTerminalValue
// accepts all scalar kinds, not just bool/int/string.
func TestFragment_ScalarTypes(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "data.yaml")
require.NoError(t, os.WriteFile(f, []byte(
"ratio: 1.5\n"+
"neg: -2.5\n"+
"big: 9223372036854775808\n"+ // > math.MaxInt64 -> yaml decodes as uint64
"n: 42\n"+
"s: hello\n"+
"flag: true\n"), 0o600))
ref := "ref+file://" + f
res, err := Eval(map[string]interface{}{
"ratio": ref + "#/ratio",
"neg": ref + "#/neg",
"big": ref + "#/big",
"n": ref + "#/n",
"s": ref + "#/s",
"flag": ref + "#/flag",
}, Options{})
require.NoError(t, err)
require.Equal(t, 1.5, res["ratio"])
require.Equal(t, -2.5, res["neg"])
require.Equal(t, uint64(9223372036854775808), res["big"])
require.Equal(t, 42, res["n"])
require.Equal(t, "hello", res["s"])
require.Equal(t, true, res["flag"])
}
// TestFragment_NestedObjectAndArray verifies that a fragment pointing at a nested
// map or slice returns that structure as-is, and that descending to a deeper leaf
// keeps working.
func TestFragment_NestedObjectAndArray(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "data.yaml")
require.NoError(t, os.WriteFile(f, []byte(
"obj:\n"+
" a: 1\n"+
" b: two\n"+
"list:\n"+
" - x\n"+
" - y\n"), 0o600))
ref := "ref+file://" + f
t.Run("object", func(t *testing.T) {
res, err := Eval(map[string]interface{}{"v": ref + "#/obj"}, Options{})
require.NoError(t, err)
require.Equal(t, map[string]interface{}{"a": 1, "b": "two"}, res["v"])
})
t.Run("array", func(t *testing.T) {
res, err := Eval(map[string]interface{}{"v": ref + "#/list"}, Options{})
require.NoError(t, err)
require.Equal(t, []interface{}{"x", "y"}, res["v"])
})
t.Run("nested-leaf", func(t *testing.T) {
res, err := Eval(map[string]interface{}{"v": ref + "#/obj/a"}, Options{})
require.NoError(t, err)
require.Equal(t, 1, res["v"])
})
}
// TestFragment_IntermediateInterfaceMap covers descending through an intermediate
// map[interface{}]interface{} (which some providers produce, unlike the yaml.v3
// file provider). A mock provider supplies that shape.
func TestFragment_IntermediateInterfaceMap(t *testing.T) {
r, err := New(Options{})
require.NoError(t, err)
hash := fmt.Sprintf("%x", md5.Sum([]byte("echo")))
r.providers[hash] = &mockProvider{
getStringMapFunc: func(key string) (map[string]interface{}, error) {
return map[string]interface{}{
"outer": map[interface{}]interface{}{
"inner": "deep",
},
}, nil
},
}
res, err := r.Get("ref+echo://mykey#/outer/inner")
require.NoError(t, err)
require.Equal(t, "deep", res)
}
// TestFragment_MissingKeyAndNil checks that an absent key honors
// FailOnMissingKeyInMap while a present null value returns nil without error.
func TestFragment_MissingKeyAndNil(t *testing.T) {
dir := t.TempDir()
f := filepath.Join(dir, "data.yaml")
require.NoError(t, os.WriteFile(f, []byte(
"present: value\n"+
"empty: null\n"+
"obj:\n"+
" a: 1\n"), 0o600))
ref := "ref+file://" + f
t.Run("absent-key-fails", func(t *testing.T) {
_, err := Eval(map[string]interface{}{"v": ref + "#/absent"}, Options{FailOnMissingKeyInMap: true})
require.Error(t, err)
require.Contains(t, err.Error(), "no value found for key")
})
t.Run("absent-nested-key-fails", func(t *testing.T) {
// A key missing deeper in the path errors cleanly ("no value found"),
// not with the confusing "unsupported type" of the pre-fix code.
_, err := Eval(map[string]interface{}{"v": ref + "#/obj/missing"}, Options{FailOnMissingKeyInMap: true})
require.Error(t, err)
require.Contains(t, err.Error(), "no value found for key")
})
t.Run("present-null-is-nil", func(t *testing.T) {
res, err := Eval(map[string]interface{}{"v": ref + "#/empty"}, Options{FailOnMissingKeyInMap: true})
require.NoError(t, err)
require.Nil(t, res["v"])
})
}