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 }