github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/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 "github.com/joomcode/cue/cue" 22 "github.com/joomcode/cue/cue/ast" 23 "github.com/joomcode/cue/cue/cuecontext" 24 "github.com/joomcode/cue/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 188 // Legacy values.format. 189 {`"no"`, `"no"`}, 190 {`"on"`, `"on"`}, 191 {`".Nan"`, `".Nan"`}, 192 193 // binary 194 {`'no'`, `!!binary bm8=`}, 195 196 // floats 197 {`.2`, "0.2"}, 198 {`2.`, "2."}, 199 {`".inf"`, `".inf"`}, 200 {`685_230.15`, `685230.15`}, 201 202 // Date and time-like 203 {`"2001-12-15T02:59:43.1Z"`, `"2001-12-15T02:59:43.1Z"`}, 204 {`"2001-12-14t21:59:43.10-05:00"`, `"2001-12-14t21:59:43.10-05:00"`}, 205 {`"2001-12-14 21:59:43.10 -5"`, `"2001-12-14 21:59:43.10 -5"`}, 206 {`"2001-12-15 2:59:43.10"`, `"2001-12-15 2:59:43.10"`}, 207 {`"2002-12-14"`, `"2002-12-14"`}, 208 {`"12-12-12"`, `"12-12-12"`}, 209 210 // legacy base60 floats 211 {`"2222:22"`, `"2222:22"`}, 212 213 // hostport 214 {`"hostname:22"`, `hostname:22`}, 215 216 // maps 217 { 218 cue: ` 219 true: 1 220 True: 2 221 ".Nan": 3 222 ".Inf": 4 223 y: 5 224 `, 225 yaml: `"true": 1 226 "True": 2 227 ".Nan": 3 228 ".Inf": 4 229 "y": 5`, 230 }, 231 } 232 for _, tc := range testCases { 233 t.Run(tc.cue, func(t *testing.T) { 234 c := cuecontext.New() 235 v := c.CompileString(tc.cue) 236 237 b, err := Encode(v) 238 if err != nil { 239 t.Error(err) 240 } 241 if got := strings.TrimSpace(string(b)); got != tc.yaml { 242 t.Errorf("Encode:\ngot %q\nwant %q", got, tc.yaml) 243 } 244 245 }) 246 } 247 }