github.com/banmanh482/nomad@v0.11.8/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  }