github.com/smithx10/nomad@v0.9.1-rc1/client/taskenv/util_test.go (about)

     1  package taskenv
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/require"
     8  	"github.com/zclconf/go-cty/cty"
     9  )
    10  
    11  // TestAddNestedKey_Ok asserts test cases that succeed when passed to
    12  // addNestedKey.
    13  func TestAddNestedKey_Ok(t *testing.T) {
    14  	cases := []struct {
    15  		// M will be initialized if unset
    16  		M map[string]interface{}
    17  		K string
    18  		// Value is always "x"
    19  		Result map[string]interface{}
    20  	}{
    21  		{
    22  			K: "foo",
    23  			Result: map[string]interface{}{
    24  				"foo": "x",
    25  			},
    26  		},
    27  		{
    28  			K: "foo.bar",
    29  			Result: map[string]interface{}{
    30  				"foo": map[string]interface{}{
    31  					"bar": "x",
    32  				},
    33  			},
    34  		},
    35  		{
    36  			K: "foo.bar.quux",
    37  			Result: map[string]interface{}{
    38  				"foo": map[string]interface{}{
    39  					"bar": map[string]interface{}{
    40  						"quux": "x",
    41  					},
    42  				},
    43  			},
    44  		},
    45  		{
    46  			K: "a.b.c",
    47  			Result: map[string]interface{}{
    48  				"a": map[string]interface{}{
    49  					"b": map[string]interface{}{
    50  						"c": "x",
    51  					},
    52  				},
    53  			},
    54  		},
    55  		{
    56  			// Nested object b should take precedence over values
    57  			M: map[string]interface{}{
    58  				"a": map[string]interface{}{
    59  					"b": map[string]interface{}{
    60  						"c": "c",
    61  					},
    62  				},
    63  			},
    64  			K: "a.b",
    65  			Result: map[string]interface{}{
    66  				"a": map[string]interface{}{
    67  					"b": map[string]interface{}{
    68  						"c": "c",
    69  					},
    70  				},
    71  			},
    72  		},
    73  		{
    74  			M: map[string]interface{}{
    75  				"a": map[string]interface{}{
    76  					"x": "x",
    77  				},
    78  				"z": "z",
    79  			},
    80  			K: "a.b.c",
    81  			Result: map[string]interface{}{
    82  				"a": map[string]interface{}{
    83  					"b": map[string]interface{}{
    84  						"c": "x",
    85  					},
    86  					"x": "x",
    87  				},
    88  				"z": "z",
    89  			},
    90  		},
    91  		{
    92  			M: map[string]interface{}{
    93  				"foo": map[string]interface{}{
    94  					"bar": map[string]interface{}{
    95  						"a":    "z",
    96  						"quux": "z",
    97  					},
    98  				},
    99  			},
   100  			K: "foo.bar.quux",
   101  			Result: map[string]interface{}{
   102  				"foo": map[string]interface{}{
   103  					"bar": map[string]interface{}{
   104  						"a":    "z",
   105  						"quux": "x",
   106  					},
   107  				},
   108  			},
   109  		},
   110  		{
   111  			M: map[string]interface{}{
   112  				"foo":  "1",
   113  				"bar":  "2",
   114  				"quux": "3",
   115  			},
   116  			K: "a.bbbbbb.c",
   117  			Result: map[string]interface{}{
   118  				"foo":  "1",
   119  				"bar":  "2",
   120  				"quux": "3",
   121  				"a": map[string]interface{}{
   122  					"bbbbbb": map[string]interface{}{
   123  						"c": "x",
   124  					},
   125  				},
   126  			},
   127  		},
   128  		// Regardless of whether attr.driver.qemu = "1" is added first
   129  		// or second, attr.driver.qemu.version = "..." should take
   130  		// precedence (nested maps take precedence over values)
   131  		{
   132  			M: map[string]interface{}{
   133  				"attr": map[string]interface{}{
   134  					"driver": map[string]interface{}{
   135  						"qemu": "1",
   136  					},
   137  				},
   138  			},
   139  			K: "attr.driver.qemu.version",
   140  			Result: map[string]interface{}{
   141  				"attr": map[string]interface{}{
   142  					"driver": map[string]interface{}{
   143  						"qemu": map[string]interface{}{
   144  							"version": "x",
   145  						},
   146  					},
   147  				},
   148  			},
   149  		},
   150  		{
   151  			M: map[string]interface{}{
   152  				"attr": map[string]interface{}{
   153  					"driver": map[string]interface{}{
   154  						"qemu": map[string]interface{}{
   155  							"version": "1.2.3",
   156  						},
   157  					},
   158  				},
   159  			},
   160  			K: "attr.driver.qemu",
   161  			Result: map[string]interface{}{
   162  				"attr": map[string]interface{}{
   163  					"driver": map[string]interface{}{
   164  						"qemu": map[string]interface{}{
   165  							"version": "1.2.3",
   166  						},
   167  					},
   168  				},
   169  			},
   170  		},
   171  		{
   172  			M: map[string]interface{}{
   173  				"a": "a",
   174  			},
   175  			K: "a.b",
   176  			Result: map[string]interface{}{
   177  				"a": map[string]interface{}{
   178  					"b": "x",
   179  				},
   180  			},
   181  		},
   182  		{
   183  			M: map[string]interface{}{
   184  				"a": "a",
   185  				"foo": map[string]interface{}{
   186  					"b":   "b",
   187  					"bar": "quux",
   188  				},
   189  				"c": map[string]interface{}{},
   190  			},
   191  			K: "foo.bar.quux",
   192  			Result: map[string]interface{}{
   193  				"a": "a",
   194  				"foo": map[string]interface{}{
   195  					"b": "b",
   196  					"bar": map[string]interface{}{
   197  						"quux": "x",
   198  					},
   199  				},
   200  				"c": map[string]interface{}{},
   201  			},
   202  		},
   203  	}
   204  
   205  	for i := range cases {
   206  		tc := cases[i]
   207  		name := tc.K
   208  		if len(tc.M) > 0 {
   209  			name = fmt.Sprintf("%s-%d", name, len(tc.M))
   210  		}
   211  		t.Run(name, func(t *testing.T) {
   212  			t.Parallel()
   213  			if tc.M == nil {
   214  				tc.M = map[string]interface{}{}
   215  			}
   216  			require.NoError(t, addNestedKey(tc.M, tc.K, "x"))
   217  			require.Equal(t, tc.Result, tc.M)
   218  		})
   219  	}
   220  }
   221  
   222  // TestAddNestedKey_Bad asserts test cases return an error when passed to
   223  // addNestedKey.
   224  func TestAddNestedKey_Bad(t *testing.T) {
   225  	cases := []struct {
   226  		// M will be initialized if unset
   227  		M func() map[string]interface{}
   228  		K string
   229  		// Value is always "x"
   230  		// Result is compared by Error() string equality
   231  		Result error
   232  	}{
   233  		{
   234  			K:      ".",
   235  			Result: ErrInvalidObjectPath,
   236  		},
   237  		{
   238  			K:      ".foo",
   239  			Result: ErrInvalidObjectPath,
   240  		},
   241  		{
   242  			K:      "foo.",
   243  			Result: ErrInvalidObjectPath,
   244  		},
   245  		{
   246  			K:      ".a.",
   247  			Result: ErrInvalidObjectPath,
   248  		},
   249  		{
   250  			K:      "foo..bar",
   251  			Result: ErrInvalidObjectPath,
   252  		},
   253  		{
   254  			K:      "foo...bar",
   255  			Result: ErrInvalidObjectPath,
   256  		},
   257  		{
   258  			K:      "foo.bar..quux",
   259  			Result: ErrInvalidObjectPath,
   260  		},
   261  		{
   262  			K:      "foo..bar.quux",
   263  			Result: ErrInvalidObjectPath,
   264  		},
   265  		{
   266  			K:      "foo.bar.quux.",
   267  			Result: ErrInvalidObjectPath,
   268  		},
   269  		{
   270  			M: func() map[string]interface{} {
   271  				return map[string]interface{}{
   272  					"a": "a",
   273  					"foo": map[string]interface{}{
   274  						"b": "b",
   275  						"bar": map[string]interface{}{
   276  							"c": "c",
   277  						},
   278  					},
   279  				}
   280  			},
   281  			K:      "foo.bar.quux.",
   282  			Result: ErrInvalidObjectPath,
   283  		},
   284  		{
   285  			M: func() map[string]interface{} {
   286  				return map[string]interface{}{
   287  					"a": "a",
   288  					"foo": map[string]interface{}{
   289  						"b": "b",
   290  						"bar": map[string]interface{}{
   291  							"c": "c",
   292  						},
   293  					},
   294  				}
   295  			},
   296  			K:      "foo.bar..quux",
   297  			Result: ErrInvalidObjectPath,
   298  		},
   299  		{
   300  			M: func() map[string]interface{} {
   301  				return map[string]interface{}{
   302  					"a": "a",
   303  					"foo": map[string]interface{}{
   304  						"b": "b",
   305  						"bar": map[string]interface{}{
   306  							"c": "c",
   307  						},
   308  					},
   309  				}
   310  			},
   311  			K:      "foo.bar..quux",
   312  			Result: ErrInvalidObjectPath,
   313  		},
   314  	}
   315  
   316  	for i := range cases {
   317  		tc := cases[i]
   318  		name := tc.K
   319  		if tc.M != nil {
   320  			name += "-cleanup"
   321  		}
   322  		t.Run(name, func(t *testing.T) {
   323  			t.Parallel()
   324  
   325  			// Copy original M value to ensure it doesn't get altered
   326  			if tc.M == nil {
   327  				tc.M = func() map[string]interface{} {
   328  					return map[string]interface{}{}
   329  				}
   330  			}
   331  
   332  			// Call func and assert error
   333  			m := tc.M()
   334  			err := addNestedKey(m, tc.K, "x")
   335  			require.EqualError(t, err, tc.Result.Error())
   336  
   337  			// Ensure M wasn't altered
   338  			require.Equal(t, tc.M(), m)
   339  		})
   340  	}
   341  }
   342  
   343  func TestCtyify_Ok(t *testing.T) {
   344  	cases := []struct {
   345  		Name string
   346  		In   map[string]interface{}
   347  		Out  map[string]cty.Value
   348  	}{
   349  		{
   350  			Name: "OneVal",
   351  			In: map[string]interface{}{
   352  				"a": "b",
   353  			},
   354  			Out: map[string]cty.Value{
   355  				"a": cty.StringVal("b"),
   356  			},
   357  		},
   358  		{
   359  			Name: "MultiVal",
   360  			In: map[string]interface{}{
   361  				"a":   "b",
   362  				"foo": "bar",
   363  			},
   364  			Out: map[string]cty.Value{
   365  				"a":   cty.StringVal("b"),
   366  				"foo": cty.StringVal("bar"),
   367  			},
   368  		},
   369  		{
   370  			Name: "NestedVals",
   371  			In: map[string]interface{}{
   372  				"a": "b",
   373  				"foo": map[string]interface{}{
   374  					"c": "d",
   375  					"bar": map[string]interface{}{
   376  						"quux": "z",
   377  					},
   378  				},
   379  				"123": map[string]interface{}{
   380  					"bar": map[string]interface{}{
   381  						"456": "789",
   382  					},
   383  				},
   384  			},
   385  			Out: map[string]cty.Value{
   386  				"a": cty.StringVal("b"),
   387  				"foo": cty.ObjectVal(map[string]cty.Value{
   388  					"c": cty.StringVal("d"),
   389  					"bar": cty.ObjectVal(map[string]cty.Value{
   390  						"quux": cty.StringVal("z"),
   391  					}),
   392  				}),
   393  				"123": cty.ObjectVal(map[string]cty.Value{
   394  					"bar": cty.ObjectVal(map[string]cty.Value{
   395  						"456": cty.StringVal("789"),
   396  					}),
   397  				}),
   398  			},
   399  		},
   400  	}
   401  
   402  	for i := range cases {
   403  		tc := cases[i]
   404  		t.Run(tc.Name, func(t *testing.T) {
   405  			t.Parallel()
   406  
   407  			// ctiyif and check for errors
   408  			result, err := ctyify(tc.In)
   409  			require.NoError(t, err)
   410  
   411  			// convert results to ObjectVals and compare with RawEquals
   412  			resultObj := cty.ObjectVal(result)
   413  			OutObj := cty.ObjectVal(tc.Out)
   414  			require.True(t, OutObj.RawEquals(resultObj))
   415  		})
   416  	}
   417  }
   418  
   419  func TestCtyify_Bad(t *testing.T) {
   420  	cases := []struct {
   421  		Name string
   422  		In   map[string]interface{}
   423  		Out  map[string]cty.Value
   424  	}{
   425  		{
   426  			Name: "NonStringVal",
   427  			In: map[string]interface{}{
   428  				"a": 1,
   429  			},
   430  		},
   431  		{
   432  			Name: "NestedNonString",
   433  			In: map[string]interface{}{
   434  				"foo": map[string]interface{}{
   435  					"c": 1,
   436  				},
   437  			},
   438  		},
   439  	}
   440  
   441  	for i := range cases {
   442  		tc := cases[i]
   443  		t.Run(tc.Name, func(t *testing.T) {
   444  			t.Parallel()
   445  
   446  			// ctiyif and check for errors
   447  			result, err := ctyify(tc.In)
   448  			require.Error(t, err)
   449  			require.Nil(t, result)
   450  		})
   451  	}
   452  }