cuelang.org/go@v0.13.0/internal/filetypes/filetypes_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 filetypes 16 17 import ( 18 "strings" 19 "testing" 20 21 "github.com/go-quicktest/qt" 22 "github.com/google/go-cmp/cmp" 23 "github.com/google/go-cmp/cmp/cmpopts" 24 25 "cuelang.org/go/cue/build" 26 "cuelang.org/go/cue/errors" 27 ) 28 29 func check(t *testing.T, want, x interface{}, err error) { 30 t.Helper() 31 if err != nil { 32 x = errors.String(err.(errors.Error)) 33 } 34 if diff := cmp.Diff(want, x, cmpopts.EquateEmpty()); diff != "" { 35 t.Errorf("unexpected result; -want +got\n%s", diff) 36 } 37 } 38 39 func TestFromFile(t *testing.T) { 40 testCases := []struct { 41 name string 42 in build.File 43 mode Mode 44 out interface{} 45 }{{ 46 name: "must specify encoding", 47 in: build.File{}, 48 out: `no encoding specified`, 49 }, { 50 // Default without any 51 name: "cue", 52 in: build.File{ 53 Filename: "", 54 Encoding: build.CUE, 55 }, 56 mode: Def, 57 out: &FileInfo{ 58 Encoding: build.CUE, 59 Form: build.Schema, 60 Definitions: true, 61 Data: true, 62 Optional: true, 63 Constraints: true, 64 References: true, 65 Cycles: true, 66 KeepDefaults: true, 67 Incomplete: true, 68 Imports: true, 69 Docs: true, 70 Attributes: true, 71 }, 72 }, { 73 // CUE encoding in data form. 74 name: "data: cue", 75 in: build.File{ 76 Filename: "", 77 Form: build.Data, 78 Encoding: build.CUE, 79 }, 80 mode: Input, 81 out: &FileInfo{ 82 Filename: "", 83 Encoding: build.CUE, 84 Form: build.Data, 85 Data: true, 86 Docs: true, 87 Attributes: true, 88 }, 89 }, { 90 // Filename starting with a . but no other extension. 91 name: "filename-with-dot", 92 in: build.File{ 93 Filename: ".json", 94 }, 95 mode: Def, 96 out: `no encoding specified`, 97 }, { 98 name: "yaml", 99 mode: Def, 100 in: build.File{ 101 Filename: "foo.yaml", 102 Encoding: build.YAML, 103 }, 104 out: &FileInfo{ 105 Filename: "foo.yaml", 106 Encoding: build.YAML, 107 Form: build.Graph, 108 Data: true, 109 References: true, 110 Cycles: true, 111 Stream: true, 112 Docs: true, 113 Attributes: true, 114 }, 115 }, { 116 name: "yaml+openapi", 117 in: build.File{ 118 Filename: "foo.yaml", 119 Encoding: build.YAML, 120 Interpretation: build.OpenAPI, 121 }, 122 out: &FileInfo{ 123 Filename: "foo.yaml", 124 Encoding: build.YAML, 125 Interpretation: build.OpenAPI, 126 Form: build.Schema, 127 Definitions: true, 128 Data: true, 129 Optional: true, 130 Constraints: true, 131 References: true, 132 Cycles: true, 133 KeepDefaults: true, 134 Incomplete: true, 135 Imports: true, 136 Docs: true, 137 Attributes: true, 138 }, 139 }, { 140 name: "JSONDefault", 141 mode: Input, 142 in: build.File{ 143 Filename: "data.json", 144 Encoding: build.JSON, 145 }, 146 out: &FileInfo{ 147 Filename: "data.json", 148 Encoding: build.JSON, 149 Form: build.Data, 150 Definitions: false, 151 Data: true, 152 Optional: false, 153 Constraints: false, 154 References: false, 155 Cycles: false, 156 KeepDefaults: false, 157 Incomplete: false, 158 Imports: false, 159 Docs: false, 160 Attributes: false, 161 }, 162 }, { 163 name: "JSONSchema", 164 in: build.File{ 165 Filename: "foo.json", 166 Interpretation: "jsonschema", 167 Encoding: build.JSON, 168 }, 169 out: &FileInfo{ 170 Filename: "foo.json", 171 Encoding: build.JSON, 172 Interpretation: "jsonschema", 173 Form: build.Schema, 174 Definitions: true, 175 Data: true, 176 Optional: true, 177 Constraints: true, 178 References: true, 179 Cycles: true, 180 KeepDefaults: true, 181 Incomplete: true, 182 Imports: true, 183 Docs: true, 184 Attributes: true, 185 }, 186 }, { 187 name: "JSONOpenAPI", 188 in: build.File{ 189 Filename: "foo.json", 190 Encoding: build.JSON, 191 Interpretation: build.OpenAPI, 192 }, 193 mode: Def, 194 out: &FileInfo{ 195 Filename: "foo.json", 196 Encoding: build.JSON, 197 Interpretation: build.OpenAPI, 198 Form: build.Schema, 199 Definitions: true, 200 Data: true, 201 Optional: true, 202 Constraints: true, 203 References: true, 204 Cycles: true, 205 KeepDefaults: true, 206 Incomplete: true, 207 Imports: true, 208 Docs: true, 209 Attributes: true, 210 }, 211 }, { 212 name: "KoalaXML", 213 in: build.File{ 214 Filename: "foo.xml", 215 Encoding: "xml", 216 BoolTags: map[string]bool{ 217 "koala": true, 218 }, 219 }, 220 mode: Def, 221 out: &FileInfo{ 222 Filename: "foo.xml", 223 Encoding: "xml", 224 Form: "data", 225 Definitions: false, 226 Data: true, 227 Optional: false, 228 Constraints: false, 229 References: false, 230 Cycles: false, 231 KeepDefaults: false, 232 Incomplete: false, 233 Imports: false, 234 Docs: true, 235 Attributes: true, 236 }, 237 }, { 238 name: "OpenAPIDefaults", 239 in: build.File{ 240 Filename: "-", 241 Encoding: build.JSON, 242 Interpretation: build.OpenAPI, 243 }, 244 mode: Def, 245 out: &FileInfo{ 246 Filename: "-", 247 Encoding: build.JSON, 248 Interpretation: build.OpenAPI, 249 Form: build.Schema, 250 Definitions: true, 251 Data: true, 252 Optional: true, 253 Constraints: true, 254 References: true, 255 Cycles: true, 256 KeepDefaults: true, 257 Incomplete: true, 258 Imports: true, 259 Docs: true, 260 Attributes: true, 261 }, 262 }, { 263 name: "Go", 264 in: build.File{ 265 Encoding: build.Code, 266 Filename: "foo.go", 267 }, 268 mode: Def, 269 out: &FileInfo{ 270 Filename: "foo.go", 271 Encoding: build.Code, 272 Form: build.Schema, 273 Definitions: true, 274 Data: true, 275 Optional: true, 276 Constraints: true, 277 References: true, 278 Cycles: true, 279 KeepDefaults: true, 280 Incomplete: true, 281 Imports: true, 282 Docs: true, 283 Attributes: true, 284 }, 285 }} 286 for _, tc := range testCases { 287 t.Run(tc.name, func(t *testing.T) { 288 info, err := FromFile(&tc.in, tc.mode) 289 check(t, tc.out, info, err) 290 }) 291 } 292 } 293 294 func TestParseFile(t *testing.T) { 295 // TODO(errors): wrong path? 296 testCases := []struct { 297 in string 298 mode Mode 299 out interface{} 300 }{{ 301 in: "file.json", 302 mode: Input, 303 out: &build.File{ 304 Filename: "file.json", 305 Encoding: build.JSON, 306 Interpretation: build.Auto, 307 }, 308 }, { 309 in: ".json", 310 mode: Input, 311 out: `no encoding specified for file ".json"`, 312 }, { 313 in: ".json.yaml", 314 mode: Input, 315 out: &build.File{ 316 Filename: ".json.yaml", 317 Encoding: build.YAML, 318 Interpretation: build.Auto, 319 }, 320 }, { 321 in: "file.json", 322 mode: Def, 323 out: &build.File{ 324 Filename: "file.json", 325 Encoding: build.JSON, 326 }, 327 }, { 328 in: "schema:file.json", 329 out: &build.File{ 330 Filename: "file.json", 331 Encoding: build.JSON, 332 Interpretation: build.Auto, 333 Form: build.Schema, 334 }, 335 }, { 336 in: "openapi:-", 337 out: &build.File{ 338 Filename: "-", 339 Encoding: build.JSON, 340 Interpretation: build.OpenAPI, 341 Form: build.Schema, 342 BoolTags: map[string]bool{ 343 "strict": false, 344 "strictFeatures": false, 345 "strictKeywords": false, 346 }, 347 }, 348 }, { 349 in: "cue:file.json", 350 out: &build.File{ 351 Filename: "file.json", 352 Encoding: build.CUE, 353 }, 354 }, { 355 in: "cue+schema:-", 356 out: &build.File{ 357 Filename: "-", 358 Encoding: build.CUE, 359 Form: build.Schema, 360 }, 361 }, { 362 in: "code+lang=js:foo.x", 363 out: &build.File{ 364 Filename: "foo.x", 365 Encoding: build.Code, 366 Tags: map[string]string{"lang": "js"}, 367 }, 368 }, { 369 in: "json+lang=js:foo.x", 370 out: `tag lang is not allowed in this context`, 371 }, { 372 in: "foo:file.bar", 373 out: `unknown filetype foo`, 374 }, { 375 in: "file.bar", 376 out: `unknown file extension .bar`, 377 }, { 378 in: "-", 379 mode: Input, 380 out: &build.File{ 381 Filename: "-", 382 Encoding: build.CUE, 383 }, 384 }, { 385 in: "-", 386 mode: Export, 387 out: &build.File{ 388 Filename: "-", 389 Encoding: build.JSON, 390 }, 391 }} 392 for _, tc := range testCases { 393 t.Run(tc.in, func(t *testing.T) { 394 f, err := ParseFile(tc.in, tc.mode) 395 check(t, tc.out, f, err) 396 }) 397 } 398 } 399 400 func TestParseArgs(t *testing.T) { 401 testCases := []struct { 402 in string 403 out interface{} 404 }{{ 405 in: "foo.json baz.yaml", 406 out: []*build.File{ 407 { 408 Filename: "foo.json", 409 Encoding: build.JSON, 410 Interpretation: build.Auto, 411 }, 412 { 413 Filename: "baz.yaml", 414 Encoding: build.YAML, 415 Interpretation: build.Auto, 416 }, 417 }, 418 }, { 419 in: "data: foo.cue", 420 out: []*build.File{ 421 {Filename: "foo.cue", Encoding: build.CUE, Form: build.Data}, 422 }, 423 }, { 424 in: "json: foo.json bar.data jsonschema: bar.schema", 425 out: []*build.File{ 426 {Filename: "foo.json", Encoding: build.JSON}, // no auto! 427 {Filename: "bar.data", Encoding: build.JSON}, 428 { 429 Filename: "bar.schema", 430 Encoding: build.JSON, 431 Form: build.Schema, 432 Interpretation: "jsonschema", 433 BoolTags: map[string]bool{ 434 "strict": false, 435 "strictFeatures": false, 436 "strictKeywords": false, 437 }, 438 }, 439 }, 440 }, { 441 in: "koala: bar.xml", 442 out: []*build.File{ 443 { 444 Filename: "bar.xml", 445 Encoding: "xml", 446 Interpretation: "auto", 447 BoolTags: map[string]bool{ 448 "koala": true, 449 }, 450 }, 451 }, 452 }, { 453 in: "jsonschema+strict: bar.schema", 454 out: []*build.File{ 455 { 456 Filename: "bar.schema", 457 Encoding: "json", 458 Interpretation: "jsonschema", 459 Form: build.Schema, 460 BoolTags: map[string]bool{ 461 "strict": true, 462 "strictFeatures": true, 463 "strictKeywords": true, 464 }, 465 }, 466 }, 467 }, { 468 in: "jsonschema+strictFeatures=1: bar.schema", 469 out: []*build.File{ 470 { 471 Filename: "bar.schema", 472 Encoding: "json", 473 Interpretation: "jsonschema", 474 Form: build.Schema, 475 BoolTags: map[string]bool{ 476 "strict": false, 477 "strictFeatures": true, 478 "strictKeywords": false, 479 }, 480 }, 481 }, 482 }, { 483 in: `json: c:\foo.json c:\path\to\file.dat`, 484 out: []*build.File{ 485 {Filename: `c:\foo.json`, Encoding: build.JSON}, 486 {Filename: `c:\path\to\file.dat`, Encoding: build.JSON}, 487 }, 488 }, { 489 in: "json: json+schema: bar.schema", 490 out: `scoped qualifier "json:" without file`, 491 }, { 492 in: "json:", 493 out: `scoped qualifier "json:" without file`, 494 }, { 495 in: "json:foo:bar.yaml", 496 out: `unsupported file name "json:foo:bar.yaml": may not have ':'`, 497 }} 498 for _, tc := range testCases { 499 t.Run(tc.in, func(t *testing.T) { 500 files, err := ParseArgs(strings.Split(tc.in, " ")) 501 check(t, tc.out, files, err) 502 }) 503 } 504 } 505 506 func TestDefaultTagsForInterpretation(t *testing.T) { 507 tags := DefaultTagsForInterpretation(build.JSONSchema, Input) 508 qt.Assert(t, qt.DeepEquals(tags, map[string]bool{ 509 "strict": false, 510 "strictFeatures": false, 511 "strictKeywords": false, 512 })) 513 }