github.com/solo-io/cue@v0.4.7/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 "github.com/solo-io/cue/cue/ast" 26 "github.com/solo-io/cue/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 not allowed", 187 }, { 188 name: "disallowOptionals", 189 in: `a?: 2`, 190 out: "yaml: optional fields not allowed", 191 }, { 192 name: "disallowBulkOptionals", 193 in: `[string]: 2`, 194 out: "yaml: only literal labels allowed", 195 }, { 196 name: "noImports", 197 in: ` 198 import "foo" 199 200 a: 1 201 `, 202 out: `yaml: unsupported node import "foo" (*ast.ImportDecl)`, 203 }, { 204 name: "disallowMultipleEmbeddings", 205 in: ` 206 1 207 a: 2 208 `, 209 out: "yaml: embedding mixed with fields", 210 }, { 211 name: "prometheus", 212 in: ` 213 { 214 receivers: [{ 215 name: "pager" 216 slack_configs: [{ 217 text: """ 218 {{ range .Alerts }}{{ .Annotations.description }} 219 {{ end }} 220 """ 221 channel: "#cloudmon" 222 send_resolved: true 223 }] 224 }] 225 route: { 226 receiver: "pager" 227 group_by: ["alertname", "cluster"] 228 } 229 }`, 230 out: ` 231 receivers: 232 - name: pager 233 slack_configs: 234 - text: |- 235 {{ range .Alerts }}{{ .Annotations.description }} 236 {{ end }} 237 channel: '#cloudmon' 238 send_resolved: true 239 route: 240 receiver: pager 241 group_by: [alertname, cluster] 242 `, 243 }} 244 for _, tc := range testCases { 245 t.Run(tc.name, func(t *testing.T) { 246 f, err := parser.ParseFile(tc.name, tc.in, parser.ParseComments) 247 if err != nil { 248 t.Fatal(err) 249 } 250 b, err := Encode(f) 251 var got string 252 if err != nil { 253 got = err.Error() 254 } else { 255 got = strings.TrimSpace(string(b)) 256 } 257 want := strings.TrimSpace(tc.out) 258 if got != want { 259 t.Error(cmp.Diff(got, want)) 260 } 261 }) 262 } 263 } 264 265 func TestEncodeAST(t *testing.T) { 266 comment := func(s string) *ast.CommentGroup { 267 return &ast.CommentGroup{List: []*ast.Comment{ 268 &ast.Comment{Text: "// " + s}, 269 }} 270 } 271 testCases := []struct { 272 name string 273 in ast.Expr 274 out string 275 }{{ 276 in: ast.NewStruct( 277 comment("foo"), 278 comment("bar"), 279 "field", ast.NewString("value"), 280 "field2", ast.NewString("value"), 281 comment("trail1"), 282 comment("trail2"), 283 ), 284 out: ` 285 # foo 286 287 # bar 288 289 field: value 290 field2: value 291 292 # trail1 293 294 # trail2 295 `, 296 }, { 297 in: &ast.StructLit{Elts: []ast.Decl{ 298 comment("bar"), 299 &ast.EmbedDecl{Expr: ast.NewBool(true)}, 300 }}, 301 out: ` 302 # bar 303 304 true 305 `, 306 }} 307 for _, tc := range testCases { 308 t.Run(tc.name, func(t *testing.T) { 309 n, err := encode(tc.in) 310 if err != nil { 311 t.Fatal(err) 312 } 313 b, err := yaml.Marshal(n) 314 if err != nil { 315 t.Fatal(err) 316 } 317 got := strings.TrimSpace(string(b)) 318 want := strings.TrimSpace(tc.out) 319 if got != want { 320 t.Error(cmp.Diff(got, want)) 321 } 322 }) 323 } 324 } 325 326 // TestX is for experimentation with the YAML package to figure out the 327 // semantics of the Node type. 328 func TestX(t *testing.T) { 329 t.Skip() 330 var n yaml.Node 331 yaml.Unmarshal([]byte(` 332 # map 333 map: { 334 # doc 335 } # line 1 336 337 `), &n) 338 339 pretty.Print(n) 340 t.Fail() 341 }