github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/plugins/shared/hclspec/dec_test.go (about) 1 package hclspec 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/hcl2/hcldec" 7 "github.com/stretchr/testify/require" 8 "github.com/zclconf/go-cty/cty" 9 ) 10 11 type testConversions struct { 12 Name string 13 Input *Spec 14 Expected hcldec.Spec 15 ExpectedError string 16 } 17 18 func testSpecConversions(t *testing.T, cases []testConversions) { 19 t.Helper() 20 21 for _, c := range cases { 22 t.Run(c.Name, func(t *testing.T) { 23 act, diag := Convert(c.Input) 24 if diag.HasErrors() { 25 if c.ExpectedError == "" { 26 t.Fatalf("Convert %q failed: %v", c.Name, diag.Error()) 27 } 28 29 require.Contains(t, diag.Error(), c.ExpectedError) 30 } else if c.ExpectedError != "" { 31 t.Fatalf("Expected error %q", c.ExpectedError) 32 } 33 34 require.EqualValues(t, c.Expected, act) 35 }) 36 } 37 } 38 39 func TestDec_Convert_Object(t *testing.T) { 40 t.Parallel() 41 42 tests := []testConversions{ 43 { 44 Name: "Object w/ only attributes", 45 Input: &Spec{ 46 Block: &Spec_Object{ 47 &Object{ 48 Attributes: map[string]*Spec{ 49 "foo": { 50 Block: &Spec_Attr{ 51 &Attr{ 52 Type: "string", 53 Required: false, 54 }, 55 }, 56 }, 57 "bar": { 58 Block: &Spec_Attr{ 59 &Attr{ 60 Type: "number", 61 Required: true, 62 }, 63 }, 64 }, 65 "baz": { 66 Block: &Spec_Attr{ 67 &Attr{ 68 Type: "bool", 69 }, 70 }, 71 }, 72 }, 73 }, 74 }, 75 }, 76 Expected: hcldec.ObjectSpec(map[string]hcldec.Spec{ 77 "foo": &hcldec.AttrSpec{ 78 Name: "foo", 79 Type: cty.String, 80 Required: false, 81 }, 82 "bar": &hcldec.AttrSpec{ 83 Name: "bar", 84 Type: cty.Number, 85 Required: true, 86 }, 87 "baz": &hcldec.AttrSpec{ 88 Name: "baz", 89 Type: cty.Bool, 90 Required: false, 91 }, 92 }), 93 }, 94 } 95 96 testSpecConversions(t, tests) 97 } 98 99 func TestDec_Convert_Array(t *testing.T) { 100 t.Parallel() 101 102 tests := []testConversions{ 103 { 104 Name: "array basic", 105 Input: &Spec{ 106 Block: &Spec_Array{ 107 Array: &Array{ 108 Values: []*Spec{ 109 { 110 Block: &Spec_Attr{ 111 &Attr{ 112 Name: "foo", 113 Required: true, 114 Type: "string", 115 }, 116 }, 117 }, 118 { 119 Block: &Spec_Attr{ 120 &Attr{ 121 Name: "bar", 122 Required: true, 123 Type: "string", 124 }, 125 }, 126 }, 127 }, 128 }, 129 }, 130 }, 131 Expected: hcldec.TupleSpec{ 132 &hcldec.AttrSpec{ 133 Name: "foo", 134 Type: cty.String, 135 Required: true, 136 }, 137 &hcldec.AttrSpec{ 138 Name: "bar", 139 Type: cty.String, 140 Required: true, 141 }, 142 }, 143 }, 144 } 145 146 testSpecConversions(t, tests) 147 } 148 149 func TestDec_Convert_Attr(t *testing.T) { 150 t.Parallel() 151 152 tests := []testConversions{ 153 { 154 Name: "attr basic type", 155 Input: &Spec{ 156 Block: &Spec_Attr{ 157 &Attr{ 158 Name: "foo", 159 Required: true, 160 Type: "string", 161 }, 162 }, 163 }, 164 Expected: &hcldec.AttrSpec{ 165 Name: "foo", 166 Type: cty.String, 167 Required: true, 168 }, 169 }, 170 { 171 Name: "attr object type", 172 Input: &Spec{ 173 Block: &Spec_Attr{ 174 &Attr{ 175 Name: "foo", 176 Required: true, 177 Type: "object({name1 = string, name2 = bool})", 178 }, 179 }, 180 }, 181 Expected: &hcldec.AttrSpec{ 182 Name: "foo", 183 Type: cty.Object(map[string]cty.Type{ 184 "name1": cty.String, 185 "name2": cty.Bool, 186 }), 187 Required: true, 188 }, 189 }, 190 { 191 Name: "attr no name", 192 Input: &Spec{ 193 Block: &Spec_Attr{ 194 &Attr{ 195 Required: true, 196 Type: "string", 197 }, 198 }, 199 }, 200 ExpectedError: "Missing name in attribute spec", 201 }, 202 } 203 204 testSpecConversions(t, tests) 205 } 206 207 func TestDec_Convert_Block(t *testing.T) { 208 t.Parallel() 209 210 tests := []testConversions{ 211 { 212 Name: "block with attr", 213 Input: &Spec{ 214 Block: &Spec_BlockValue{ 215 BlockValue: &Block{ 216 Name: "test", 217 Required: true, 218 Nested: &Spec{ 219 Block: &Spec_Attr{ 220 &Attr{ 221 Name: "foo", 222 Type: "string", 223 }, 224 }, 225 }, 226 }, 227 }, 228 }, 229 Expected: &hcldec.BlockSpec{ 230 TypeName: "test", 231 Required: true, 232 Nested: &hcldec.AttrSpec{ 233 Name: "foo", 234 Type: cty.String, 235 Required: false, 236 }, 237 }, 238 }, 239 { 240 Name: "block with nested block", 241 Input: &Spec{ 242 Block: &Spec_BlockValue{ 243 BlockValue: &Block{ 244 Name: "test", 245 Required: true, 246 Nested: &Spec{ 247 Block: &Spec_BlockValue{ 248 BlockValue: &Block{ 249 Name: "test", 250 Required: true, 251 Nested: &Spec{ 252 Block: &Spec_Attr{ 253 &Attr{ 254 Name: "foo", 255 Type: "string", 256 }, 257 }, 258 }, 259 }, 260 }, 261 }, 262 }, 263 }, 264 }, 265 Expected: &hcldec.BlockSpec{ 266 TypeName: "test", 267 Required: true, 268 Nested: &hcldec.BlockSpec{ 269 TypeName: "test", 270 Required: true, 271 Nested: &hcldec.AttrSpec{ 272 Name: "foo", 273 Type: cty.String, 274 Required: false, 275 }, 276 }, 277 }, 278 }, 279 } 280 281 testSpecConversions(t, tests) 282 } 283 284 func TestDec_Convert_BlockList(t *testing.T) { 285 t.Parallel() 286 287 tests := []testConversions{ 288 { 289 Name: "block list with attr", 290 Input: &Spec{ 291 Block: &Spec_BlockList{ 292 BlockList: &BlockList{ 293 Name: "test", 294 MinItems: 1, 295 MaxItems: 3, 296 Nested: &Spec{ 297 Block: &Spec_Attr{ 298 &Attr{ 299 Name: "foo", 300 Type: "string", 301 }, 302 }, 303 }, 304 }, 305 }, 306 }, 307 Expected: &hcldec.BlockListSpec{ 308 TypeName: "test", 309 MinItems: 1, 310 MaxItems: 3, 311 Nested: &hcldec.AttrSpec{ 312 Name: "foo", 313 Type: cty.String, 314 Required: false, 315 }, 316 }, 317 }, 318 { 319 Name: "block list no name", 320 Input: &Spec{ 321 Block: &Spec_BlockList{ 322 BlockList: &BlockList{ 323 MinItems: 1, 324 MaxItems: 3, 325 Nested: &Spec{ 326 Block: &Spec_Attr{ 327 &Attr{ 328 Name: "foo", 329 Type: "string", 330 }, 331 }, 332 }, 333 }, 334 }, 335 }, 336 ExpectedError: "Missing name in block_list spec", 337 }, 338 } 339 340 testSpecConversions(t, tests) 341 } 342 343 func TestDec_Convert_BlockSet(t *testing.T) { 344 t.Parallel() 345 346 tests := []testConversions{ 347 { 348 Name: "block set with attr", 349 Input: &Spec{ 350 Block: &Spec_BlockSet{ 351 BlockSet: &BlockSet{ 352 Name: "test", 353 MinItems: 1, 354 MaxItems: 3, 355 Nested: &Spec{ 356 Block: &Spec_Attr{ 357 &Attr{ 358 Name: "foo", 359 Type: "string", 360 }, 361 }, 362 }, 363 }, 364 }, 365 }, 366 Expected: &hcldec.BlockSetSpec{ 367 TypeName: "test", 368 MinItems: 1, 369 MaxItems: 3, 370 Nested: &hcldec.AttrSpec{ 371 Name: "foo", 372 Type: cty.String, 373 Required: false, 374 }, 375 }, 376 }, 377 { 378 Name: "block set missing name", 379 Input: &Spec{ 380 Block: &Spec_BlockSet{ 381 BlockSet: &BlockSet{ 382 MinItems: 1, 383 MaxItems: 3, 384 Nested: &Spec{ 385 Block: &Spec_Attr{ 386 &Attr{ 387 Name: "foo", 388 Type: "string", 389 }, 390 }, 391 }, 392 }, 393 }, 394 }, 395 ExpectedError: "Missing name in block_set spec", 396 }, 397 } 398 399 testSpecConversions(t, tests) 400 } 401 402 func TestDec_Convert_BlockMap(t *testing.T) { 403 t.Parallel() 404 405 tests := []testConversions{ 406 { 407 Name: "block map with attr", 408 Input: &Spec{ 409 Block: &Spec_BlockMap{ 410 BlockMap: &BlockMap{ 411 Name: "test", 412 Labels: []string{"key1", "key2"}, 413 Nested: &Spec{ 414 Block: &Spec_Attr{ 415 &Attr{ 416 Name: "foo", 417 Type: "string", 418 }, 419 }, 420 }, 421 }, 422 }, 423 }, 424 Expected: &hcldec.BlockMapSpec{ 425 TypeName: "test", 426 LabelNames: []string{"key1", "key2"}, 427 Nested: &hcldec.AttrSpec{ 428 Name: "foo", 429 Type: cty.String, 430 Required: false, 431 }, 432 }, 433 }, 434 { 435 Name: "block map missing name", 436 Input: &Spec{ 437 Block: &Spec_BlockMap{ 438 BlockMap: &BlockMap{ 439 Labels: []string{"key1", "key2"}, 440 Nested: &Spec{ 441 Block: &Spec_Attr{ 442 &Attr{ 443 Name: "foo", 444 Type: "string", 445 }, 446 }, 447 }, 448 }, 449 }, 450 }, 451 ExpectedError: "Missing name in block_map spec", 452 }, 453 { 454 Name: "block map missing labels", 455 Input: &Spec{ 456 Block: &Spec_BlockMap{ 457 BlockMap: &BlockMap{ 458 Name: "foo", 459 Nested: &Spec{ 460 Block: &Spec_Attr{ 461 &Attr{ 462 Name: "foo", 463 Type: "string", 464 }, 465 }, 466 }, 467 }, 468 }, 469 }, 470 ExpectedError: "Invalid block label name list", 471 }, 472 } 473 474 testSpecConversions(t, tests) 475 } 476 477 func TestDec_Convert_Default(t *testing.T) { 478 t.Parallel() 479 480 tests := []testConversions{ 481 { 482 Name: "default attr", 483 Input: &Spec{ 484 Block: &Spec_Default{ 485 Default: &Default{ 486 Primary: &Spec{ 487 Block: &Spec_Attr{ 488 &Attr{ 489 Name: "foo", 490 Type: "string", 491 Required: true, 492 }, 493 }, 494 }, 495 Default: &Spec{ 496 Block: &Spec_Literal{ 497 &Literal{ 498 Value: "\"hi\"", 499 }, 500 }, 501 }, 502 }, 503 }, 504 }, 505 Expected: &hcldec.DefaultSpec{ 506 Primary: &hcldec.AttrSpec{ 507 Name: "foo", 508 Type: cty.String, 509 Required: true, 510 }, 511 Default: &hcldec.LiteralSpec{ 512 Value: cty.StringVal("hi"), 513 }, 514 }, 515 }, 516 } 517 518 testSpecConversions(t, tests) 519 } 520 521 func TestDec_Convert_Literal(t *testing.T) { 522 t.Parallel() 523 524 tests := []testConversions{ 525 { 526 Name: "bool: true", 527 Input: &Spec{ 528 Block: &Spec_Literal{ 529 Literal: &Literal{ 530 Value: "true", 531 }, 532 }, 533 }, 534 Expected: &hcldec.LiteralSpec{ 535 Value: cty.BoolVal(true), 536 }, 537 }, 538 { 539 Name: "bool: false", 540 Input: &Spec{ 541 Block: &Spec_Literal{ 542 Literal: &Literal{ 543 Value: "false", 544 }, 545 }, 546 }, 547 Expected: &hcldec.LiteralSpec{ 548 Value: cty.BoolVal(false), 549 }, 550 }, 551 { 552 Name: "string", 553 Input: &Spec{ 554 Block: &Spec_Literal{ 555 Literal: &Literal{ 556 Value: "\"hi\"", 557 }, 558 }, 559 }, 560 Expected: &hcldec.LiteralSpec{ 561 Value: cty.StringVal("hi"), 562 }, 563 }, 564 { 565 Name: "string w/ func", 566 Input: &Spec{ 567 Block: &Spec_Literal{ 568 Literal: &Literal{ 569 Value: "reverse(\"hi\")", 570 }, 571 }, 572 }, 573 Expected: &hcldec.LiteralSpec{ 574 Value: cty.StringVal("ih"), 575 }, 576 }, 577 { 578 Name: "list string", 579 Input: &Spec{ 580 Block: &Spec_Literal{ 581 Literal: &Literal{ 582 Value: "[\"hi\", \"bye\"]", 583 }, 584 }, 585 }, 586 Expected: &hcldec.LiteralSpec{ 587 Value: cty.TupleVal([]cty.Value{cty.StringVal("hi"), cty.StringVal("bye")}), 588 }, 589 }, 590 } 591 592 testSpecConversions(t, tests) 593 }