Skip to content

Commit

Permalink
Fix azurerm_databricks_workspace.name validation
Browse files Browse the repository at this point in the history
  • Loading branch information
Gerry Hernandez committed Oct 23, 2020
1 parent a5e603d commit 2ed4d27
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -355,23 +355,29 @@ func ValidateDatabricksWorkspaceName(i interface{}, k string) (warnings []string
return warnings, errors
}

// Cannot be empty
// The Azure Portal shows the following validation criteria:

// 1) Cannot be empty
if len(v) == 0 {
errors = append(errors, fmt.Errorf("%q cannot be an empty string: %q", k, v))
// Treating this as a special case and returning early to match Azure Portal behavior.
return warnings, errors
}

// First, second, and last characters must be a letter or number with a total length between 3 to 64 characters
// 2) Must be at least 3 characters:
if len(v) < 3 {
errors = append(errors, fmt.Errorf("%q must be at least 3 characters: %q", k, v))
}

// 3) The value must have a length of at most 30.
// NOTE: Restricted name to 30 characters because that is the restriction in Azure Portal even though the API supports 64 characters
if !regexp.MustCompile("^[a-zA-Z0-9]{2}[-_a-zA-Z0-9]{0,27}[a-zA-Z0-9]{1}$").MatchString(v) {
errors = append(errors, fmt.Errorf("%q must be 3 - 30 characters in length", k))
errors = append(errors, fmt.Errorf("%q first, second, and last characters must be a letter or number", k))
errors = append(errors, fmt.Errorf("%q can only contain letters, numbers, underscores, and hyphens", k))
if len(v) > 30 {
errors = append(errors, fmt.Errorf("%q must be no more than 30 characters: %q", k, v))
}

// No consecutive hyphens
if regexp.MustCompile("(--)").MatchString(v) {
errors = append(errors, fmt.Errorf("%q must not contain any consecutive hyphens", k))
// 4) Only alphanumeric characters, underscores, and hyphens are allowed.
if !regexp.MustCompile("^[a-zA-Z0-9_-]*$").MatchString(v) {
errors = append(errors, fmt.Errorf("%q can contain only alphanumeric characters, underscores, and hyphens: %q", k, v))
}

return warnings, errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"net/http"
"regexp"
"strings"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
Expand All @@ -15,71 +16,89 @@ import (
)

func TestAzureRMDatabrickWorkspaceName(t *testing.T) {
const errEmpty = "cannot be an empty string"
const errMinLen = "must be at least 3 characters"
const errMaxLen = "must be no more than 30 characters"
const errAllowList = "can contain only alphanumeric characters, underscores, and hyphens"

cases := []struct {
Value string
ShouldError bool
Name string
Input string
ExpectedErrors []string
}{
// Happy paths:
{
Value: "hello",
ShouldError: false,
},
{
Value: "hello123there",
ShouldError: false,
},
{
Value: "hello-1-2-3-there",
ShouldError: false,
},
{
Value: "hello_1_2_3_there",
ShouldError: false,
Name: "Entire character allow-list",
Input: "aZ09_-",
},
{
Value: "hello-1-2-3-",
ShouldError: true,
Name: "Minimum character length",
Input: "---",
},
{
Value: "-hello-1-2-3",
ShouldError: true,
Name: "Maximum character length",
Input: "012345678901234567890123456789", // 30 chars
},

// Simple negative cases:
{
Value: "hello_1_2_3_",
ShouldError: true,
Name: "Introduce a non-allowed character",
Input: "aZ09_-$", // dollar sign
ExpectedErrors: []string{errAllowList},
},
{
Value: "_hello_1_2_3",
ShouldError: true,
Name: "Below minimum character length",
Input: "--",
ExpectedErrors: []string{errMinLen},
},
{
Value: "hello!there",
ShouldError: true,
Name: "Above maximum character length",
Input: "0123456789012345678901234567890", // 31 chars
ExpectedErrors: []string{errMaxLen},
},
{
Value: "hello--there",
ShouldError: true,
Name: "Specifically test for emptiness",
Input: "",
ExpectedErrors: []string{errEmpty},
},

// Complex negative cases
{
Value: "!hellothere",
ShouldError: true,
Name: "Too short and non-allowed char",
Input: "*^",
ExpectedErrors: []string{errMinLen, errAllowList},
},
{
Value: "hellothere!",
ShouldError: true,
Name: "Too long and non-allowed char",
Input: "012345678901234567890123456789ß",
ExpectedErrors: []string{errMaxLen, errAllowList},
},
}

for _, tc := range cases {
_, errors := databricks.ValidateDatabricksWorkspaceName(tc.Value, "test")

hasErrors := len(errors) > 0
if hasErrors && !tc.ShouldError {
t.Fatalf("Expected no errors but got %d for %q", len(errors), tc.Value)
errsContain := func(errors []error, text string) bool {
for _, err := range errors {
if strings.Contains(err.Error(), text) {
return true
}
}
return false
}

if !hasErrors && tc.ShouldError {
t.Fatalf("Expected no errors but got %d for %q", len(errors), tc.Value)
}
t.Parallel()
for _, tc := range cases {
t.Run(tc.Name, func(t *testing.T) {
_, errors := databricks.ValidateDatabricksWorkspaceName(tc.Input, "azurerm_databricks_workspace.test.name")

if len(errors) != len(tc.ExpectedErrors) {
t.Fatalf("Expected %d errors but got %d for %q: %v", len(tc.ExpectedErrors), len(errors), tc.Input, errors)
}

for _, expectedError := range tc.ExpectedErrors {
if !errsContain(errors, expectedError) {
t.Fatalf("Errors did not contain expected error: %s", expectedError)
}
}
})
}
}

Expand Down

0 comments on commit 2ed4d27

Please sign in to comment.