cuelang.org/go@v0.10.1/cue/decode_test.go (about) 1 // Copyright 2021 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package cue_test 16 17 import ( 18 "fmt" 19 "reflect" 20 "testing" 21 "time" 22 23 "cuelang.org/go/cue" 24 "cuelang.org/go/internal/cuetdtest" 25 "github.com/go-quicktest/qt" 26 "github.com/google/go-cmp/cmp" 27 ) 28 29 func TestDecode(t *testing.T) { 30 type Nested struct { 31 P *int `json:"P"` 32 } 33 type fields struct { 34 A int `json:"A"` 35 B int `json:"B"` 36 C int `json:"C"` 37 M map[string]interface{} 38 *Nested 39 } 40 one := 1 41 intList := func(ints ...int) *[]int { 42 ints = append([]int{}, ints...) 43 return &ints 44 } 45 testCases := []struct { 46 value string 47 dst interface{} 48 want interface{} 49 err string 50 }{{ 51 // clear pointer 52 value: `null`, 53 dst: &[]int{1}, 54 want: []int(nil), 55 }, { 56 57 value: `1`, 58 err: "cannot decode into unsettable value", 59 }, { 60 dst: new(interface{}), 61 value: `_|_`, 62 err: "explicit error (_|_ literal) in source", 63 }, { 64 // clear pointer 65 value: `null`, 66 dst: &[]int{1}, 67 want: []int(nil), 68 }, { 69 // clear pointer 70 value: `[null]`, 71 dst: &[]*int{&one}, 72 want: []*int{nil}, 73 }, { 74 value: `true`, 75 dst: new(bool), 76 want: true, 77 }, { 78 value: `false`, 79 dst: new(bool), 80 want: false, 81 }, { 82 value: `bool`, 83 dst: new(bool), 84 err: "cannot convert non-concrete value bool", 85 }, { 86 value: `_`, 87 dst: new([]int), 88 want: []int(nil), 89 }, { 90 value: `"str"`, 91 dst: new(string), 92 want: "str", 93 }, { 94 value: `"str"`, 95 dst: new(int), 96 err: "cannot use value \"str\" (type string) as int", 97 }, { 98 value: `'bytes'`, 99 dst: new([]byte), 100 want: []byte("bytes"), 101 }, { 102 value: `'bytes'`, 103 dst: &[3]byte{}, 104 want: [3]byte{0x62, 0x79, 0x74}, 105 }, { 106 value: `1`, 107 dst: new(float32), 108 want: float32(1), 109 }, { 110 value: `500`, 111 dst: new(uint8), 112 err: "integer 500 overflows uint8", 113 }, { 114 value: `501`, 115 dst: new(int8), 116 err: "integer 501 overflows int8", 117 }, { 118 value: `{}`, 119 dst: &fields{}, 120 want: fields{}, 121 }, { 122 value: `{A:1,b:2,c:3}`, 123 dst: &fields{}, 124 want: fields{A: 1, B: 2, C: 3}, 125 }, { 126 // allocate map 127 value: `{a:1,m:{a: 3}}`, 128 dst: &fields{}, 129 want: fields{A: 1, 130 M: map[string]interface{}{"a": int(3)}}, 131 }, { 132 // indirect int 133 value: `{p: 1}`, 134 dst: &fields{}, 135 want: fields{Nested: &Nested{P: &one}}, 136 }, { 137 value: `{for k, v in y if v > 1 {"\(k)": v} } 138 y: {a:1,b:2,c:3}`, 139 dst: &fields{}, 140 want: fields{B: 2, C: 3}, 141 }, { 142 value: `{a:1,b:2,c:int}`, 143 dst: new(fields), 144 err: "c: cannot convert non-concrete value int", 145 }, { 146 value: `[]`, 147 dst: intList(), 148 want: *intList(), 149 }, { 150 value: `[1,2,3]`, 151 dst: intList(), 152 want: *intList(1, 2, 3), 153 }, { 154 // shorten list 155 value: `[1,2,3]`, 156 dst: intList(1, 2, 3, 4), 157 want: *intList(1, 2, 3), 158 }, { 159 // shorter array 160 value: `[1,2,3]`, 161 dst: &[2]int{}, 162 want: [2]int{1, 2}, 163 }, { 164 // longer array 165 value: `[1,2,3]`, 166 dst: &[4]int{}, 167 want: [4]int{1, 2, 3, 0}, 168 }, { 169 value: `[for x in #y if x > 1 { x }] 170 #y: [1,2,3]`, 171 dst: intList(), 172 want: *intList(2, 3), 173 }, { 174 value: `[int]`, 175 dst: intList(), 176 err: "0: cannot convert non-concrete value int", 177 }, { 178 value: `{a: 1, b: 2, c: 3}`, 179 dst: &map[string]int{}, 180 want: map[string]int{"a": 1, "b": 2, "c": 3}, 181 }, { 182 value: `{"1": 1, "-2": 2, "3": 3}`, 183 dst: &map[int]int{}, 184 want: map[int]int{1: 1, -2: 2, 3: 3}, 185 }, { 186 value: `{"1": 1, "2": 2, "3": 3}`, 187 dst: &map[uint]int{}, 188 want: map[uint]int{1: 1, 2: 2, 3: 3}, 189 }, { 190 value: `{a: 1, b: 2, c: true, d: e: 2}`, 191 dst: &map[string]interface{}{}, 192 want: map[string]interface{}{ 193 "a": 1, "b": 2, "c": true, 194 "d": map[string]interface{}{"e": 2}}, 195 }, { 196 value: `{a: b: *2 | int}`, 197 dst: &map[string]interface{}{}, 198 want: map[string]interface{}{"a": map[string]interface{}{"b": int(2)}}, 199 }, { 200 value: `{a: 1, b: 2, c: true}`, 201 dst: &map[string]int{}, 202 err: "c: cannot use value true (type bool) as int", 203 }, { 204 value: `{"300": 3}`, 205 dst: &map[int8]int{}, 206 err: "key integer 300 overflows int8", 207 }, { 208 value: `{"300": 3}`, 209 dst: &map[uint8]int{}, 210 err: "key integer 300 overflows uint8", 211 }, { 212 // Issue #1401 213 value: `a: b: _ | *[0, ...]`, 214 dst: &map[string]interface{}{}, 215 want: map[string]interface{}{ 216 "a": map[string]interface{}{ 217 "b": []interface{}{int(0)}, 218 }, 219 }, 220 }, { 221 // Issue #1466 222 value: `{"x": "1s"} 223 `, 224 dst: &S{}, 225 want: S{X: Duration{D: 1000000000}}, 226 }, { 227 // Issue #1466 228 value: `{"x": '1s'} 229 `, 230 dst: &S{}, 231 want: S{X: Duration{D: 1000000000}}, 232 }, { 233 // Issue #1466 234 value: `{"x": 1} 235 `, 236 dst: &S{}, 237 err: "Decode: x: cannot use value 1 (type int) as (string|bytes)", 238 }, { 239 value: `[]`, 240 dst: new(interface{}), 241 want: []interface{}{}, 242 }} 243 for _, tc := range testCases { 244 cuetdtest.FullMatrix.Run(t, tc.value, func(t *cuetdtest.M) { 245 err := getValue(t, tc.value).Decode(tc.dst) 246 checkFatal(t.T, err, tc.err, "init") 247 248 got := reflect.ValueOf(tc.dst).Elem().Interface() 249 if diff := cmp.Diff(got, tc.want); diff != "" { 250 t.Error(diff) 251 t.Errorf("\n%#v\n%#v", got, tc.want) 252 } 253 }) 254 } 255 } 256 257 func TestDecodeIntoCUEValue(t *testing.T) { 258 cuetdtest.FullMatrix.Do(t, func(t *cuetdtest.M) { 259 // We should be able to decode into a CUE value so we can 260 // decode partially incomplete values into Go. 261 // This test doesn't fit within the table used by TestDecode 262 // because cue values aren't easily comparable with cmp.Diff. 263 var st struct { 264 X cue.Value `json:"x"` 265 } 266 err := getValue(t, `x: string`).Decode(&st) 267 qt.Assert(t, qt.IsNil(err)) 268 qt.Assert(t, qt.Equals(fmt.Sprint(st.X), "string")) 269 270 // Check we can decode into a top level value. 271 var v cue.Value 272 err = getValue(t, `int`).Decode(&v) 273 qt.Assert(t, qt.IsNil(err)) 274 qt.Assert(t, qt.Equals(fmt.Sprint(v), "int")) 275 }) 276 } 277 278 type Duration struct { 279 D time.Duration 280 } 281 type S struct { 282 X Duration `json:"x"` 283 } 284 285 func (d *Duration) UnmarshalText(data []byte) error { 286 v, err := time.ParseDuration(string(data)) 287 if err != nil { 288 return err 289 } 290 d.D = v 291 return nil 292 } 293 294 func (d *Duration) MarshalText() ([]byte, error) { 295 return []byte(d.D.String()), nil 296 }