...

Source file src/cuelang.org/go/cue/decode_test.go

Documentation: cuelang.org/go/cue

     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
    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  		// clear pointer
    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  		// clear pointer
    63  		value: `null`,
    64  		dst:   &[]int{1},
    65  		want:  []int(nil),
    66  	}, {
    67  		// clear pointer
    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  		// allocate map
   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  		// indirect int
   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  		// shorten list
   153  		value: `[1,2,3]`,
   154  		dst:   intList(1, 2, 3, 4),
   155  		want:  *intList(1, 2, 3),
   156  	}, {
   157  		// shorter array
   158  		value: `[1,2,3]`,
   159  		dst:   &[2]int{},
   160  		want:  [2]int{1, 2},
   161  	}, {
   162  		// longer array
   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  		// Issue #1401
   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  		// Issue #1466
   220  		value: `{"x": "1s"}
   221  		`,
   222  		dst:  &S{},
   223  		want: S{X: Duration{D: 1000000000}},
   224  	}, {
   225  		// Issue #1466
   226  		value: `{"x": '1s'}
   227  			`,
   228  		dst:  &S{},
   229  		want: S{X: Duration{D: 1000000000}},
   230  	}, {
   231  		// Issue #1466
   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  	// We should be able to decode into a CUE value so we can
   257  	// decode partially incomplete values into Go.
   258  	// This test doesn't fit within the table used by TestDecode
   259  	// because cue values aren't easily comparable with cmp.Diff.
   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  	// Check we can decode into a top level value.
   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