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 }