-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
knownvalue: Add
TupleExact
, TuplePartial
and TupleSizeExact
che…
…cks for dynamic value testing (#313) * knownvalue: Add `Tuple*` equivalents for exact, partial, and size checks * add changelog * add documentation for tuples * test name dup
- Loading branch information
1 parent
e984fdb
commit c294752
Showing
12 changed files
with
709 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
kind: ENHANCEMENTS | ||
body: 'knownvalue: Add `TupleExact`, `TuplePartial` and `TupleSizeExact` checks for | ||
dynamic value testing.' | ||
time: 2024-03-25T12:05:39.777695-04:00 | ||
custom: | ||
Issue: "312" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package knownvalue | ||
|
||
import ( | ||
"fmt" | ||
) | ||
|
||
var _ Check = tupleExact{} | ||
|
||
type tupleExact struct { | ||
value []Check | ||
} | ||
|
||
// CheckValue determines whether the passed value is of type []any, and | ||
// contains matching slice entries in the same sequence. | ||
func (v tupleExact) CheckValue(other any) error { | ||
otherVal, ok := other.([]any) | ||
|
||
if !ok { | ||
return fmt.Errorf("expected []any value for TupleExact check, got: %T", other) | ||
} | ||
|
||
if len(otherVal) != len(v.value) { | ||
expectedElements := "elements" | ||
actualElements := "elements" | ||
|
||
if len(v.value) == 1 { | ||
expectedElements = "element" | ||
} | ||
|
||
if len(otherVal) == 1 { | ||
actualElements = "element" | ||
} | ||
|
||
return fmt.Errorf("expected %d %s for TupleExact check, got %d %s", len(v.value), expectedElements, len(otherVal), actualElements) | ||
} | ||
|
||
for i := 0; i < len(v.value); i++ { | ||
if err := v.value[i].CheckValue(otherVal[i]); err != nil { | ||
return fmt.Errorf("tuple element index %d: %s", i, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// String returns the string representation of the value. | ||
func (v tupleExact) String() string { | ||
var tupleVals []string | ||
|
||
for _, val := range v.value { | ||
tupleVals = append(tupleVals, val.String()) | ||
} | ||
|
||
return fmt.Sprintf("%s", tupleVals) | ||
} | ||
|
||
// TupleExact returns a Check for asserting equality between the | ||
// supplied []Check and the value passed to the CheckValue method. | ||
// This is an order-dependent check. | ||
func TupleExact(value []Check) tupleExact { | ||
return tupleExact{ | ||
value: value, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package knownvalue | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
"sort" | ||
"strings" | ||
) | ||
|
||
var _ Check = tuplePartial{} | ||
|
||
type tuplePartial struct { | ||
value map[int]Check | ||
} | ||
|
||
// CheckValue determines whether the passed value is of type []any, and | ||
// contains matching slice entries in the same sequence. | ||
func (v tuplePartial) CheckValue(other any) error { | ||
otherVal, ok := other.([]any) | ||
|
||
if !ok { | ||
return fmt.Errorf("expected []any value for TuplePartial check, got: %T", other) | ||
} | ||
|
||
var keys []int | ||
|
||
for k := range v.value { | ||
keys = append(keys, k) | ||
} | ||
|
||
sort.SliceStable(keys, func(i, j int) bool { | ||
return keys[i] < keys[j] | ||
}) | ||
|
||
for _, k := range keys { | ||
if len(otherVal) <= k { | ||
return fmt.Errorf("missing element index %d for TuplePartial check", k) | ||
} | ||
|
||
if err := v.value[k].CheckValue(otherVal[k]); err != nil { | ||
return fmt.Errorf("tuple element %d: %s", k, err) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// String returns the string representation of the value. | ||
func (v tuplePartial) String() string { | ||
var b bytes.Buffer | ||
|
||
b.WriteString("[") | ||
|
||
var keys []int | ||
|
||
var tupleVals []string | ||
|
||
for k := range v.value { | ||
keys = append(keys, k) | ||
} | ||
|
||
sort.SliceStable(keys, func(i, j int) bool { | ||
return keys[i] < keys[j] | ||
}) | ||
|
||
for _, k := range keys { | ||
tupleVals = append(tupleVals, fmt.Sprintf("%d:%s", k, v.value[k])) | ||
} | ||
|
||
b.WriteString(strings.Join(tupleVals, " ")) | ||
|
||
b.WriteString("]") | ||
|
||
return b.String() | ||
} | ||
|
||
// TuplePartial returns a Check for asserting partial equality between the | ||
// supplied map[int]Check and the value passed to the CheckValue method. The | ||
// map keys represent the zero-ordered element indices within the tuple that is | ||
// being checked. Only the elements at the indices defined within the | ||
// supplied map[int]Check are checked. | ||
func TuplePartial(value map[int]Check) tuplePartial { | ||
return tuplePartial{ | ||
value: value, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package knownvalue_test | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/google/go-cmp/cmp" | ||
|
||
"github.com/hashicorp/terraform-plugin-testing/knownvalue" | ||
) | ||
|
||
func TestTuplePartial_CheckValue(t *testing.T) { | ||
t.Parallel() | ||
|
||
testCases := map[string]struct { | ||
self knownvalue.Check | ||
other any | ||
expectedError error | ||
}{ | ||
"zero-nil": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{}), | ||
expectedError: fmt.Errorf("expected []any value for TuplePartial check, got: <nil>"), | ||
}, | ||
"zero-other": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{}), | ||
other: []any{}, // checking against the underlying value field zero-value | ||
}, | ||
"nil": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
expectedError: fmt.Errorf("expected []any value for TuplePartial check, got: <nil>"), | ||
}, | ||
"wrong-type": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
other: 1.234, | ||
expectedError: fmt.Errorf("expected []any value for TuplePartial check, got: float64"), | ||
}, | ||
"empty": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
other: []any{}, | ||
expectedError: fmt.Errorf("missing element index 0 for TuplePartial check"), | ||
}, | ||
"wrong-length": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
other: []any{ | ||
json.Number("1.23"), | ||
"hello", | ||
}, | ||
expectedError: fmt.Errorf("missing element index 2 for TuplePartial check"), | ||
}, | ||
"not-equal": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
other: []any{ | ||
json.Number("1.23"), | ||
"world", | ||
"hello", | ||
}, | ||
expectedError: fmt.Errorf("tuple element 2: expected value world for StringExact check, got: hello"), | ||
}, | ||
"wrong-order": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
other: []any{ | ||
json.Number("1.23"), | ||
"world", | ||
true, | ||
}, | ||
expectedError: fmt.Errorf("tuple element 2: expected string value for StringExact check, got: bool"), | ||
}, | ||
"equal": { | ||
self: knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}), | ||
other: []any{ | ||
json.Number("1.23"), | ||
"hello", | ||
"world", | ||
true, | ||
}, | ||
}, | ||
} | ||
|
||
for name, testCase := range testCases { | ||
name, testCase := name, testCase | ||
|
||
t.Run(name, func(t *testing.T) { | ||
t.Parallel() | ||
|
||
got := testCase.self.CheckValue(testCase.other) | ||
|
||
if diff := cmp.Diff(got, testCase.expectedError, equateErrorMessage); diff != "" { | ||
t.Errorf("unexpected difference: %s", diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestTuplePartial_String(t *testing.T) { | ||
t.Parallel() | ||
|
||
got := knownvalue.TuplePartial(map[int]knownvalue.Check{ | ||
0: knownvalue.Float64Exact(1.23), | ||
2: knownvalue.StringExact("world"), | ||
3: knownvalue.Bool(true), | ||
}).String() | ||
|
||
if diff := cmp.Diff(got, "[0:1.23 2:world 3:true]"); diff != "" { | ||
t.Errorf("unexpected difference: %s", diff) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package knownvalue | ||
|
||
import ( | ||
"fmt" | ||
"strconv" | ||
) | ||
|
||
var _ Check = tupleSizeExact{} | ||
|
||
type tupleSizeExact struct { | ||
size int | ||
} | ||
|
||
// CheckValue verifies that the passed value is a tuple, map, object, | ||
// or set, and contains a matching number of elements. | ||
func (v tupleSizeExact) CheckValue(other any) error { | ||
otherVal, ok := other.([]any) | ||
|
||
if !ok { | ||
return fmt.Errorf("expected []any value for TupleSizeExact check, got: %T", other) | ||
} | ||
|
||
if len(otherVal) != v.size { | ||
expectedElements := "elements" | ||
actualElements := "elements" | ||
|
||
if v.size == 1 { | ||
expectedElements = "element" | ||
} | ||
|
||
if len(otherVal) == 1 { | ||
actualElements = "element" | ||
} | ||
|
||
return fmt.Errorf("expected %d %s for TupleSizeExact check, got %d %s", v.size, expectedElements, len(otherVal), actualElements) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// String returns the string representation of the value. | ||
func (v tupleSizeExact) String() string { | ||
return strconv.FormatInt(int64(v.size), 10) | ||
} | ||
|
||
// TupleSizeExact returns a Check for asserting that | ||
// a tuple has size elements. | ||
func TupleSizeExact(size int) tupleSizeExact { | ||
return tupleSizeExact{ | ||
size: size, | ||
} | ||
} |
Oops, something went wrong.