cuelang.org/go@v0.10.1/internal/encoding/yaml/encode_test.go (about) 1 // Copyright 2020 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/google/go-cmp/cmp" 22 "github.com/kr/pretty" 23 "gopkg.in/yaml.v3" 24 25 "cuelang.org/go/cue/ast" 26 "cuelang.org/go/cue/parser" 27 ) 28 29 func TestEncodeFile(t *testing.T) { 30 testCases := []struct { 31 name string 32 in string 33 out string 34 }{{ 35 name: "foo", 36 in: ` 37 package test 38 39 seq: [ 40 1, 2, 3, { 41 a: 1 42 b: 2 43 } 44 ] 45 a: b: c: 3 46 b: { 47 x: 0 48 y: 1 49 z: 2 50 } 51 `, 52 out: ` 53 seq: 54 - 1 55 - 2 56 - 3 57 - a: 1 58 b: 2 59 a: 60 b: 61 c: 3 62 b: 63 x: 0 64 "y": 1 65 z: 2 66 `, 67 }, { 68 name: "oneLineFields", 69 in: ` 70 seq: [1, 2, 3] 71 map: {a: 3} 72 str: "str" 73 int: 1K 74 bin: 0b11 75 hex: 0x11 76 dec: .3 77 dat: '\x80' 78 nil: null 79 yes: true 80 non: false 81 `, 82 out: ` 83 seq: [1, 2, 3] 84 map: {a: 3} 85 str: str 86 int: 1000 87 bin: 0b11 88 hex: 0x11 89 dec: .3 90 dat: !!binary gA== 91 nil: null 92 "yes": true 93 non: false 94 `, 95 }, { 96 name: "comments", 97 in: ` 98 // Document 99 100 // head 1 101 f1: 1 102 // foot 1 103 104 // head 2 105 f2: 2 // line 2 106 107 // intermezzo f2 108 // 109 // with multiline 110 111 // head 3 112 f3: 113 // struct doc 114 { 115 a: 1 116 } 117 118 f4: { 119 } // line 4 120 121 // Trailing 122 `, 123 out: ` 124 # Document 125 126 # head 1 127 f1: 1 128 # foot 1 129 130 # head 2 131 f2: 2 # line 2 132 133 # intermezzo f2 134 # 135 # with multiline 136 137 # head 3 138 f3: 139 # struct doc 140 a: 1 141 f4: {} # line 4 142 143 # Trailing 144 `, 145 }, { 146 // TODO: support this at some point 147 name: "embed", 148 in: ` 149 // hex 150 0xabc // line 151 // trail 152 `, 153 out: ` 154 # hex 155 0xabc # line 156 # trail 157 `, 158 }, { 159 // TODO: support this at some point 160 name: "anchors", 161 in: ` 162 a: b 163 b: 3 164 `, 165 out: "yaml: unsupported node b (*ast.Ident)", 166 }, { 167 name: "errors", 168 in: ` 169 m: { 170 a: 1 171 b: 3 172 } 173 c: [1, [for x in m {x}]] 174 `, 175 out: "yaml: unsupported node for x in m {x} (*ast.Comprehension)", 176 }, { 177 name: "disallowMultipleEmbeddings", 178 in: ` 179 1 180 1 181 `, 182 out: "yaml: multiple embedded values", 183 }, { 184 name: "disallowDefinitions", 185 in: `#a: 2 `, 186 out: "yaml: definition or hidden fields not allowed", 187 }, { 188 name: "disallowHidden", 189 in: `_a: 2 `, 190 out: "yaml: definition or hidden fields not allowed", 191 }, { 192 name: "disallowOptionals", 193 in: `a?: 2`, 194 out: "yaml: optional fields not allowed", 195 }, { 196 name: "disallowBulkOptionals", 197 in: `[string]: 2`, 198 out: "yaml: only literal labels allowed", 199 }, { 200 name: "noImports", 201 in: ` 202 import "foo" 203 204 a: 1 205 `, 206 out: `yaml: unsupported node import "foo" (*ast.ImportDecl)`, 207 }, { 208 name: "disallowMultipleEmbeddings", 209 in: ` 210 1 211 a: 2 212 `, 213 out: "yaml: embedding mixed with fields", 214 }, { 215 name: "prometheus", 216 in: ` 217 { 218 receivers: [{ 219 name: "pager" 220 slack_configs: [{ 221 text: """ 222 {{ range .Alerts }}{{ .Annotations.description }} 223 {{ end }} 224 """ 225 channel: "#cloudmon" 226 send_resolved: true 227 }] 228 }] 229 route: { 230 receiver: "pager" 231 group_by: ["alertname", "cluster"] 232 } 233 }`, 234 out: ` 235 receivers: 236 - name: pager 237 slack_configs: 238 - text: |- 239 {{ range .Alerts }}{{ .Annotations.description }} 240 {{ end }} 241 channel: '#cloudmon' 242 send_resolved: true 243 route: 244 receiver: pager 245 group_by: [alertname, cluster] 246 `, 247 }} 248 for _, tc := range testCases { 249 t.Run(tc.name, func(t *testing.T) { 250 f, err := parser.ParseFile(tc.name, tc.in, parser.ParseComments) 251 if err != nil { 252 t.Fatal(err) 253 } 254 b, err := Encode(f) 255 var got string 256 if err != nil { 257 got = err.Error() 258 } else { 259 got = strings.TrimSpace(string(b)) 260 } 261 want := strings.TrimSpace(tc.out) 262 if got != want { 263 t.Error(cmp.Diff(got, want)) 264 } 265 }) 266 } 267 } 268 269 func TestEncodeAST(t *testing.T) { 270 comment := func(s string) *ast.CommentGroup { 271 return &ast.CommentGroup{List: []*ast.Comment{ 272 {Text: "// " + s}, 273 }} 274 } 275 testCases := []struct { 276 name string 277 in ast.Expr 278 out string 279 }{{ 280 in: ast.NewStruct( 281 comment("foo"), 282 comment("bar"), 283 "field", ast.NewString("value"), 284 "field2", ast.NewString("value"), 285 comment("trail1"), 286 comment("trail2"), 287 ), 288 out: ` 289 # foo 290 291 # bar 292 293 field: value 294 field2: value 295 296 # trail1 297 298 # trail2 299 `, 300 }, { 301 in: &ast.StructLit{Elts: []ast.Decl{ 302 comment("bar"), 303 &ast.EmbedDecl{Expr: ast.NewBool(true)}, 304 }}, 305 out: ` 306 # bar 307 308 true 309 `, 310 }} 311 for _, tc := range testCases { 312 t.Run(tc.name, func(t *testing.T) { 313 n, err := encode(tc.in) 314 if err != nil { 315 t.Fatal(err) 316 } 317 b, err := yaml.Marshal(n) 318 if err != nil { 319 t.Fatal(err) 320 } 321 got := strings.TrimSpace(string(b)) 322 want := strings.TrimSpace(tc.out) 323 if got != want { 324 t.Error(cmp.Diff(got, want)) 325 } 326 }) 327 } 328 } 329 330 // TestX is for experimentation with the YAML package to figure out the 331 // semantics of the Node type. 332 func TestX(t *testing.T) { 333 t.Skip() 334 var n yaml.Node 335 yaml.Unmarshal([]byte(` 336 # map 337 map: { 338 # doc 339 } # line 1 340 341 `), &n) 342 343 pretty.Print(n) 344 t.Fail() 345 }