github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/validation/strings_test.go (about)

     1  package validation
     2  
     3  import (
     4  	"regexp"
     5  	"testing"
     6  )
     7  
     8  func TestValidationStringIsNotEmpty(t *testing.T) {
     9  	cases := map[string]struct {
    10  		Value interface{}
    11  		Error bool
    12  	}{
    13  		"NotString": {
    14  			Value: 7,
    15  			Error: true,
    16  		},
    17  		"Empty": {
    18  			Value: "",
    19  			Error: true,
    20  		},
    21  		"SingleSpace": {
    22  			Value: " ",
    23  			Error: false,
    24  		},
    25  		"MultipleSpaces": {
    26  			Value: "     ",
    27  			Error: false,
    28  		},
    29  		"NewLine": {
    30  			Value: "\n",
    31  			Error: false,
    32  		},
    33  		"MultipleSymbols": {
    34  			Value: "-_-",
    35  			Error: false,
    36  		},
    37  		"Sentence": {
    38  			Value: "Hello kt's sentence.",
    39  			Error: false,
    40  		},
    41  		"StartsWithWhitespace": {
    42  			Value: "  7",
    43  			Error: false,
    44  		},
    45  		"EndsWithWhitespace": {
    46  			Value: "7 ",
    47  			Error: false,
    48  		},
    49  	}
    50  
    51  	for tn, tc := range cases {
    52  		t.Run(tn, func(t *testing.T) {
    53  			_, errors := StringIsNotEmpty(tc.Value, tn)
    54  
    55  			if len(errors) > 0 && !tc.Error {
    56  				t.Errorf("StringIsNotEmpty(%s) produced an unexpected error", tc.Value)
    57  			} else if len(errors) == 0 && tc.Error {
    58  				t.Errorf("StringIsNotEmpty(%s) did not error", tc.Value)
    59  			}
    60  		})
    61  	}
    62  }
    63  
    64  func TestValidationStringIsNotWhitespace(t *testing.T) {
    65  	cases := map[string]struct {
    66  		Value interface{}
    67  		Error bool
    68  	}{
    69  		"NotString": {
    70  			Value: 7,
    71  			Error: true,
    72  		},
    73  		"Empty": {
    74  			Value: "",
    75  			Error: true,
    76  		},
    77  		"SingleSpace": {
    78  			Value: " ",
    79  			Error: true,
    80  		},
    81  		"MultipleSpaces": {
    82  			Value: "     ",
    83  			Error: true,
    84  		},
    85  		"CarriageReturn": {
    86  			Value: "\r",
    87  			Error: true,
    88  		},
    89  		"NewLine": {
    90  			Value: "\n",
    91  			Error: true,
    92  		},
    93  		"Tab": {
    94  			Value: "\t",
    95  			Error: true,
    96  		},
    97  		"FormFeed": {
    98  			Value: "\f",
    99  			Error: true,
   100  		},
   101  		"VerticalTab": {
   102  			Value: "\v",
   103  			Error: true,
   104  		},
   105  		"SingleChar": {
   106  			Value: "\v",
   107  			Error: true,
   108  		},
   109  		"MultipleChars": {
   110  			Value: "-_-",
   111  			Error: false,
   112  		},
   113  		"Sentence": {
   114  			Value: "Hello kt's sentence.",
   115  			Error: false,
   116  		},
   117  
   118  		"StartsWithWhitespace": {
   119  			Value: "  7",
   120  			Error: false,
   121  		},
   122  		"EndsWithWhitespace": {
   123  			Value: "7 ",
   124  			Error: false,
   125  		},
   126  	}
   127  
   128  	for tn, tc := range cases {
   129  		t.Run(tn, func(t *testing.T) {
   130  			_, errors := StringIsNotWhiteSpace(tc.Value, tn)
   131  
   132  			if len(errors) > 0 && !tc.Error {
   133  				t.Errorf("StringIsNotWhiteSpace(%s) produced an unexpected error", tc.Value)
   134  			} else if len(errors) == 0 && tc.Error {
   135  				t.Errorf("StringIsNotWhiteSpace(%s) did not error", tc.Value)
   136  			}
   137  		})
   138  	}
   139  }
   140  
   141  func TestValidationStringIsEmpty(t *testing.T) {
   142  	cases := map[string]struct {
   143  		Value interface{}
   144  		Error bool
   145  	}{
   146  		"NotString": {
   147  			Value: 7,
   148  			Error: true,
   149  		},
   150  		"Empty": {
   151  			Value: "",
   152  			Error: false,
   153  		},
   154  		"SingleSpace": {
   155  			Value: " ",
   156  			Error: true,
   157  		},
   158  		"MultipleSpaces": {
   159  			Value: "     ",
   160  			Error: true,
   161  		},
   162  		"Sentence": {
   163  			Value: "Hello kt's sentence.",
   164  			Error: true,
   165  		},
   166  
   167  		"StartsWithWhitespace": {
   168  			Value: "  7",
   169  			Error: true,
   170  		},
   171  		"EndsWithWhitespace": {
   172  			Value: "7 ",
   173  			Error: true,
   174  		},
   175  	}
   176  
   177  	for tn, tc := range cases {
   178  		t.Run(tn, func(t *testing.T) {
   179  			_, errors := StringIsEmpty(tc.Value, tn)
   180  
   181  			if len(errors) > 0 && !tc.Error {
   182  				t.Errorf("StringIsEmpty(%s) produced an unexpected error", tc.Value)
   183  			} else if len(errors) == 0 && tc.Error {
   184  				t.Errorf("StringIsEmpty(%s) did not error", tc.Value)
   185  			}
   186  		})
   187  	}
   188  }
   189  
   190  func TestValidationStringIsWhiteSpace(t *testing.T) {
   191  	cases := map[string]struct {
   192  		Value interface{}
   193  		Error bool
   194  	}{
   195  		"NotString": {
   196  			Value: 7,
   197  			Error: true,
   198  		},
   199  		"Empty": {
   200  			Value: "",
   201  			Error: false,
   202  		},
   203  		"SingleSpace": {
   204  			Value: " ",
   205  			Error: false,
   206  		},
   207  		"MultipleSpaces": {
   208  			Value: "     ",
   209  			Error: false,
   210  		},
   211  		"MultipleWhitespace": {
   212  			Value: "  \t\n\f   ",
   213  			Error: false,
   214  		},
   215  		"Sentence": {
   216  			Value: "Hello kt's sentence.",
   217  			Error: true,
   218  		},
   219  
   220  		"StartsWithWhitespace": {
   221  			Value: "  7",
   222  			Error: true,
   223  		},
   224  		"EndsWithWhitespace": {
   225  			Value: "7 ",
   226  			Error: true,
   227  		},
   228  	}
   229  
   230  	for tn, tc := range cases {
   231  		t.Run(tn, func(t *testing.T) {
   232  			_, errors := StringIsWhiteSpace(tc.Value, tn)
   233  
   234  			if len(errors) > 0 && !tc.Error {
   235  				t.Errorf("StringIsWhiteSpace(%s) produced an unexpected error", tc.Value)
   236  			} else if len(errors) == 0 && tc.Error {
   237  				t.Errorf("StringIsWhiteSpace(%s) did not error", tc.Value)
   238  			}
   239  		})
   240  	}
   241  }
   242  
   243  func TestValidationStringIsBase64(t *testing.T) {
   244  	cases := map[string]struct {
   245  		Value interface{}
   246  		Error bool
   247  	}{
   248  		"NotString": {
   249  			Value: 7,
   250  			Error: true,
   251  		},
   252  		"Empty": {
   253  			Value: "",
   254  			Error: true,
   255  		},
   256  		"NotBase64": {
   257  			Value: "Do'h!",
   258  			Error: true,
   259  		},
   260  		"Base64": {
   261  			Value: "RG8naCE=",
   262  			Error: false,
   263  		},
   264  	}
   265  
   266  	for tn, tc := range cases {
   267  		t.Run(tn, func(t *testing.T) {
   268  			_, errors := StringIsBase64(tc.Value, tn)
   269  
   270  			if len(errors) > 0 && !tc.Error {
   271  				t.Errorf("StringIsBase64(%s) produced an unexpected error", tc.Value)
   272  			} else if len(errors) == 0 && tc.Error {
   273  				t.Errorf("StringIsBase64(%s) did not error", tc.Value)
   274  			}
   275  		})
   276  	}
   277  }
   278  
   279  func TestValidationStringInSlice(t *testing.T) {
   280  	runTestCases(t, []testCase{
   281  		{
   282  			val: "ValidValue",
   283  			f:   StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
   284  		},
   285  		// ignore case
   286  		{
   287  			val: "VALIDVALUE",
   288  			f:   StringInSlice([]string{"ValidValue", "AnotherValidValue"}, true),
   289  		},
   290  		{
   291  			val:         "VALIDVALUE",
   292  			f:           StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
   293  			expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got VALIDVALUE"),
   294  		},
   295  		{
   296  			val:         "InvalidValue",
   297  			f:           StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
   298  			expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got InvalidValue"),
   299  		},
   300  		{
   301  			val:         1,
   302  			f:           StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
   303  			expectedErr: regexp.MustCompile("expected type of [\\w]+ to be string"),
   304  		},
   305  	})
   306  }
   307  
   308  func TestValidationStringNotInSlice(t *testing.T) {
   309  	runTestCases(t, []testCase{
   310  		{
   311  			val: "ValidValue",
   312  			f:   StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, false),
   313  		},
   314  		// ignore case
   315  		{
   316  			val: "VALIDVALUE",
   317  			f:   StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, true),
   318  		},
   319  		{
   320  			val:         "AnotherInvalidValue",
   321  			f:           StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, false),
   322  			expectedErr: regexp.MustCompile("expected [\\w]+ to not be any of \\[InvalidValue AnotherInvalidValue\\], got AnotherInvalidValue"),
   323  		},
   324  		// ignore case
   325  		{
   326  			val:         "INVALIDVALUE",
   327  			f:           StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, true),
   328  			expectedErr: regexp.MustCompile("expected [\\w]+ to not be any of \\[InvalidValue AnotherInvalidValue\\], got INVALIDVALUE"),
   329  		},
   330  		{
   331  			val:         1,
   332  			f:           StringNotInSlice([]string{"InvalidValue", "AnotherInvalidValue"}, false),
   333  			expectedErr: regexp.MustCompile("expected type of [\\w]+ to be string"),
   334  		},
   335  	})
   336  }
   337  
   338  func TestValidationStringMatch(t *testing.T) {
   339  	runTestCases(t, []testCase{
   340  		{
   341  			val: "foobar",
   342  			f:   StringMatch(regexp.MustCompile(".*foo.*"), ""),
   343  		},
   344  		{
   345  			val:         "bar",
   346  			f:           StringMatch(regexp.MustCompile(".*foo.*"), ""),
   347  			expectedErr: regexp.MustCompile("expected value of [\\w]+ to match regular expression " + regexp.QuoteMeta(`".*foo.*"`)),
   348  		},
   349  		{
   350  			val:         "bar",
   351  			f:           StringMatch(regexp.MustCompile(".*foo.*"), "value must contain foo"),
   352  			expectedErr: regexp.MustCompile("invalid value for [\\w]+ \\(value must contain foo\\)"),
   353  		},
   354  	})
   355  }
   356  
   357  func TestValidationStringDoesNotMatch(t *testing.T) {
   358  	runTestCases(t, []testCase{
   359  		{
   360  			val: "foobar",
   361  			f:   StringDoesNotMatch(regexp.MustCompile(".*baz.*"), ""),
   362  		},
   363  		{
   364  			val:         "bar",
   365  			f:           StringDoesNotMatch(regexp.MustCompile(".*bar.*"), ""),
   366  			expectedErr: regexp.MustCompile("expected value of [\\w]+ to not match regular expression " + regexp.QuoteMeta(`".*bar.*"`)),
   367  		},
   368  		{
   369  			val:         "bar",
   370  			f:           StringDoesNotMatch(regexp.MustCompile(".*bar.*"), "value must not contain foo"),
   371  			expectedErr: regexp.MustCompile("invalid value for [\\w]+ \\(value must not contain foo\\)"),
   372  		},
   373  	})
   374  }
   375  
   376  func TestValidateJsonString(t *testing.T) {
   377  	type testCases struct {
   378  		Value    string
   379  		ErrCount int
   380  	}
   381  
   382  	invalidCases := []testCases{
   383  		{
   384  			Value:    `{0:"1"}`,
   385  			ErrCount: 1,
   386  		},
   387  		{
   388  			Value:    `{'abc':1}`,
   389  			ErrCount: 1,
   390  		},
   391  		{
   392  			Value:    `{"def":}`,
   393  			ErrCount: 1,
   394  		},
   395  		{
   396  			Value:    `{"xyz":[}}`,
   397  			ErrCount: 1,
   398  		},
   399  	}
   400  
   401  	for _, tc := range invalidCases {
   402  		_, errors := ValidateJsonString(tc.Value, "json")
   403  		if len(errors) != tc.ErrCount {
   404  			t.Fatalf("Expected %q to trigger a validation error.", tc.Value)
   405  		}
   406  	}
   407  
   408  	validCases := []testCases{
   409  		{
   410  			Value:    ``,
   411  			ErrCount: 0,
   412  		},
   413  		{
   414  			Value:    `{}`,
   415  			ErrCount: 0,
   416  		},
   417  		{
   418  			Value:    `{"abc":["1","2"]}`,
   419  			ErrCount: 0,
   420  		},
   421  	}
   422  
   423  	for _, tc := range validCases {
   424  		_, errors := ValidateJsonString(tc.Value, "json")
   425  		if len(errors) != tc.ErrCount {
   426  			t.Fatalf("Expected %q not to trigger a validation error.", tc.Value)
   427  		}
   428  	}
   429  }
   430  
   431  func TestStringDoesNotContainAny(t *testing.T) {
   432  	chars := "|:/"
   433  
   434  	validStrings := []string{
   435  		"HelloWorld",
   436  		"ABC_*&^%123",
   437  	}
   438  	for _, v := range validStrings {
   439  		_, errors := StringDoesNotContainAny(chars)(v, "name")
   440  		if len(errors) != 0 {
   441  			t.Fatalf("%q should not contain any of %q", v, chars)
   442  		}
   443  	}
   444  
   445  	invalidStrings := []string{
   446  		"Hello/World",
   447  		"ABC|123",
   448  		"This will fail:",
   449  		chars,
   450  	}
   451  	for _, v := range invalidStrings {
   452  		_, errors := StringDoesNotContainAny(chars)(v, "name")
   453  		if len(errors) == 0 {
   454  			t.Fatalf("%q should contain one of %q", v, chars)
   455  		}
   456  	}
   457  }
   458  
   459  func TestValidationRegexp(t *testing.T) {
   460  	runTestCases(t, []testCase{
   461  		{
   462  			val: ".*foo.*",
   463  			f:   ValidateRegexp,
   464  		},
   465  		{
   466  			val:         "foo(bar",
   467  			f:           ValidateRegexp,
   468  			expectedErr: regexp.MustCompile(regexp.QuoteMeta("error parsing regexp: missing closing ): `foo(bar`")),
   469  		},
   470  	})
   471  }