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  }