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

Add AllDiag and AnyDiag to helper/validation for combining SchemaValidateDiagFunc #1155

Merged
merged 2 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20230222-001027.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: Add `SchemaValidateDiagFunc` variants of helper/validation `All` and `Any` as
`AllDiag` and `AnyDiag`.
Comment on lines +2 to +3
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
body: Add `SchemaValidateDiagFunc` variants of helper/validation `All` and `Any` as
`AllDiag` and `AnyDiag`.
body: 'helper/validation: Added `AllDiag` and `AnyDiag`, which are `SchemaValidateDiagFunc`
variants of `All` and `Any`'

time: 2023-02-22T00:10:27.474224+02:00
custom:
Issue: "1155"
28 changes: 28 additions & 0 deletions helper/validation/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ func All(validators ...schema.SchemaValidateFunc) schema.SchemaValidateFunc {
}
}

// AllDiag returns a SchemaValidateDiagFunc which tests if the provided value
// passes all provided SchemaValidateDiagFunc
func AllDiag(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc {
return func(i interface{}, k cty.Path) diag.Diagnostics {
var diags diag.Diagnostics
for _, validator := range validators {
diags = append(diags, validator(i, k)...)
}
return diags
}
}

// Any returns a SchemaValidateFunc which tests if the provided value
// passes any of the provided SchemaValidateFunc
func Any(validators ...schema.SchemaValidateFunc) schema.SchemaValidateFunc {
Expand All @@ -60,6 +72,22 @@ func Any(validators ...schema.SchemaValidateFunc) schema.SchemaValidateFunc {
}
}

// AnyDiag returns a SchemaValidateDiagFunc which tests if the provided value
// passes any of the provided SchemaValidateDiagFunc
func AnyDiag(validators ...schema.SchemaValidateDiagFunc) schema.SchemaValidateDiagFunc {
return func(i interface{}, k cty.Path) diag.Diagnostics {
var diags diag.Diagnostics
for _, validator := range validators {
validatorDiags := validator(i, k)
if len(validatorDiags) == 0 {
return diag.Diagnostics{}
}
diags = append(diags, validatorDiags...)
}
return diags
}
}

// ToDiagFunc is a wrapper for legacy schema.SchemaValidateFunc
// converting it to schema.SchemaValidateDiagFunc
func ToDiagFunc(validator schema.SchemaValidateFunc) schema.SchemaValidateDiagFunc {
Expand Down
63 changes: 63 additions & 0 deletions helper/validation/meta_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,34 @@ func TestValidationAll(t *testing.T) {
})
}

func TestValidationAllDiag(t *testing.T) {
runDiagTestCases(t, []diagTestCase{
{
val: "valid",
f: AllDiag(
ToDiagFunc(StringLenBetween(5, 42)),
ToDiagFunc(StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric")),
),
},
{
val: "foo",
f: AllDiag(
ToDiagFunc(StringLenBetween(5, 42)),
ToDiagFunc(StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric")),
),
expectedDiagSummary: regexp.MustCompile(`expected length of [\w]+ to be in the range \(5 - 42\), got foo`),
},
{
val: "!!!!!",
f: AllDiag(
ToDiagFunc(StringLenBetween(5, 42)),
ToDiagFunc(StringMatch(regexp.MustCompile(`[a-zA-Z0-9]+`), "value must be alphanumeric")),
),
expectedDiagSummary: regexp.MustCompile("value must be alphanumeric"),
},
})
}

func TestValidationAny(t *testing.T) {
runTestCases(t, []testCase{
{
Expand Down Expand Up @@ -103,6 +131,41 @@ func TestValidationAny(t *testing.T) {
})
}

func TestValidationAnyDiag(t *testing.T) {
runDiagTestCases(t, []diagTestCase{
{
val: 43,
f: AnyDiag(
ToDiagFunc(IntAtLeast(42)),
ToDiagFunc(IntAtMost(5)),
),
},
{
val: 4,
f: AnyDiag(
ToDiagFunc(IntAtLeast(42)),
ToDiagFunc(IntAtMost(5)),
),
},
{
val: 7,
f: AnyDiag(
ToDiagFunc(IntAtLeast(42)),
ToDiagFunc(IntAtMost(5)),
),
expectedDiagSummary: regexp.MustCompile(`expected [\w]+ to be at least \(42\), got 7`),
},
{
val: 7,
f: AnyDiag(
ToDiagFunc(IntAtLeast(42)),
ToDiagFunc(IntAtMost(5)),
),
expectedDiagSummary: regexp.MustCompile(`expected [\w]+ to be at most \(5\), got 7`),
},
})
}

func TestToDiagFunc(t *testing.T) {
t.Parallel()

Expand Down
56 changes: 44 additions & 12 deletions helper/validation/testing.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package validation

import (
"fmt"
"regexp"
"testing"

testing "github.com/mitchellh/go-testing-interface"
"github.com/hashicorp/go-cty/cty"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
Expand All @@ -15,23 +17,53 @@ type testCase struct {
expectedErr *regexp.Regexp
}

func runTestCases(t testing.T, cases []testCase) {
func runTestCases(t *testing.T, cases []testCase) {
t.Helper()

for i, tc := range cases {
_, errs := tc.f(tc.val, "test_property")
t.Run(fmt.Sprintf("TestCase_%d", i), func(t *testing.T) {
bflad marked this conversation as resolved.
Show resolved Hide resolved
_, errs := tc.f(tc.val, "test_property")

if len(errs) == 0 && tc.expectedErr == nil {
continue
}
if len(errs) == 0 && tc.expectedErr == nil {
return
}

if len(errs) != 0 && tc.expectedErr == nil {
t.Fatalf("expected test case %d to produce no errors, got %v", i, errs)
}
if len(errs) != 0 && tc.expectedErr == nil {
t.Fatalf("expected test case %d to produce no errors, got %v", i, errs)
}

if !matchAnyError(errs, tc.expectedErr) {
t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs)
}
if !matchAnyError(errs, tc.expectedErr) {
t.Fatalf("expected test case %d to produce error matching \"%s\", got %v", i, tc.expectedErr, errs)
}
})
}
}

type diagTestCase struct {
val interface{}
f schema.SchemaValidateDiagFunc
expectedDiagSummary *regexp.Regexp
}

func runDiagTestCases(t *testing.T, cases []diagTestCase) {
t.Helper()

for i, tc := range cases {
t.Run(fmt.Sprintf("TestCase_%d", i), func(t *testing.T) {
diags := tc.f(tc.val, cty.GetAttrPath("test_property"))

if len(diags) == 0 && tc.expectedDiagSummary == nil {
return
}

if len(diags) != 0 && tc.expectedDiagSummary == nil {
t.Fatalf("expected test case %d to produce no diagnostics, got %v", i, diags)
}

if !matchAnyDiagSummary(diags, tc.expectedDiagSummary) {
t.Fatalf("expected test case %d to produce diagnostic summary matching \"%s\", got %v", i, tc.expectedDiagSummary, diags)
}
})
}
}

Expand Down