Skip to content

Commit 3ef7858

Browse files
bendbennettIvan De Marinobflad
authored
Adding AtLeastSumOf, AtMostSumOf, EqualToSumOf Validators (#29)
* Adding atLeastSumOf, atMostSumOf and equalToSumOf int64 validators (#20) * Switching to using path expressions * Do not validate if any attributes are unknown (#20) * Updating dependencies, including [email protected] * PR review: making use of the new `path.Expression` `.MergeExpressions` method * Preparing CHANGELOG entry * Rely on 'tfsdk.ValueAs' to do type validation Co-authored-by: Ivan De Marino <[email protected]> Co-authored-by: Brian Flad <[email protected]>
1 parent 81783a7 commit 3ef7858

11 files changed

+953
-36
lines changed

.changelog/29.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:enhancement
2+
int64validator: Added `AtLeastSumOf()`, `AtMostSumOf()` and `EqualToSumOf()` validation functions
3+
```

go.mod

+7-7
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,22 @@ go 1.17
44

55
require (
66
github.com/google/go-cmp v0.5.8
7-
github.com/hashicorp/terraform-plugin-framework v0.9.1-0.20220711170800-89baaa204707
8-
github.com/hashicorp/terraform-plugin-go v0.11.0
7+
github.com/hashicorp/terraform-plugin-framework v0.10.0
8+
github.com/hashicorp/terraform-plugin-go v0.12.0
99
)
1010

1111
require (
1212
github.com/fatih/color v1.13.0 // indirect
1313
github.com/golang/protobuf v1.5.2 // indirect
1414
github.com/hashicorp/go-hclog v1.2.1 // indirect
15-
github.com/hashicorp/terraform-plugin-log v0.4.1 // indirect
15+
github.com/hashicorp/terraform-plugin-log v0.6.0 // indirect
1616
github.com/mattn/go-colorable v0.1.12 // indirect
1717
github.com/mattn/go-isatty v0.0.14 // indirect
1818
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
1919
github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect
20-
github.com/vmihailenco/tagparser v0.1.1 // indirect
21-
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 // indirect
22-
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
23-
google.golang.org/appengine v1.6.5 // indirect
20+
github.com/vmihailenco/tagparser v0.1.2 // indirect
21+
golang.org/x/net v0.0.0-20220708220712-1185a9018129 // indirect
22+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
23+
google.golang.org/appengine v1.6.7 // indirect
2424
google.golang.org/protobuf v1.28.0 // indirect
2525
)

go.sum

+15-7
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,12 @@ github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH
6262
github.com/hashicorp/go-plugin v1.4.4/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
6363
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
6464
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
65-
github.com/hashicorp/terraform-plugin-framework v0.9.1-0.20220711170800-89baaa204707 h1:4wAdubsgZ/eAbCjW2At8w6UDeVCel76jWDHsWvbc8Pk=
66-
github.com/hashicorp/terraform-plugin-framework v0.9.1-0.20220711170800-89baaa204707/go.mod h1:+H4ieVu7X4bfYlLB/zytek48e4CjcG+gjKdVOjVY1PU=
67-
github.com/hashicorp/terraform-plugin-go v0.11.0 h1:YXsvSCx7GbQO5jIUQd77FesqmIBxgSvYAtAX1NqErTk=
68-
github.com/hashicorp/terraform-plugin-go v0.11.0/go.mod h1:aphXBG8qtQH0yF1waMRlaw/3G+ZFlR/6Artnvt1QEDE=
69-
github.com/hashicorp/terraform-plugin-log v0.4.1 h1:xpbmVhvuU3mgHzLetOmx9pkOL2rmgpu302XxddON6eo=
70-
github.com/hashicorp/terraform-plugin-log v0.4.1/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4=
65+
github.com/hashicorp/terraform-plugin-framework v0.10.0 h1:LGYcnvNdVaZA1ZHe53BHLVjaaGs7HTiq6+9Js29stL4=
66+
github.com/hashicorp/terraform-plugin-framework v0.10.0/go.mod h1:CK7Opzukfu/2CPJs+HzUdfHrFlp+ZIQeSxjF0x8k464=
67+
github.com/hashicorp/terraform-plugin-go v0.12.0 h1:6wW9mT1dSs0Xq4LR6HXj1heQ5ovr5GxXNJwkErZzpJw=
68+
github.com/hashicorp/terraform-plugin-go v0.12.0/go.mod h1:kwhmaWHNDvT1B3QiSJdAtrB/D4RaKSY/v3r2BuoWK4M=
69+
github.com/hashicorp/terraform-plugin-log v0.6.0 h1:/Vq78uSIdUSZ3iqDc9PESKtwt8YqNKN6u+khD+lLjuw=
70+
github.com/hashicorp/terraform-plugin-log v0.6.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4=
7171
github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI=
7272
github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg=
7373
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
@@ -110,6 +110,8 @@ github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvC
110110
github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4=
111111
github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY=
112112
github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
113+
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
114+
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
113115
github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLEih+O3s=
114116
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
115117
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -134,6 +136,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
134136
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
135137
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
136138
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
139+
golang.org/x/net v0.0.0-20220708220712-1185a9018129 h1:vucSRfWwTsoXro7P+3Cjlr6flUMtzCwzlvkxEQtHHB0=
140+
golang.org/x/net v0.0.0-20220708220712-1185a9018129/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
137141
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
138142
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
139143
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -157,6 +161,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
157161
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
158162
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
159163
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
164+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
165+
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
160166
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
161167
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
162168
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@@ -174,6 +180,8 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
174180
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
175181
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
176182
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
183+
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
184+
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
177185
google.golang.org/genproto v0.0.0-20170818010345-ee236bd376b0/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
178186
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
179187
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
@@ -187,7 +195,7 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8
187195
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
188196
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
189197
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
190-
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
198+
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
191199
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY=
192200
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
193201
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=

int64validator/at_least_sum_of.go

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package int64validator
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/hashicorp/terraform-plugin-framework/attr"
9+
"github.com/hashicorp/terraform-plugin-framework/path"
10+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
11+
"github.com/hashicorp/terraform-plugin-framework/types"
12+
13+
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
14+
)
15+
16+
var _ tfsdk.AttributeValidator = atLeastSumOfValidator{}
17+
18+
// atLeastSumOfValidator validates that an integer Attribute's value is at least the sum of one
19+
// or more integer Attributes retrieved via the given path expressions.
20+
type atLeastSumOfValidator struct {
21+
attributesToSumPathExpressions path.Expressions
22+
}
23+
24+
// Description describes the validation in plain text formatting.
25+
func (av atLeastSumOfValidator) Description(_ context.Context) string {
26+
var attributePaths []string
27+
for _, p := range av.attributesToSumPathExpressions {
28+
attributePaths = append(attributePaths, p.String())
29+
}
30+
31+
return fmt.Sprintf("value must be at least sum of %s", strings.Join(attributePaths, " + "))
32+
}
33+
34+
// MarkdownDescription describes the validation in Markdown formatting.
35+
func (av atLeastSumOfValidator) MarkdownDescription(ctx context.Context) string {
36+
return av.Description(ctx)
37+
}
38+
39+
// Validate performs the validation.
40+
func (av atLeastSumOfValidator) Validate(ctx context.Context, request tfsdk.ValidateAttributeRequest, response *tfsdk.ValidateAttributeResponse) {
41+
i, ok := validateInt(ctx, request, response)
42+
if !ok {
43+
return
44+
}
45+
46+
// Ensure input path expressions resolution against the current attribute
47+
expressions := request.AttributePathExpression.MergeExpressions(av.attributesToSumPathExpressions...)
48+
49+
// Sum the value of all the attributes involved, but only if they are all known.
50+
var sumOfAttribs int64
51+
for _, expression := range expressions {
52+
matchedPaths, diags := request.Config.PathMatches(ctx, expression)
53+
response.Diagnostics.Append(diags...)
54+
55+
// Collect all errors
56+
if diags.HasError() {
57+
continue
58+
}
59+
60+
for _, mp := range matchedPaths {
61+
// If the user specifies the same attribute this validator is applied to,
62+
// also as part of the input, skip it
63+
if mp.Equal(request.AttributePath) {
64+
continue
65+
}
66+
67+
// Get the value
68+
var matchedValue attr.Value
69+
diags := request.Config.GetAttribute(ctx, mp, &matchedValue)
70+
response.Diagnostics.Append(diags...)
71+
if diags.HasError() {
72+
continue
73+
}
74+
75+
if matchedValue.IsUnknown() {
76+
return
77+
}
78+
79+
if matchedValue.IsNull() {
80+
continue
81+
}
82+
83+
// We know there is a value, convert it to the expected type
84+
var attribToSum types.Int64
85+
diags = tfsdk.ValueAs(ctx, matchedValue, &attribToSum)
86+
response.Diagnostics.Append(diags...)
87+
if diags.HasError() {
88+
continue
89+
}
90+
91+
sumOfAttribs += attribToSum.Value
92+
}
93+
}
94+
95+
if i < sumOfAttribs {
96+
response.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
97+
request.AttributePath,
98+
av.Description(ctx),
99+
fmt.Sprintf("%d", i),
100+
))
101+
102+
return
103+
}
104+
}
105+
106+
// AtLeastSumOf returns an AttributeValidator which ensures that any configured
107+
// attribute value:
108+
//
109+
// - Is a number, which can be represented by a 64-bit integer.
110+
// - Is at least the sum of the attributes retrieved via the given path expression(s).
111+
//
112+
// Null (unconfigured) and unknown (known after apply) values are skipped.
113+
func AtLeastSumOf(attributesToSumPathExpressions ...path.Expression) tfsdk.AttributeValidator {
114+
return atLeastSumOfValidator{attributesToSumPathExpressions}
115+
}
+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
package int64validator
2+
3+
import (
4+
"context"
5+
"testing"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/path"
9+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/hashicorp/terraform-plugin-go/tftypes"
12+
)
13+
14+
func TestAtLeastSumOfValidator(t *testing.T) {
15+
t.Parallel()
16+
17+
type testCase struct {
18+
val attr.Value
19+
attributesToSumExpressions path.Expressions
20+
requestConfigRaw map[string]tftypes.Value
21+
expectError bool
22+
}
23+
tests := map[string]testCase{
24+
"not an Int64": {
25+
val: types.Bool{Value: true},
26+
expectError: true,
27+
},
28+
"unknown Int64": {
29+
val: types.Int64{Unknown: true},
30+
},
31+
"null Int64": {
32+
val: types.Int64{Null: true},
33+
},
34+
"valid integer as Int64 less than sum of attributes": {
35+
val: types.Int64{Value: 10},
36+
attributesToSumExpressions: path.Expressions{
37+
path.MatchRoot("one"),
38+
path.MatchRoot("two"),
39+
},
40+
requestConfigRaw: map[string]tftypes.Value{
41+
"one": tftypes.NewValue(tftypes.Number, 15),
42+
"two": tftypes.NewValue(tftypes.Number, 15),
43+
},
44+
expectError: true,
45+
},
46+
"valid integer as Int64 equal to sum of attributes": {
47+
val: types.Int64{Value: 10},
48+
attributesToSumExpressions: path.Expressions{
49+
path.MatchRoot("one"),
50+
path.MatchRoot("two"),
51+
},
52+
requestConfigRaw: map[string]tftypes.Value{
53+
"one": tftypes.NewValue(tftypes.Number, 5),
54+
"two": tftypes.NewValue(tftypes.Number, 5),
55+
},
56+
},
57+
"valid integer as Int64 greater than sum of attributes": {
58+
val: types.Int64{Value: 10},
59+
attributesToSumExpressions: path.Expressions{
60+
path.MatchRoot("one"),
61+
path.MatchRoot("two"),
62+
},
63+
requestConfigRaw: map[string]tftypes.Value{
64+
"one": tftypes.NewValue(tftypes.Number, 4),
65+
"two": tftypes.NewValue(tftypes.Number, 4),
66+
},
67+
},
68+
"valid integer as Int64 greater than sum of attributes, when one summed attribute is null": {
69+
val: types.Int64{Value: 10},
70+
attributesToSumExpressions: path.Expressions{
71+
path.MatchRoot("one"),
72+
path.MatchRoot("two"),
73+
},
74+
requestConfigRaw: map[string]tftypes.Value{
75+
"one": tftypes.NewValue(tftypes.Number, nil),
76+
"two": tftypes.NewValue(tftypes.Number, 9),
77+
},
78+
},
79+
"valid integer as Int64 does not return error when all attributes are null": {
80+
val: types.Int64{Null: true},
81+
attributesToSumExpressions: path.Expressions{
82+
path.MatchRoot("one"),
83+
path.MatchRoot("two"),
84+
},
85+
requestConfigRaw: map[string]tftypes.Value{
86+
"one": tftypes.NewValue(tftypes.Number, nil),
87+
"two": tftypes.NewValue(tftypes.Number, nil),
88+
},
89+
},
90+
"valid integer as Int64 returns error when all attributes to sum are null": {
91+
val: types.Int64{Value: -1},
92+
attributesToSumExpressions: path.Expressions{
93+
path.MatchRoot("one"),
94+
path.MatchRoot("two"),
95+
},
96+
requestConfigRaw: map[string]tftypes.Value{
97+
"one": tftypes.NewValue(tftypes.Number, nil),
98+
"two": tftypes.NewValue(tftypes.Number, nil),
99+
},
100+
expectError: true,
101+
},
102+
"valid integer as Int64 greater than sum of attributes, when one summed attribute is unknown": {
103+
val: types.Int64{Value: 10},
104+
attributesToSumExpressions: path.Expressions{
105+
path.MatchRoot("one"),
106+
path.MatchRoot("two"),
107+
},
108+
requestConfigRaw: map[string]tftypes.Value{
109+
"one": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
110+
"two": tftypes.NewValue(tftypes.Number, 9),
111+
},
112+
},
113+
"valid integer as Int64 does not return error when all attributes are unknown": {
114+
val: types.Int64{Unknown: true},
115+
attributesToSumExpressions: path.Expressions{
116+
path.MatchRoot("one"),
117+
path.MatchRoot("two"),
118+
},
119+
requestConfigRaw: map[string]tftypes.Value{
120+
"one": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
121+
"two": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
122+
},
123+
},
124+
"valid integer as Int64 does not return error when all attributes to sum are unknown": {
125+
val: types.Int64{Value: -1},
126+
attributesToSumExpressions: path.Expressions{
127+
path.MatchRoot("one"),
128+
path.MatchRoot("two"),
129+
},
130+
requestConfigRaw: map[string]tftypes.Value{
131+
"one": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
132+
"two": tftypes.NewValue(tftypes.Number, tftypes.UnknownValue),
133+
},
134+
},
135+
"error when attribute to sum is not Number": {
136+
val: types.Int64{Value: 9},
137+
attributesToSumExpressions: path.Expressions{
138+
path.MatchRoot("one"),
139+
path.MatchRoot("two"),
140+
},
141+
requestConfigRaw: map[string]tftypes.Value{
142+
"one": tftypes.NewValue(tftypes.Bool, true),
143+
"two": tftypes.NewValue(tftypes.Number, 9),
144+
},
145+
expectError: true,
146+
},
147+
}
148+
149+
for name, test := range tests {
150+
name, test := name, test
151+
t.Run(name, func(t *testing.T) {
152+
request := tfsdk.ValidateAttributeRequest{
153+
AttributePath: path.Root("test"),
154+
AttributePathExpression: path.MatchRoot("test"),
155+
AttributeConfig: test.val,
156+
Config: tfsdk.Config{
157+
Raw: tftypes.NewValue(tftypes.Object{}, test.requestConfigRaw),
158+
Schema: tfsdk.Schema{
159+
Attributes: map[string]tfsdk.Attribute{
160+
"test": {Type: types.Int64Type},
161+
"one": {Type: types.Int64Type},
162+
"two": {Type: types.Int64Type},
163+
},
164+
},
165+
},
166+
}
167+
168+
response := tfsdk.ValidateAttributeResponse{}
169+
170+
AtLeastSumOf(test.attributesToSumExpressions...).Validate(context.Background(), request, &response)
171+
172+
if !response.Diagnostics.HasError() && test.expectError {
173+
t.Fatal("expected error, got no error")
174+
}
175+
176+
if response.Diagnostics.HasError() && !test.expectError {
177+
t.Fatalf("got unexpected error: %s", response.Diagnostics)
178+
}
179+
})
180+
}
181+
}

0 commit comments

Comments
 (0)