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