Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

validation: add additional Int, Network, Time and Web validators #296

Merged
merged 15 commits into from
Jan 16, 2020
64 changes: 64 additions & 0 deletions helper/validation/float.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package validation

import (
"fmt"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

// FloatBetween returns a SchemaValidateFunc which tests if the provided value
// is of type float64 and is between min and max (inclusive).
func FloatBetween(min, max float64) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(float64)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be float64", k))
return
}

if v < min || v > max {
es = append(es, fmt.Errorf("expected %s to be in the range (%f - %f), got %f", k, min, max, v))
return
}

return
}
}

// FloatAtLeast returns a SchemaValidateFunc which tests if the provided value
// is of type float and is at least min (inclusive)
func FloatAtLeast(min float64) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(float64)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be float", k))
return
}

if v < min {
es = append(es, fmt.Errorf("expected %s to be at least (%f), got %f", k, min, v))
return
}

return
}
}

// FloatAtMost returns a SchemaValidateFunc which tests if the provided value
// is of type float and is at most max (inclusive)
func FloatAtMost(max float64) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(float64)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be float", k))
return
}

if v > max {
es = append(es, fmt.Errorf("expected %s to be at most (%f), got %f", k, max, v))
return
}

return
}
}
97 changes: 97 additions & 0 deletions helper/validation/float_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package validation

import (
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

func TestValidateFloatBetween(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)
}
}
}

func TestValidateFloatAtLeast(t *testing.T) {
runTestCases(t, []testCase{
{
val: 2.5,
f: FloatAtLeast(1.5),
},
{
val: -1.0,
f: FloatAtLeast(-1.5),
},
{
val: 1.5,
f: FloatAtLeast(2.5),
expectedErr: regexp.MustCompile("expected [\\w]+ to be at least \\(2\\.5\\d*\\), got 1\\.5\\d*"),
},
{
val: "2.5",
f: FloatAtLeast(1.5),
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be float"),
},
})
}

func TestValidateFloatAtMost(t *testing.T) {
runTestCases(t, []testCase{
{
val: 2.5,
f: FloatAtMost(3.5),
},
{
val: -1.0,
f: FloatAtMost(-0.5),
},
{
val: 2.5,
f: FloatAtMost(1.5),
expectedErr: regexp.MustCompile("expected [\\w]+ to be at most \\(1\\.5\\d*\\), got 2\\.5\\d*"),
},
{
val: "2.5",
f: FloatAtMost(3.5),
expectedErr: regexp.MustCompile("expected type of [\\w]+ to be float"),
},
})
}
125 changes: 125 additions & 0 deletions helper/validation/int.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package validation

import (
"fmt"
"math"

"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
)

// IntBetween returns a SchemaValidateFunc which tests if the provided value
// is of type int and is between min and max (inclusive)
func IntBetween(min, max int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(int)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be int", k))
return
}

if v < min || v > max {
es = append(es, fmt.Errorf("expected %s to be in the range (%d - %d), got %d", k, min, max, v))
return
}

return
}
}

// IntAtLeast returns a SchemaValidateFunc which tests if the provided value
// is of type int and is at least min (inclusive)
func IntAtLeast(min int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(int)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be int", k))
return
}

if v < min {
es = append(es, fmt.Errorf("expected %s to be at least (%d), got %d", k, min, v))
return
}

return
}
}

// IntAtMost returns a SchemaValidateFunc which tests if the provided value
// is of type int and is at most max (inclusive)
func IntAtMost(max int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(int)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be int", k))
return
}

if v > max {
es = append(es, fmt.Errorf("expected %s to be at most (%d), got %d", k, max, v))
return
}

return
}
}

// IntDivisibleBy returns a SchemaValidateFunc which tests if the provided value
// is of type int and is divisible by a given number
func IntDivisibleBy(divisor int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (warnings []string, errors []error) {
v, ok := i.(int)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %s to be int", k))
return
}

if math.Mod(float64(v), float64(divisor)) != 0 {
errors = append(errors, fmt.Errorf("expected %s to be divisible by %d, got: %v", k, divisor, i))
return
}

return warnings, errors
}
}

// IntInSlice returns a SchemaValidateFunc which tests if the provided value
// is of type int and matches the value of an element in the valid slice
func IntInSlice(valid []int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (s []string, es []error) {
v, ok := i.(int)
if !ok {
es = append(es, fmt.Errorf("expected type of %s to be an integer", k))
return
}

for _, validInt := range valid {
if v == validInt {
return
}
}

es = append(es, fmt.Errorf("expected %s to be one of %v, got %d", k, valid, v))
return
}
}

// IntInSlice returns a SchemaValidateFunc which tests if the provided value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment/doc needs updating

// is of type int and matches the value of an element in the valid slice
func IntNotInSlice(valid []int) schema.SchemaValidateFunc {
return func(i interface{}, k string) (_ []string, errors []error) {
v, ok := i.(int)
if !ok {
errors = append(errors, fmt.Errorf("expected type of %s to be an integer", k))
return
}

for _, validInt := range valid {
if v == validInt {
errors = append(errors, fmt.Errorf("expected %s to not be one of %v, got %d", k, valid, v))
}
}

return
}
}
Loading