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