419 lines
9.9 KiB
Go
419 lines
9.9 KiB
Go
package validation
|
|
|
|
import (
|
|
"regexp"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
|
)
|
|
|
|
type testCase struct {
|
|
val interface{}
|
|
f schema.SchemaValidateFunc
|
|
expectedErr *regexp.Regexp
|
|
}
|
|
|
|
func TestValidationIntBetween(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: 1,
|
|
f: IntBetween(1, 1),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: IntBetween(0, 2),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: IntBetween(2, 3),
|
|
expectedErr: regexp.MustCompile("expected [\\w]+ to be in the range \\(2 - 3\\), got 1"),
|
|
},
|
|
{
|
|
val: "1",
|
|
f: IntBetween(2, 3),
|
|
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be int"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationIntAtLeast(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: 1,
|
|
f: IntAtLeast(1),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: IntAtLeast(0),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: IntAtLeast(2),
|
|
expectedErr: regexp.MustCompile("expected [\\w]+ to be at least \\(2\\), got 1"),
|
|
},
|
|
{
|
|
val: "1",
|
|
f: IntAtLeast(2),
|
|
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be int"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationIntAtMost(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: 1,
|
|
f: IntAtMost(1),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: IntAtMost(2),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: IntAtMost(0),
|
|
expectedErr: regexp.MustCompile("expected [\\w]+ to be at most \\(0\\), got 1"),
|
|
},
|
|
{
|
|
val: "1",
|
|
f: IntAtMost(0),
|
|
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be int"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationStringInSlice(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: "ValidValue",
|
|
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
|
},
|
|
// ignore case
|
|
{
|
|
val: "VALIDVALUE",
|
|
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, true),
|
|
},
|
|
{
|
|
val: "VALIDVALUE",
|
|
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
|
expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got VALIDVALUE"),
|
|
},
|
|
{
|
|
val: "InvalidValue",
|
|
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
|
expectedErr: regexp.MustCompile("expected [\\w]+ to be one of \\[ValidValue AnotherValidValue\\], got InvalidValue"),
|
|
},
|
|
{
|
|
val: 1,
|
|
f: StringInSlice([]string{"ValidValue", "AnotherValidValue"}, false),
|
|
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be string"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationStringMatch(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: "foobar",
|
|
f: StringMatch(regexp.MustCompile(".*foo.*"), ""),
|
|
},
|
|
{
|
|
val: "bar",
|
|
f: StringMatch(regexp.MustCompile(".*foo.*"), ""),
|
|
expectedErr: regexp.MustCompile("expected value of [\\w]+ to match regular expression " + regexp.QuoteMeta(`".*foo.*"`)),
|
|
},
|
|
{
|
|
val: "bar",
|
|
f: StringMatch(regexp.MustCompile(".*foo.*"), "value must contain foo"),
|
|
expectedErr: regexp.MustCompile("invalid value for [\\w]+ \\(value must contain foo\\)"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationRegexp(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: ".*foo.*",
|
|
f: ValidateRegexp,
|
|
},
|
|
{
|
|
val: "foo(bar",
|
|
f: ValidateRegexp,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta("error parsing regexp: missing closing ): `foo(bar`")),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationSingleIP(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: "172.10.10.10",
|
|
f: SingleIP(),
|
|
},
|
|
{
|
|
val: "1.1.1",
|
|
f: SingleIP(),
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP, got:")),
|
|
},
|
|
{
|
|
val: "1.1.1.0/20",
|
|
f: SingleIP(),
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP, got:")),
|
|
},
|
|
{
|
|
val: "256.1.1.1",
|
|
f: SingleIP(),
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP, got:")),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationIPRange(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: "172.10.10.10-172.10.10.12",
|
|
f: IPRange(),
|
|
},
|
|
{
|
|
val: "172.10.10.20",
|
|
f: IPRange(),
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP range, got:")),
|
|
},
|
|
{
|
|
val: "172.10.10.20-172.10.10.12",
|
|
f: IPRange(),
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta("expected test_property to contain a valid IP range, got:")),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidateRFC3339TimeString(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: "2018-03-01T00:00:00Z",
|
|
f: ValidateRFC3339TimeString,
|
|
},
|
|
{
|
|
val: "2018-03-01T00:00:00-05:00",
|
|
f: ValidateRFC3339TimeString,
|
|
},
|
|
{
|
|
val: "2018-03-01T00:00:00+05:00",
|
|
f: ValidateRFC3339TimeString,
|
|
},
|
|
{
|
|
val: "03/01/2018",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
{
|
|
val: "03-01-2018",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
{
|
|
val: "2018-03-01",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
{
|
|
val: "2018-03-01T",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
{
|
|
val: "2018-03-01T00:00:00",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
{
|
|
val: "2018-03-01T00:00:00Z05:00",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
{
|
|
val: "2018-03-01T00:00:00Z-05:00",
|
|
f: ValidateRFC3339TimeString,
|
|
expectedErr: regexp.MustCompile(regexp.QuoteMeta(`invalid RFC3339 timestamp`)),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidateJsonString(t *testing.T) {
|
|
type testCases struct {
|
|
Value string
|
|
ErrCount int
|
|
}
|
|
|
|
invalidCases := []testCases{
|
|
{
|
|
Value: `{0:"1"}`,
|
|
ErrCount: 1,
|
|
},
|
|
{
|
|
Value: `{'abc':1}`,
|
|
ErrCount: 1,
|
|
},
|
|
{
|
|
Value: `{"def":}`,
|
|
ErrCount: 1,
|
|
},
|
|
{
|
|
Value: `{"xyz":[}}`,
|
|
ErrCount: 1,
|
|
},
|
|
}
|
|
|
|
for _, tc := range invalidCases {
|
|
_, errors := ValidateJsonString(tc.Value, "json")
|
|
if len(errors) != tc.ErrCount {
|
|
t.Fatalf("Expected %q to trigger a validation error.", tc.Value)
|
|
}
|
|
}
|
|
|
|
validCases := []testCases{
|
|
{
|
|
Value: ``,
|
|
ErrCount: 0,
|
|
},
|
|
{
|
|
Value: `{}`,
|
|
ErrCount: 0,
|
|
},
|
|
{
|
|
Value: `{"abc":["1","2"]}`,
|
|
ErrCount: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range validCases {
|
|
_, errors := ValidateJsonString(tc.Value, "json")
|
|
if len(errors) != tc.ErrCount {
|
|
t.Fatalf("Expected %q not to trigger a validation error.", tc.Value)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestValidateListUniqueStrings(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: []interface{}{"foo", "bar"},
|
|
f: ValidateListUniqueStrings,
|
|
},
|
|
{
|
|
val: []interface{}{"foo", "bar", "foo"},
|
|
f: ValidateListUniqueStrings,
|
|
expectedErr: regexp.MustCompile("duplicate entry - foo"),
|
|
},
|
|
{
|
|
val: []interface{}{"foo", "bar", "foo", "baz", "bar"},
|
|
f: ValidateListUniqueStrings,
|
|
expectedErr: regexp.MustCompile("duplicate entry - (?:foo|bar)"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func TestValidationNoZeroValues(t *testing.T) {
|
|
runTestCases(t, []testCase{
|
|
{
|
|
val: "foo",
|
|
f: NoZeroValues,
|
|
},
|
|
{
|
|
val: 1,
|
|
f: NoZeroValues,
|
|
},
|
|
{
|
|
val: float64(1),
|
|
f: NoZeroValues,
|
|
},
|
|
{
|
|
val: "",
|
|
f: NoZeroValues,
|
|
expectedErr: regexp.MustCompile("must not be empty"),
|
|
},
|
|
{
|
|
val: 0,
|
|
f: NoZeroValues,
|
|
expectedErr: regexp.MustCompile("must not be zero"),
|
|
},
|
|
{
|
|
val: float64(0),
|
|
f: NoZeroValues,
|
|
expectedErr: regexp.MustCompile("must not be zero"),
|
|
},
|
|
})
|
|
}
|
|
|
|
func runTestCases(t *testing.T, cases []testCase) {
|
|
matchErr := func(errs []error, r *regexp.Regexp) bool {
|
|
// err must match one provided
|
|
for _, err := range errs {
|
|
if r.MatchString(err.Error()) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
for i, tc := range cases {
|
|
_, errs := tc.f(tc.val, "test_property")
|
|
|
|
if len(errs) == 0 && tc.expectedErr == nil {
|
|
continue
|
|
}
|
|
|
|
if len(errs) != 0 && tc.expectedErr == nil {
|
|
t.Fatalf("expected test case %d to produce no errors, got %v", i, errs)
|
|
}
|
|
|
|
if !matchErr(errs, tc.expectedErr) {
|
|
t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFloatBetween(t *testing.T) {
|
|
cases := map[string]struct {
|
|
Value interface{}
|
|
ValidateFunc schema.SchemaValidateFunc
|
|
ExpectValidationErrors bool
|
|
}{
|
|
"accept valid value": {
|
|
Value: 1.5,
|
|
ValidateFunc: FloatBetween(1.0, 2.0),
|
|
ExpectValidationErrors: false,
|
|
},
|
|
"accept valid value inclusive upper bound": {
|
|
Value: 1.0,
|
|
ValidateFunc: FloatBetween(0.0, 1.0),
|
|
ExpectValidationErrors: false,
|
|
},
|
|
"accept valid value inclusive lower bound": {
|
|
Value: 0.0,
|
|
ValidateFunc: FloatBetween(0.0, 1.0),
|
|
ExpectValidationErrors: false,
|
|
},
|
|
"reject out of range value": {
|
|
Value: -1.0,
|
|
ValidateFunc: FloatBetween(0.0, 1.0),
|
|
ExpectValidationErrors: true,
|
|
},
|
|
"reject incorrectly typed value": {
|
|
Value: 1,
|
|
ValidateFunc: FloatBetween(0.0, 1.0),
|
|
ExpectValidationErrors: true,
|
|
},
|
|
}
|
|
|
|
for tn, tc := range cases {
|
|
_, errors := tc.ValidateFunc(tc.Value, tn)
|
|
if len(errors) > 0 && !tc.ExpectValidationErrors {
|
|
t.Errorf("%s: unexpected errors %s", tn, errors)
|
|
} else if len(errors) == 0 && tc.ExpectValidationErrors {
|
|
t.Errorf("%s: expected errors but got none", tn)
|
|
}
|
|
}
|
|
}
|