github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/config/config_test.go (about)

     1  package config
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/hashicorp/consul-template/config"
     8  	"github.com/hashicorp/nomad/ci"
     9  	"github.com/hashicorp/nomad/helper/pointer"
    10  	"github.com/stretchr/testify/require"
    11  )
    12  
    13  func TestConfigRead(t *testing.T) {
    14  	ci.Parallel(t)
    15  
    16  	config := Config{}
    17  
    18  	actual := config.Read("cake")
    19  	if actual != "" {
    20  		t.Errorf("Expected empty string; found %s", actual)
    21  	}
    22  
    23  	expected := "chocolate"
    24  	config.Options = map[string]string{"cake": expected}
    25  	actual = config.Read("cake")
    26  	if actual != expected {
    27  		t.Errorf("Expected %s, found %s", expected, actual)
    28  	}
    29  }
    30  
    31  func TestConfigReadDefault(t *testing.T) {
    32  	ci.Parallel(t)
    33  
    34  	config := Config{}
    35  
    36  	expected := "vanilla"
    37  	actual := config.ReadDefault("cake", expected)
    38  	if actual != expected {
    39  		t.Errorf("Expected %s, found %s", expected, actual)
    40  	}
    41  
    42  	expected = "chocolate"
    43  	config.Options = map[string]string{"cake": expected}
    44  	actual = config.ReadDefault("cake", "vanilla")
    45  	if actual != expected {
    46  		t.Errorf("Expected %s, found %s", expected, actual)
    47  	}
    48  }
    49  
    50  func mockWaitConfig() *WaitConfig {
    51  	return &WaitConfig{
    52  		Min: pointer.Of(5 * time.Second),
    53  		Max: pointer.Of(10 * time.Second),
    54  	}
    55  }
    56  
    57  func TestWaitConfig_Copy(t *testing.T) {
    58  	ci.Parallel(t)
    59  
    60  	cases := []struct {
    61  		Name     string
    62  		Wait     *WaitConfig
    63  		Expected *WaitConfig
    64  	}{
    65  		{
    66  			"fully-populated",
    67  			mockWaitConfig(),
    68  			&WaitConfig{
    69  				Min: pointer.Of(5 * time.Second),
    70  				Max: pointer.Of(10 * time.Second),
    71  			},
    72  		},
    73  		{
    74  			"min-only",
    75  			&WaitConfig{
    76  				Min: pointer.Of(5 * time.Second),
    77  			},
    78  			&WaitConfig{
    79  				Min: pointer.Of(5 * time.Second),
    80  			},
    81  		},
    82  		{
    83  			"max-only",
    84  			&WaitConfig{
    85  				Max: pointer.Of(5 * time.Second),
    86  			},
    87  			&WaitConfig{
    88  				Max: pointer.Of(5 * time.Second),
    89  			},
    90  		},
    91  	}
    92  
    93  	for _, _case := range cases {
    94  		t.Run(_case.Name, func(t *testing.T) {
    95  			result := _case.Expected.Equal(_case.Wait.Copy())
    96  			if !result {
    97  				t.Logf("\nExpected %v\n   Found %v", _case.Expected, result)
    98  			}
    99  			require.True(t, result)
   100  		})
   101  	}
   102  }
   103  
   104  func TestWaitConfig_IsEmpty(t *testing.T) {
   105  	ci.Parallel(t)
   106  
   107  	cases := []struct {
   108  		Name     string
   109  		Wait     *WaitConfig
   110  		Expected bool
   111  	}{
   112  		{
   113  			"is-nil",
   114  			nil,
   115  			true,
   116  		},
   117  		{
   118  			"is-empty",
   119  			&WaitConfig{},
   120  			true,
   121  		},
   122  		{
   123  			"is-not-empty",
   124  			&WaitConfig{
   125  				Min: pointer.Of(10 * time.Second),
   126  			},
   127  			false,
   128  		},
   129  	}
   130  
   131  	for _, _case := range cases {
   132  		t.Run(_case.Name, func(t *testing.T) {
   133  			require.Equal(t, _case.Expected, _case.Wait.IsEmpty())
   134  		})
   135  	}
   136  }
   137  
   138  func TestWaitConfig_IsEqual(t *testing.T) {
   139  	ci.Parallel(t)
   140  
   141  	cases := []struct {
   142  		Name     string
   143  		Wait     *WaitConfig
   144  		Other    *WaitConfig
   145  		Expected bool
   146  	}{
   147  		{
   148  			"are-equal",
   149  			mockWaitConfig(),
   150  			&WaitConfig{
   151  				Min: pointer.Of(5 * time.Second),
   152  				Max: pointer.Of(10 * time.Second),
   153  			},
   154  			true,
   155  		},
   156  		{
   157  			"min-different",
   158  			mockWaitConfig(),
   159  			&WaitConfig{
   160  				Min: pointer.Of(4 * time.Second),
   161  				Max: pointer.Of(10 * time.Second),
   162  			},
   163  			false,
   164  		},
   165  		{
   166  			"max-different",
   167  			mockWaitConfig(),
   168  			&WaitConfig{
   169  				Min: pointer.Of(5 * time.Second),
   170  				Max: pointer.Of(9 * time.Second),
   171  			},
   172  			false,
   173  		},
   174  	}
   175  
   176  	for _, _case := range cases {
   177  		t.Run(_case.Name, func(t *testing.T) {
   178  			require.Equal(t, _case.Expected, _case.Wait.Equal(_case.Other))
   179  		})
   180  	}
   181  }
   182  
   183  func TestWaitConfig_IsValid(t *testing.T) {
   184  	ci.Parallel(t)
   185  
   186  	cases := []struct {
   187  		Name     string
   188  		Retry    *WaitConfig
   189  		Expected string
   190  	}{
   191  		{
   192  			"is-valid",
   193  			&WaitConfig{
   194  				Min: pointer.Of(5 * time.Second),
   195  				Max: pointer.Of(10 * time.Second),
   196  			},
   197  			"",
   198  		},
   199  		{
   200  			"is-nil",
   201  			nil,
   202  			"is nil",
   203  		},
   204  		{
   205  			"is-empty",
   206  			&WaitConfig{},
   207  			"or empty",
   208  		},
   209  		{
   210  			"min-greater-than-max",
   211  			&WaitConfig{
   212  				Min: pointer.Of(10 * time.Second),
   213  				Max: pointer.Of(5 * time.Second),
   214  			},
   215  			"greater than",
   216  		},
   217  		{
   218  			"max-not-set",
   219  			&WaitConfig{
   220  				Min: pointer.Of(10 * time.Second),
   221  			},
   222  			"",
   223  		},
   224  	}
   225  
   226  	for _, _case := range cases {
   227  		t.Run(_case.Name, func(t *testing.T) {
   228  			if _case.Expected == "" {
   229  				require.Nil(t, _case.Retry.Validate())
   230  			} else {
   231  				err := _case.Retry.Validate()
   232  				require.Contains(t, err.Error(), _case.Expected)
   233  			}
   234  		})
   235  	}
   236  }
   237  
   238  func TestWaitConfig_Merge(t *testing.T) {
   239  	ci.Parallel(t)
   240  
   241  	cases := []struct {
   242  		Name     string
   243  		Target   *WaitConfig
   244  		Other    *WaitConfig
   245  		Expected *WaitConfig
   246  	}{
   247  		{
   248  			"all-fields",
   249  			mockWaitConfig(),
   250  			&WaitConfig{
   251  				Min: pointer.Of(4 * time.Second),
   252  				Max: pointer.Of(9 * time.Second),
   253  			},
   254  			&WaitConfig{
   255  				Min: pointer.Of(4 * time.Second),
   256  				Max: pointer.Of(9 * time.Second),
   257  			},
   258  		},
   259  		{
   260  			"min-only",
   261  			mockWaitConfig(),
   262  			&WaitConfig{
   263  				Min: pointer.Of(4 * time.Second),
   264  				Max: pointer.Of(10 * time.Second),
   265  			},
   266  			&WaitConfig{
   267  				Min: pointer.Of(4 * time.Second),
   268  				Max: pointer.Of(10 * time.Second),
   269  			},
   270  		},
   271  		{
   272  			"max-only",
   273  			mockWaitConfig(),
   274  			&WaitConfig{
   275  				Min: pointer.Of(5 * time.Second),
   276  				Max: pointer.Of(9 * time.Second),
   277  			},
   278  			&WaitConfig{
   279  				Min: pointer.Of(5 * time.Second),
   280  				Max: pointer.Of(9 * time.Second),
   281  			},
   282  		},
   283  	}
   284  
   285  	for _, _case := range cases {
   286  		t.Run(_case.Name, func(t *testing.T) {
   287  			merged := _case.Target.Merge(_case.Other)
   288  			result := _case.Expected.Equal(merged)
   289  			if !result {
   290  				t.Logf("\nExpected %v\n   Found %v", _case.Expected, merged)
   291  			}
   292  			require.True(t, result)
   293  		})
   294  	}
   295  }
   296  
   297  func TestWaitConfig_ToConsulTemplate(t *testing.T) {
   298  	ci.Parallel(t)
   299  
   300  	expected := config.WaitConfig{
   301  		Enabled: pointer.Of(true),
   302  		Min:     pointer.Of(5 * time.Second),
   303  		Max:     pointer.Of(10 * time.Second),
   304  	}
   305  
   306  	clientWaitConfig := &WaitConfig{
   307  		Min: pointer.Of(5 * time.Second),
   308  		Max: pointer.Of(10 * time.Second),
   309  	}
   310  
   311  	actual, err := clientWaitConfig.ToConsulTemplate()
   312  	require.NoError(t, err)
   313  	require.Equal(t, *expected.Min, *actual.Min)
   314  	require.Equal(t, *expected.Max, *actual.Max)
   315  }
   316  
   317  func mockRetryConfig() *RetryConfig {
   318  	return &RetryConfig{
   319  		Attempts:      pointer.Of(5),
   320  		Backoff:       pointer.Of(5 * time.Second),
   321  		BackoffHCL:    "5s",
   322  		MaxBackoff:    pointer.Of(10 * time.Second),
   323  		MaxBackoffHCL: "10s",
   324  	}
   325  }
   326  func TestRetryConfig_Copy(t *testing.T) {
   327  	ci.Parallel(t)
   328  
   329  	cases := []struct {
   330  		Name     string
   331  		Retry    *RetryConfig
   332  		Expected *RetryConfig
   333  	}{
   334  		{
   335  			"fully-populated",
   336  			mockRetryConfig(),
   337  			&RetryConfig{
   338  				Attempts:      pointer.Of(5),
   339  				Backoff:       pointer.Of(5 * time.Second),
   340  				BackoffHCL:    "5s",
   341  				MaxBackoff:    pointer.Of(10 * time.Second),
   342  				MaxBackoffHCL: "10s",
   343  			},
   344  		},
   345  		{
   346  			"attempts-only",
   347  			&RetryConfig{
   348  				Attempts: pointer.Of(5),
   349  			},
   350  			&RetryConfig{
   351  				Attempts: pointer.Of(5),
   352  			},
   353  		},
   354  		{
   355  			"backoff-only",
   356  			&RetryConfig{
   357  				Backoff: pointer.Of(5 * time.Second),
   358  			},
   359  			&RetryConfig{
   360  				Backoff: pointer.Of(5 * time.Second),
   361  			},
   362  		},
   363  		{
   364  			"backoff-hcl-only",
   365  			&RetryConfig{
   366  				BackoffHCL: "5s",
   367  			},
   368  			&RetryConfig{
   369  				BackoffHCL: "5s",
   370  			},
   371  		},
   372  		{
   373  			"max-backoff-only",
   374  			&RetryConfig{
   375  				MaxBackoff: pointer.Of(10 * time.Second),
   376  			},
   377  			&RetryConfig{
   378  				MaxBackoff: pointer.Of(10 * time.Second),
   379  			},
   380  		},
   381  		{
   382  			"max-backoff-hcl-only",
   383  			&RetryConfig{
   384  				MaxBackoffHCL: "10s",
   385  			},
   386  			&RetryConfig{
   387  				MaxBackoffHCL: "10s",
   388  			},
   389  		},
   390  	}
   391  
   392  	for _, _case := range cases {
   393  		t.Run(_case.Name, func(t *testing.T) {
   394  			result := _case.Expected.Equal(_case.Retry.Copy())
   395  			if !result {
   396  				t.Logf("\nExpected %v\n   Found %v", _case.Expected, result)
   397  			}
   398  			require.True(t, result)
   399  		})
   400  	}
   401  }
   402  
   403  func TestRetryConfig_IsEmpty(t *testing.T) {
   404  	ci.Parallel(t)
   405  
   406  	cases := []struct {
   407  		Name     string
   408  		Retry    *RetryConfig
   409  		Expected bool
   410  	}{
   411  		{
   412  			"is-nil",
   413  			nil,
   414  			true,
   415  		},
   416  		{
   417  			"is-empty",
   418  			&RetryConfig{},
   419  			true,
   420  		},
   421  		{
   422  			"is-not-empty",
   423  			&RetryConfig{
   424  				Attempts: pointer.Of(12),
   425  			},
   426  			false,
   427  		},
   428  	}
   429  
   430  	for _, _case := range cases {
   431  		t.Run(_case.Name, func(t *testing.T) {
   432  			require.Equal(t, _case.Expected, _case.Retry.IsEmpty())
   433  		})
   434  	}
   435  }
   436  
   437  func TestRetryConfig_IsEqual(t *testing.T) {
   438  	ci.Parallel(t)
   439  
   440  	cases := []struct {
   441  		Name     string
   442  		Retry    *RetryConfig
   443  		Other    *RetryConfig
   444  		Expected bool
   445  	}{
   446  		{
   447  			"are-equal",
   448  			mockRetryConfig(),
   449  			&RetryConfig{
   450  				Attempts:      pointer.Of(5),
   451  				Backoff:       pointer.Of(5 * time.Second),
   452  				BackoffHCL:    "5s",
   453  				MaxBackoff:    pointer.Of(10 * time.Second),
   454  				MaxBackoffHCL: "10s",
   455  			},
   456  			true,
   457  		},
   458  		{
   459  			"attempts-different",
   460  			mockRetryConfig(),
   461  			&RetryConfig{
   462  				Attempts:      pointer.Of(4),
   463  				Backoff:       pointer.Of(5 * time.Second),
   464  				BackoffHCL:    "5s",
   465  				MaxBackoff:    pointer.Of(10 * time.Second),
   466  				MaxBackoffHCL: "10s",
   467  			},
   468  			false,
   469  		},
   470  		{
   471  			"backoff-different",
   472  			mockRetryConfig(),
   473  			&RetryConfig{
   474  				Attempts:      pointer.Of(5),
   475  				Backoff:       pointer.Of(4 * time.Second),
   476  				BackoffHCL:    "5s",
   477  				MaxBackoff:    pointer.Of(10 * time.Second),
   478  				MaxBackoffHCL: "10s",
   479  			},
   480  			false,
   481  		},
   482  		{
   483  			"backoff-hcl-different",
   484  			mockRetryConfig(),
   485  			&RetryConfig{
   486  				Attempts:      pointer.Of(5),
   487  				Backoff:       pointer.Of(5 * time.Second),
   488  				BackoffHCL:    "4s",
   489  				MaxBackoff:    pointer.Of(10 * time.Second),
   490  				MaxBackoffHCL: "10s",
   491  			},
   492  			false,
   493  		},
   494  		{
   495  			"max-backoff-different",
   496  			mockRetryConfig(),
   497  			&RetryConfig{
   498  				Attempts:      pointer.Of(5),
   499  				Backoff:       pointer.Of(5 * time.Second),
   500  				BackoffHCL:    "5s",
   501  				MaxBackoff:    pointer.Of(9 * time.Second),
   502  				MaxBackoffHCL: "10s",
   503  			},
   504  			false,
   505  		},
   506  		{
   507  			"max-backoff-hcl-different",
   508  			mockRetryConfig(),
   509  			&RetryConfig{
   510  				Attempts:      pointer.Of(5),
   511  				Backoff:       pointer.Of(5 * time.Second),
   512  				BackoffHCL:    "5s",
   513  				MaxBackoff:    pointer.Of(10 * time.Second),
   514  				MaxBackoffHCL: "9s",
   515  			},
   516  			false,
   517  		},
   518  	}
   519  
   520  	for _, _case := range cases {
   521  		t.Run(_case.Name, func(t *testing.T) {
   522  			require.Equal(t, _case.Expected, _case.Retry.Equal(_case.Other))
   523  		})
   524  	}
   525  }
   526  
   527  func TestRetryConfig_IsValid(t *testing.T) {
   528  	ci.Parallel(t)
   529  
   530  	cases := []struct {
   531  		Name     string
   532  		Retry    *RetryConfig
   533  		Expected string
   534  	}{
   535  		{
   536  			"is-valid",
   537  			&RetryConfig{
   538  				Backoff:    pointer.Of(5 * time.Second),
   539  				MaxBackoff: pointer.Of(10 * time.Second),
   540  			},
   541  			"",
   542  		},
   543  		{
   544  			"is-nil",
   545  			nil,
   546  			"is nil",
   547  		},
   548  		{
   549  			"is-empty",
   550  			&RetryConfig{},
   551  			"or empty",
   552  		},
   553  		{
   554  			"backoff-greater-than-max-backoff",
   555  			&RetryConfig{
   556  				Backoff:    pointer.Of(10 * time.Second),
   557  				MaxBackoff: pointer.Of(5 * time.Second),
   558  			},
   559  			"greater than max_backoff",
   560  		},
   561  		{
   562  			"backoff-not-set",
   563  			&RetryConfig{
   564  				MaxBackoff: pointer.Of(10 * time.Second),
   565  			},
   566  			"",
   567  		},
   568  		{
   569  			"max-backoff-not-set",
   570  			&RetryConfig{
   571  				Backoff: pointer.Of(2 * time.Minute),
   572  			},
   573  			"greater than default",
   574  		},
   575  		{
   576  			"max-backoff-unbounded",
   577  			&RetryConfig{
   578  				Backoff:    pointer.Of(10 * time.Second),
   579  				MaxBackoff: pointer.Of(0 * time.Second),
   580  			},
   581  			"",
   582  		},
   583  	}
   584  
   585  	for _, _case := range cases {
   586  		t.Run(_case.Name, func(t *testing.T) {
   587  			if _case.Expected == "" {
   588  				require.Nil(t, _case.Retry.Validate())
   589  			} else {
   590  				err := _case.Retry.Validate()
   591  				require.Contains(t, err.Error(), _case.Expected)
   592  			}
   593  		})
   594  	}
   595  }
   596  
   597  func TestRetryConfig_Merge(t *testing.T) {
   598  	ci.Parallel(t)
   599  
   600  	cases := []struct {
   601  		Name     string
   602  		Target   *RetryConfig
   603  		Other    *RetryConfig
   604  		Expected *RetryConfig
   605  	}{
   606  		{
   607  			"all-fields",
   608  			mockRetryConfig(),
   609  			&RetryConfig{
   610  				Attempts:      pointer.Of(4),
   611  				Backoff:       pointer.Of(4 * time.Second),
   612  				BackoffHCL:    "4s",
   613  				MaxBackoff:    pointer.Of(9 * time.Second),
   614  				MaxBackoffHCL: "9s",
   615  			},
   616  			&RetryConfig{
   617  				Attempts:      pointer.Of(4),
   618  				Backoff:       pointer.Of(4 * time.Second),
   619  				BackoffHCL:    "4s",
   620  				MaxBackoff:    pointer.Of(9 * time.Second),
   621  				MaxBackoffHCL: "9s",
   622  			},
   623  		},
   624  		{
   625  			"attempts-only",
   626  			mockRetryConfig(),
   627  			&RetryConfig{
   628  				Attempts:      pointer.Of(4),
   629  				Backoff:       pointer.Of(5 * time.Second),
   630  				BackoffHCL:    "5s",
   631  				MaxBackoff:    pointer.Of(10 * time.Second),
   632  				MaxBackoffHCL: "10s",
   633  			},
   634  			&RetryConfig{
   635  				Attempts:      pointer.Of(4),
   636  				Backoff:       pointer.Of(5 * time.Second),
   637  				BackoffHCL:    "5s",
   638  				MaxBackoff:    pointer.Of(10 * time.Second),
   639  				MaxBackoffHCL: "10s",
   640  			},
   641  		},
   642  		{
   643  			"multi-field",
   644  			mockRetryConfig(),
   645  			&RetryConfig{
   646  				Attempts:      pointer.Of(5),
   647  				Backoff:       pointer.Of(4 * time.Second),
   648  				BackoffHCL:    "4s",
   649  				MaxBackoff:    pointer.Of(9 * time.Second),
   650  				MaxBackoffHCL: "9s",
   651  			},
   652  			&RetryConfig{
   653  				Attempts:      pointer.Of(5),
   654  				Backoff:       pointer.Of(4 * time.Second),
   655  				BackoffHCL:    "4s",
   656  				MaxBackoff:    pointer.Of(9 * time.Second),
   657  				MaxBackoffHCL: "9s",
   658  			},
   659  		},
   660  	}
   661  
   662  	for _, _case := range cases {
   663  		t.Run(_case.Name, func(t *testing.T) {
   664  			merged := _case.Target.Merge(_case.Other)
   665  			result := _case.Expected.Equal(merged)
   666  			if !result {
   667  				t.Logf("\nExpected %v\n   Found %v", _case.Expected, merged)
   668  			}
   669  			require.True(t, result)
   670  		})
   671  	}
   672  }
   673  
   674  func TestRetryConfig_ToConsulTemplate(t *testing.T) {
   675  	ci.Parallel(t)
   676  
   677  	expected := config.RetryConfig{
   678  		Enabled:    pointer.Of(true),
   679  		Attempts:   pointer.Of(5),
   680  		Backoff:    pointer.Of(5 * time.Second),
   681  		MaxBackoff: pointer.Of(10 * time.Second),
   682  	}
   683  
   684  	actual := mockRetryConfig()
   685  
   686  	require.Equal(t, *expected.Attempts, *actual.Attempts)
   687  	require.Equal(t, *expected.Backoff, *actual.Backoff)
   688  	require.Equal(t, *expected.MaxBackoff, *actual.MaxBackoff)
   689  }