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