cuelang.org/go@v0.10.1/encoding/yaml/yaml_test.go (about)

     1  // Copyright 2019 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 yaml
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"cuelang.org/go/cue"
    22  	"cuelang.org/go/cue/ast"
    23  	"cuelang.org/go/cue/cuecontext"
    24  	"cuelang.org/go/cue/format"
    25  )
    26  
    27  func TestYAML(t *testing.T) {
    28  	testCases := []struct {
    29  		name     string
    30  		yaml     string
    31  		yamlOut  string
    32  		want     string
    33  		isStream bool
    34  	}{{
    35  		name:    "empty",
    36  		yaml:    "",
    37  		yamlOut: "null",
    38  		want:    "null",
    39  	}, {
    40  		name:     "empty stream",
    41  		want:     "null",
    42  		isStream: true,
    43  	}, {
    44  		name: "string literal",
    45  		yaml: `foo`,
    46  		want: `"foo"`,
    47  	}, {
    48  		name: "struct",
    49  		yaml: `a: foo
    50  b: bar`,
    51  		want: `a: "foo"
    52  b: "bar"`,
    53  	}, {
    54  		name: "stream",
    55  		yaml: `a: foo
    56  ---
    57  b: bar
    58  c: baz
    59  `,
    60  		want: `[{
    61  	a: "foo"
    62  }, {
    63  	b: "bar"
    64  	c: "baz"
    65  }]`,
    66  		isStream: true,
    67  	}, {
    68  		name: "stream with null",
    69  		yaml: `
    70  ---
    71  a: foo
    72  ---
    73  ---
    74  b: bar
    75  c: baz
    76  ---
    77  `,
    78  		// Not sure if a leading document separator should be gobbled, but the
    79  		// YAML parser seems to think so. This could have something to do with
    80  		// the fact that the document separator is really an "end of directives"
    81  		// marker, while ... means "end of document". YAML is hard!
    82  		yamlOut: `a: foo
    83  ---
    84  null
    85  ---
    86  b: bar
    87  c: baz
    88  ---
    89  null
    90  `,
    91  		// TODO(bug): seems like bug in yaml parser. Try moving to yaml.v3,
    92  		// or validate that this is indeed a correct interpretation.
    93  		want: `[{
    94  	a: "foo"
    95  }, null, {
    96  	b: "bar"
    97  	c: "baz"
    98  }, null]`,
    99  		isStream: true,
   100  	}}
   101  	r := &cue.Runtime{}
   102  	for _, tc := range testCases {
   103  		t.Run(tc.name, func(t *testing.T) {
   104  			f, err := Extract(tc.name, tc.yaml)
   105  			if err != nil {
   106  				t.Fatal(err)
   107  			}
   108  			b, _ := format.Node(f)
   109  			if got := strings.TrimSpace(string(b)); got != tc.want {
   110  				t.Errorf("Extract:\ngot  %q\nwant %q", got, tc.want)
   111  			}
   112  
   113  			inst, err := Decode(r, tc.name, tc.yaml)
   114  			if err != nil {
   115  				t.Fatal(err)
   116  			}
   117  			n := inst.Value().Syntax()
   118  			if s, ok := n.(*ast.StructLit); ok {
   119  				n = &ast.File{Decls: s.Elts}
   120  			}
   121  			b, _ = format.Node(n)
   122  			if got := strings.TrimSpace(string(b)); got != tc.want {
   123  				t.Errorf("Decode:\ngot  %q\nwant %q", got, tc.want)
   124  			}
   125  
   126  			yamlOut := tc.yaml
   127  			if tc.yamlOut != "" {
   128  				yamlOut = tc.yamlOut
   129  			}
   130  
   131  			inst, _ = r.Compile(tc.name, tc.want)
   132  			if !tc.isStream {
   133  				b, err = Encode(inst.Value())
   134  				if err != nil {
   135  					t.Error(err)
   136  				}
   137  				if got := strings.TrimSpace(string(b)); got != yamlOut {
   138  					t.Errorf("Encode:\ngot  %q\nwant %q", got, yamlOut)
   139  				}
   140  			} else {
   141  				iter, _ := inst.Value().List()
   142  				b, err := EncodeStream(iter)
   143  				if err != nil {
   144  					t.Error(err)
   145  				}
   146  				if got := string(b); got != yamlOut {
   147  					t.Errorf("EncodeStream:\ngot  %q\nwant %q", got, yamlOut)
   148  				}
   149  			}
   150  		})
   151  	}
   152  }
   153  
   154  func TestYAMLValues(t *testing.T) {
   155  	testCases := []struct {
   156  		cue  string
   157  		yaml string
   158  	}{
   159  		// strings
   160  		{`"""
   161  	single
   162  	"""`, "single"}, // TODO: CUE simplifies this.
   163  
   164  		{`"""
   165  	aaaa
   166  	bbbb
   167  	"""`, `|-
   168    aaaa
   169    bbbb`},
   170  
   171  		// keep as is
   172  		{`"non"`, `non`},
   173  
   174  		// Non-strings in v1.2. These are single-quoted by the go-yaml.v3 package.
   175  		{`"#cloudmon"`, `'#cloudmon'`},
   176  
   177  		// Strings that mimic numeric values are double quoted by the go-yaml.v3
   178  		// package.
   179  		{`".inf"`, `".inf"`},
   180  		{`".Inf"`, `".Inf"`},
   181  		{`".INF"`, `".INF"`},
   182  		{`".NaN"`, `".NaN"`},
   183  		{`"+.Inf"`, `"+.Inf"`},
   184  		{`"-.Inf"`, `"-.Inf"`},
   185  		{`"2002"`, `"2002"`},
   186  		{`"685_230.15"`, `"685_230.15"`},
   187  		// Note that go-yaml doesn't quote strings which look like hexadecimal numbers,
   188  		// but we do in our fork. See: https://github.com/go-yaml/yaml/issues/847
   189  		{`"0x123456789012345678901234567890"`, `"0x123456789012345678901234567890"`},
   190  
   191  		// Legacy values.format.
   192  		{`"no"`, `"no"`},
   193  		{`"on"`, `"on"`},
   194  		{`".Nan"`, `".Nan"`},
   195  
   196  		// binary
   197  		{`'no'`, `!!binary bm8=`},
   198  
   199  		// floats
   200  		{`.2`, "0.2"},
   201  		{`2.`, "2."},
   202  		{`".inf"`, `".inf"`},
   203  		{`685_230.15`, `685230.15`},
   204  
   205  		// Date and time-like
   206  		{`"2001-12-15T02:59:43.1Z"`, `"2001-12-15T02:59:43.1Z"`},
   207  		{`"2001-12-14t21:59:43.10-05:00"`, `"2001-12-14t21:59:43.10-05:00"`},
   208  		{`"2001-12-14 21:59:43.10 -5"`, `"2001-12-14 21:59:43.10 -5"`},
   209  		{`"2001-12-15 2:59:43.10"`, `"2001-12-15 2:59:43.10"`},
   210  		{`"2002-12-14"`, `"2002-12-14"`},
   211  		{`"12-12-12"`, `"12-12-12"`},
   212  
   213  		// legacy base60 floats
   214  		{`"2222:22"`, `"2222:22"`},
   215  
   216  		// hostport
   217  		{`"hostname:22"`, `hostname:22`},
   218  
   219  		// maps
   220  		{
   221  			cue: `
   222  			true:   1
   223  			True:   2
   224  			".Nan": 3
   225  			".Inf": 4
   226  			y:      5
   227  			`,
   228  			yaml: `"true": 1
   229  "True": 2
   230  ".Nan": 3
   231  ".Inf": 4
   232  "y": 5`,
   233  		},
   234  	}
   235  	for _, tc := range testCases {
   236  		t.Run(tc.cue, func(t *testing.T) {
   237  			c := cuecontext.New()
   238  			v := c.CompileString(tc.cue)
   239  
   240  			b, err := Encode(v)
   241  			if err != nil {
   242  				t.Error(err)
   243  			}
   244  			if got := strings.TrimSpace(string(b)); got != tc.yaml {
   245  				t.Errorf("Encode:\ngot  %q\nwant %q", got, tc.yaml)
   246  			}
   247  
   248  		})
   249  	}
   250  }