1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package cue
16
17 import (
18 "fmt"
19 "reflect"
20 "testing"
21 "time"
22
23 "github.com/go-quicktest/qt"
24 "github.com/google/go-cmp/cmp"
25 )
26
27 func TestDecode(t *testing.T) {
28 type Nested struct {
29 P *int `json:"P"`
30 }
31 type fields struct {
32 A int `json:"A"`
33 B int `json:"B"`
34 C int `json:"C"`
35 M map[string]interface{}
36 *Nested
37 }
38 one := 1
39 intList := func(ints ...int) *[]int {
40 ints = append([]int{}, ints...)
41 return &ints
42 }
43 testCases := []struct {
44 value string
45 dst interface{}
46 want interface{}
47 err string
48 }{{
49
50 value: `null`,
51 dst: &[]int{1},
52 want: []int(nil),
53 }, {
54
55 value: `1`,
56 err: "cannot decode into unsettable value",
57 }, {
58 dst: new(interface{}),
59 value: `_|_`,
60 err: "explicit error (_|_ literal) in source",
61 }, {
62
63 value: `null`,
64 dst: &[]int{1},
65 want: []int(nil),
66 }, {
67
68 value: `[null]`,
69 dst: &[]*int{&one},
70 want: []*int{nil},
71 }, {
72 value: `true`,
73 dst: new(bool),
74 want: true,
75 }, {
76 value: `false`,
77 dst: new(bool),
78 want: false,
79 }, {
80 value: `bool`,
81 dst: new(bool),
82 err: "cannot convert non-concrete value bool",
83 }, {
84 value: `_`,
85 dst: new([]int),
86 want: []int(nil),
87 }, {
88 value: `"str"`,
89 dst: new(string),
90 want: "str",
91 }, {
92 value: `"str"`,
93 dst: new(int),
94 err: "cannot use value \"str\" (type string) as int",
95 }, {
96 value: `'bytes'`,
97 dst: new([]byte),
98 want: []byte("bytes"),
99 }, {
100 value: `'bytes'`,
101 dst: &[3]byte{},
102 want: [3]byte{0x62, 0x79, 0x74},
103 }, {
104 value: `1`,
105 dst: new(float32),
106 want: float32(1),
107 }, {
108 value: `500`,
109 dst: new(uint8),
110 err: "integer 500 overflows uint8",
111 }, {
112 value: `501`,
113 dst: new(int8),
114 err: "integer 501 overflows int8",
115 }, {
116 value: `{}`,
117 dst: &fields{},
118 want: fields{},
119 }, {
120 value: `{A:1,b:2,c:3}`,
121 dst: &fields{},
122 want: fields{A: 1, B: 2, C: 3},
123 }, {
124
125 value: `{a:1,m:{a: 3}}`,
126 dst: &fields{},
127 want: fields{A: 1,
128 M: map[string]interface{}{"a": int(3)}},
129 }, {
130
131 value: `{p: 1}`,
132 dst: &fields{},
133 want: fields{Nested: &Nested{P: &one}},
134 }, {
135 value: `{for k, v in y if v > 1 {"\(k)": v} }
136 y: {a:1,b:2,c:3}`,
137 dst: &fields{},
138 want: fields{B: 2, C: 3},
139 }, {
140 value: `{a:1,b:2,c:int}`,
141 dst: new(fields),
142 err: "c: cannot convert non-concrete value int",
143 }, {
144 value: `[]`,
145 dst: intList(),
146 want: *intList(),
147 }, {
148 value: `[1,2,3]`,
149 dst: intList(),
150 want: *intList(1, 2, 3),
151 }, {
152
153 value: `[1,2,3]`,
154 dst: intList(1, 2, 3, 4),
155 want: *intList(1, 2, 3),
156 }, {
157
158 value: `[1,2,3]`,
159 dst: &[2]int{},
160 want: [2]int{1, 2},
161 }, {
162
163 value: `[1,2,3]`,
164 dst: &[4]int{},
165 want: [4]int{1, 2, 3, 0},
166 }, {
167 value: `[for x in #y if x > 1 { x }]
168 #y: [1,2,3]`,
169 dst: intList(),
170 want: *intList(2, 3),
171 }, {
172 value: `[int]`,
173 dst: intList(),
174 err: "0: cannot convert non-concrete value int",
175 }, {
176 value: `{a: 1, b: 2, c: 3}`,
177 dst: &map[string]int{},
178 want: map[string]int{"a": 1, "b": 2, "c": 3},
179 }, {
180 value: `{"1": 1, "-2": 2, "3": 3}`,
181 dst: &map[int]int{},
182 want: map[int]int{1: 1, -2: 2, 3: 3},
183 }, {
184 value: `{"1": 1, "2": 2, "3": 3}`,
185 dst: &map[uint]int{},
186 want: map[uint]int{1: 1, 2: 2, 3: 3},
187 }, {
188 value: `{a: 1, b: 2, c: true, d: e: 2}`,
189 dst: &map[string]interface{}{},
190 want: map[string]interface{}{
191 "a": 1, "b": 2, "c": true,
192 "d": map[string]interface{}{"e": 2}},
193 }, {
194 value: `{a: b: *2 | int}`,
195 dst: &map[string]interface{}{},
196 want: map[string]interface{}{"a": map[string]interface{}{"b": int(2)}},
197 }, {
198 value: `{a: 1, b: 2, c: true}`,
199 dst: &map[string]int{},
200 err: "c: cannot use value true (type bool) as int",
201 }, {
202 value: `{"300": 3}`,
203 dst: &map[int8]int{},
204 err: "key integer 300 overflows int8",
205 }, {
206 value: `{"300": 3}`,
207 dst: &map[uint8]int{},
208 err: "key integer 300 overflows uint8",
209 }, {
210
211 value: `a: b: _ | *[0, ...]`,
212 dst: &map[string]interface{}{},
213 want: map[string]interface{}{
214 "a": map[string]interface{}{
215 "b": []interface{}{int(0)},
216 },
217 },
218 }, {
219
220 value: `{"x": "1s"}
221 `,
222 dst: &S{},
223 want: S{X: Duration{D: 1000000000}},
224 }, {
225
226 value: `{"x": '1s'}
227 `,
228 dst: &S{},
229 want: S{X: Duration{D: 1000000000}},
230 }, {
231
232 value: `{"x": 1}
233 `,
234 dst: &S{},
235 err: "Decode: x: cannot use value 1 (type int) as (string|bytes)",
236 }, {
237 value: `[]`,
238 dst: new(interface{}),
239 want: []interface{}{},
240 }}
241 for _, tc := range testCases {
242 t.Run(tc.value, func(t *testing.T) {
243 err := getInstance(t, tc.value).Value().Decode(tc.dst)
244 checkFatal(t, err, tc.err, "init")
245
246 got := reflect.ValueOf(tc.dst).Elem().Interface()
247 if diff := cmp.Diff(got, tc.want); diff != "" {
248 t.Error(diff)
249 t.Errorf("\n%#v\n%#v", got, tc.want)
250 }
251 })
252 }
253 }
254
255 func TestDecodeIntoCUEValue(t *testing.T) {
256
257
258
259
260 var st struct {
261 X Value `json:"x"`
262 }
263 err := getInstance(t, `x: string`).Value().Decode(&st)
264 qt.Assert(t, qt.IsNil(err))
265 qt.Assert(t, qt.Equals(fmt.Sprint(st.X), "string"))
266
267
268 var v Value
269 err = getInstance(t, `int`).Value().Decode(&v)
270 qt.Assert(t, qt.IsNil(err))
271 qt.Assert(t, qt.Equals(fmt.Sprint(v), "int"))
272 }
273
274 type Duration struct {
275 D time.Duration
276 }
277 type S struct {
278 X Duration `json:"x"`
279 }
280
281 func (d *Duration) UnmarshalText(data []byte) error {
282 v, err := time.ParseDuration(string(data))
283 if err != nil {
284 return err
285 }
286 d.D = v
287 return nil
288 }
289
290 func (d *Duration) MarshalText() ([]byte, error) {
291 return []byte(d.D.String()), nil
292 }
293
View as plain text