1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package gocodec
16
17 import (
18 "fmt"
19 "reflect"
20 "testing"
21
22 "github.com/google/go-cmp/cmp"
23
24 "cuelang.org/go/cue"
25 "cuelang.org/go/cue/cuecontext"
26 )
27
28 type Sum struct {
29 A int `cue:"C-B" json:",omitempty"`
30 B int `cue:"C-A" json:",omitempty"`
31 C int `cue:"A+B & >=5" json:",omitempty"`
32 }
33
34 func checkErr(t *testing.T, got error, want string) {
35 t.Helper()
36 if (got == nil) != (want == "") {
37 t.Errorf("error: got %v; want %v", got, want)
38 }
39 }
40 func TestValidate(t *testing.T) {
41 fail := "some error"
42 testCases := []struct {
43 name string
44 value interface{}
45 constraints string
46 err string
47 }{{
48 name: "*Sum: nil disallowed by constraint",
49 value: (*Sum)(nil),
50 constraints: "!=null",
51 err: fail,
52 }, {
53 name: "Sum",
54 value: Sum{A: 1, B: 4, C: 5},
55 }, {
56 name: "*Sum",
57 value: &Sum{A: 1, B: 4, C: 5},
58 }, {
59 name: "*Sum: incorrect sum",
60 value: &Sum{A: 1, B: 4, C: 6},
61 err: fail,
62 }, {
63 name: "*Sum: field C is too low",
64 value: &Sum{A: 1, B: 3, C: 4},
65 err: fail,
66 }, {
67 name: "*Sum: nil value",
68 value: (*Sum)(nil),
69 }, {
70
71 name: "string list",
72 value: []string{"a", "b", "c"},
73 constraints: `[_, "b", ...]`,
74 }, {
75
76 name: "string list incompatible lengths",
77 value: []string{"a", "b", "c"},
78 constraints: `4*[string]`,
79 err: fail,
80 }}
81
82 for _, tc := range testCases {
83 t.Run(tc.name, func(t *testing.T) {
84 ctx := cuecontext.New()
85 codec := New(ctx, nil)
86
87 v, err := codec.ExtractType(tc.value)
88 if err != nil {
89 t.Fatal(err)
90 }
91
92 if tc.constraints != "" {
93 v1 := ctx.CompileString(tc.constraints, cue.Filename(tc.name))
94 if err := v1.Err(); err != nil {
95 t.Fatal(err)
96 }
97 v = v.Unify(v1)
98 }
99
100 err = codec.Validate(v, tc.value)
101 checkErr(t, err, tc.err)
102
103
104 r := &cue.Runtime{}
105 codec = New(r, nil)
106 if _, err := codec.ExtractType(tc.value); err != nil {
107 t.Fatal(err)
108 }
109 })
110 }
111 }
112
113 func TestComplete(t *testing.T) {
114 type updated struct {
115 A []*int `cue:"[...int|*1]"`
116 B []int `cue:"3*[int|*1]"`
117
118
119 M map[string]int `cue:",opt"`
120 }
121 type sump struct {
122 A *int `cue:"C-B"`
123 B *int `cue:"C-A"`
124 C *int `cue:"A+B"`
125 }
126 one := 1
127 two := 2
128 fail := "some error"
129 _ = fail
130 _ = one
131 testCases := []struct {
132 name string
133 value interface{}
134 result interface{}
135 constraints string
136 err string
137 }{{
138 name: "*Sum",
139 value: &Sum{A: 1, B: 4, C: 5},
140 result: &Sum{A: 1, B: 4, C: 5},
141 }, {
142 name: "*Sum",
143 value: &Sum{A: 1, B: 4},
144 result: &Sum{A: 1, B: 4, C: 5},
145 }, {
146 name: "*sump",
147 value: &sump{A: &one, B: &one},
148 result: &sump{A: &one, B: &one, C: &two},
149 }, {
150 name: "*Sum: backwards",
151 value: &Sum{B: 4, C: 8},
152 result: &Sum{A: 4, B: 4, C: 8},
153 }, {
154 name: "*Sum: sum too low",
155 value: &Sum{A: 1, B: 3},
156 result: &Sum{A: 1, B: 3},
157 err: fail,
158 }, {
159 name: "*Sum: sum underspecified",
160 value: &Sum{A: 1},
161 result: &Sum{A: 1},
162 err: fail,
163 }, {
164 name: "Sum: cannot modify",
165 value: Sum{A: 3, B: 4, C: 7},
166 result: Sum{A: 3, B: 4, C: 7},
167 err: fail,
168 }, {
169 name: "*Sum: cannot update nil value",
170 value: (*Sum)(nil),
171 result: (*Sum)(nil),
172 err: fail,
173 }, {
174 name: "cannot modify slice",
175 value: []string{"a", "b", "c"},
176 result: []string{"a", "b", "c"},
177 err: fail,
178 }, {
179 name: "composite values update",
180
181
182 value: &updated{A: make([]*int, 3)},
183 result: &updated{
184 A: []*int{&one, &one, &one},
185 B: []int{1, 1, 1},
186 M: map[string]int(nil),
187 },
188 }, {
189 name: "composite values update with unsatisfied map constraints",
190 value: &updated{},
191 result: &updated{},
192
193 constraints: ` { M: {foo: bar, bar: foo} } `,
194 err: fail,
195 }, {
196 name: "composite values update with map constraints",
197 value: &updated{M: map[string]int{"foo": 1}},
198 constraints: ` { M: {foo: bar, bar: foo} } `,
199 result: &updated{
200
201
202
203
204 A: []*int{},
205 B: []int{1, 1, 1},
206 M: map[string]int{"bar": 1, "foo": 1},
207 },
208 }}
209 for _, tc := range testCases {
210 t.Run(tc.name, func(t *testing.T) {
211 r := &cue.Runtime{}
212 codec := New(r, nil)
213
214 v, err := codec.ExtractType(tc.value)
215 if err != nil {
216 t.Fatal(err)
217 }
218
219 if tc.constraints != "" {
220 inst, err := r.Compile(tc.name, tc.constraints)
221 if err != nil {
222 t.Fatal(err)
223 }
224 v = v.Unify(inst.Value())
225 }
226
227 err = codec.Complete(v, tc.value)
228 checkErr(t, err, tc.err)
229 if diff := cmp.Diff(tc.value, tc.result); diff != "" {
230 t.Error(diff)
231 }
232 })
233 }
234 }
235
236 func TestEncode(t *testing.T) {
237 testCases := []struct {
238 in string
239 dst interface{}
240 want interface{}
241 }{{
242 in: "4",
243 dst: new(int),
244 want: 4,
245 }}
246 r := &cue.Runtime{}
247 c := New(r, nil)
248
249 for _, tc := range testCases {
250 t.Run("", func(t *testing.T) {
251 inst, err := r.Compile("test", tc.in)
252 if err != nil {
253 t.Fatal(err)
254 }
255
256 err = c.Encode(inst.Value(), tc.dst)
257 if err != nil {
258 t.Fatal(err)
259 }
260
261 got := reflect.ValueOf(tc.dst).Elem().Interface()
262 if diff := cmp.Diff(got, tc.want); diff != "" {
263 t.Error(diff)
264 }
265 })
266 }
267 }
268
269 func TestDecode(t *testing.T) {
270 testCases := []struct {
271 in interface{}
272 want string
273 }{{
274 in: "str",
275 want: `"str"`,
276 }, {
277 in: func() interface{} {
278 type T struct {
279 B int
280 }
281 type S struct {
282 A string
283 T
284 }
285 return S{}
286 }(),
287 want: `{
288 A: ""
289 B: 0
290 }`,
291 }, {
292 in: func() interface{} {
293 type T struct {
294 B int
295 }
296 type S struct {
297 A string
298 T `json:"t"`
299 }
300 return S{}
301 }(),
302 want: `{
303 A: ""
304 t: {
305 B: 0
306 }
307 }`,
308 }}
309 c := New(&cue.Runtime{}, nil)
310
311 for _, tc := range testCases {
312 t.Run("", func(t *testing.T) {
313 v, err := c.Decode(tc.in)
314 if err != nil {
315 t.Fatal(err)
316 }
317
318 got := fmt.Sprint(v)
319 if got != tc.want {
320 t.Errorf("got %v; want %v", got, tc.want)
321 }
322 })
323 }
324 }
325
326
327 func TestX(t *testing.T) {
328 t.Skip()
329
330 fail := "some error"
331
332 var (
333 name = "string list incompatible lengths"
334 value = []string{"a", "b", "c"}
335 constraints = `4*[string]`
336 wantErr = fail
337 )
338
339 r := &cue.Runtime{}
340 codec := New(r, nil)
341
342 v, err := codec.ExtractType(value)
343 if err != nil {
344 t.Fatal(err)
345 }
346
347 if constraints != "" {
348 inst, err := r.Compile(name, constraints)
349 if err != nil {
350 t.Fatal(err)
351 }
352 w := inst.Value()
353 v = v.Unify(w)
354 }
355
356 err = codec.Validate(v, value)
357 checkErr(t, err, wantErr)
358 }
359
View as plain text