From 758daeda84e19140724eac7c99085dbb24b8a7d1 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Mon, 25 Mar 2024 12:04:29 -0400 Subject: [PATCH 1/4] knownvalue: Add `Tuple*` equivalents for exact, partial, and size checks --- knownvalue/tuple.go | 67 +++++++++++++++ knownvalue/tuple_partial.go | 89 ++++++++++++++++++++ knownvalue/tuple_partial_test.go | 138 +++++++++++++++++++++++++++++++ knownvalue/tuple_size.go | 55 ++++++++++++ knownvalue/tuple_size_test.go | 87 +++++++++++++++++++ knownvalue/tuple_test.go | 137 ++++++++++++++++++++++++++++++ 6 files changed, 573 insertions(+) create mode 100644 knownvalue/tuple.go create mode 100644 knownvalue/tuple_partial.go create mode 100644 knownvalue/tuple_partial_test.go create mode 100644 knownvalue/tuple_size.go create mode 100644 knownvalue/tuple_size_test.go create mode 100644 knownvalue/tuple_test.go diff --git a/knownvalue/tuple.go b/knownvalue/tuple.go new file mode 100644 index 00000000..c034bba7 --- /dev/null +++ b/knownvalue/tuple.go @@ -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, + } +} diff --git a/knownvalue/tuple_partial.go b/knownvalue/tuple_partial.go new file mode 100644 index 00000000..cfd73b2d --- /dev/null +++ b/knownvalue/tuple_partial.go @@ -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, + } +} diff --git a/knownvalue/tuple_partial_test.go b/knownvalue/tuple_partial_test.go new file mode 100644 index 00000000..d7485756 --- /dev/null +++ b/knownvalue/tuple_partial_test.go @@ -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: "), + }, + "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: "), + }, + "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 TestTuplePartialPartial_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) + } +} diff --git a/knownvalue/tuple_size.go b/knownvalue/tuple_size.go new file mode 100644 index 00000000..a2c2dce3 --- /dev/null +++ b/knownvalue/tuple_size.go @@ -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, + } +} diff --git a/knownvalue/tuple_size_test.go b/knownvalue/tuple_size_test.go new file mode 100644 index 00000000..7e8388bd --- /dev/null +++ b/knownvalue/tuple_size_test.go @@ -0,0 +1,87 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package knownvalue_test + +import ( + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/hashicorp/terraform-plugin-testing/knownvalue" +) + +func TestTupleSizeExact_CheckValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + self knownvalue.Check + other any + expectedError error + }{ + "zero-nil": { + self: knownvalue.TupleSizeExact(0), + expectedError: fmt.Errorf("expected []any value for TupleSizeExact check, got: "), + }, + "zero-other": { + self: knownvalue.TupleSizeExact(0), + other: []any{}, // checking against the underlying value field zero-value + }, + "nil": { + self: knownvalue.TupleSizeExact(3), + expectedError: fmt.Errorf("expected []any value for TupleSizeExact check, got: "), + }, + "wrong-type": { + self: knownvalue.TupleSizeExact(3), + other: 1.234, + expectedError: fmt.Errorf("expected []any value for TupleSizeExact check, got: float64"), + }, + "empty": { + self: knownvalue.TupleSizeExact(3), + other: []any{}, + expectedError: fmt.Errorf("expected 3 elements for TupleSizeExact check, got 0 elements"), + }, + "wrong-length": { + self: knownvalue.TupleSizeExact(4), + other: []any{ + int64(123), + "hello", + true, + }, + expectedError: fmt.Errorf("expected 4 elements for TupleSizeExact check, got 3 elements"), + }, + "equal": { + self: knownvalue.TupleSizeExact(3), + other: []any{ + int64(123), + "hello", + 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 TestTupleSizeExact_String(t *testing.T) { + t.Parallel() + + got := knownvalue.TupleSizeExact(2).String() + + if diff := cmp.Diff(got, "2"); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } +} diff --git a/knownvalue/tuple_test.go b/knownvalue/tuple_test.go new file mode 100644 index 00000000..f1dfdd60 --- /dev/null +++ b/knownvalue/tuple_test.go @@ -0,0 +1,137 @@ +// 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 TestTupleExact_CheckValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + self knownvalue.Check + other any + expectedError error + }{ + "zero-nil": { + self: knownvalue.TupleExact([]knownvalue.Check{}), + expectedError: fmt.Errorf("expected []any value for TupleExact check, got: "), + }, + "zero-other": { + self: knownvalue.TupleExact([]knownvalue.Check{}), + other: []any{}, // checking against the underlying value field zero-value + }, + "nil": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + expectedError: fmt.Errorf("expected []any value for TupleExact check, got: "), + }, + "wrong-type": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + other: 1.234, + expectedError: fmt.Errorf("expected []any value for TupleExact check, got: float64"), + }, + "empty": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + other: []any{}, + expectedError: fmt.Errorf("expected 3 elements for TupleExact check, got 0 elements"), + }, + "wrong-length": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + other: []any{ + json.Number("123"), + true, + }, + expectedError: fmt.Errorf("expected 3 elements for TupleExact check, got 2 elements"), + }, + "not-equal": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + other: []any{ + json.Number("123"), + true, + "goodbye", + }, + expectedError: fmt.Errorf("tuple element index 2: expected value hello for StringExact check, got: goodbye"), + }, + "wrong-order": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + other: []any{ + json.Number("123"), + "hello", + true, + }, + expectedError: fmt.Errorf("tuple element index 1: expected bool value for Bool check, got: string"), + }, + "equal": { + self: knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }), + other: []any{ + json.Number("123"), + true, + "hello", + }, + }, + } + + 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 TestTupleExact_String(t *testing.T) { + t.Parallel() + + got := knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Int64Exact(123), + knownvalue.Bool(true), + knownvalue.StringExact("hello"), + }).String() + + if diff := cmp.Diff(got, "[123 true hello]"); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } +} From 915763b4299590b8cafea1c2f5586cbeae34ba88 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Mon, 25 Mar 2024 12:05:45 -0400 Subject: [PATCH 2/4] add changelog --- .changes/unreleased/ENHANCEMENTS-20240325-120539.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changes/unreleased/ENHANCEMENTS-20240325-120539.yaml diff --git a/.changes/unreleased/ENHANCEMENTS-20240325-120539.yaml b/.changes/unreleased/ENHANCEMENTS-20240325-120539.yaml new file mode 100644 index 00000000..ad24b19a --- /dev/null +++ b/.changes/unreleased/ENHANCEMENTS-20240325-120539.yaml @@ -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" From 29d51b59b4c4ca8099679c091c397bcbb322c483 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Mon, 25 Mar 2024 17:50:29 -0400 Subject: [PATCH 3/4] add documentation for tuples --- website/data/plugin-testing-nav-data.json | 4 + .../known-value-checks/custom.mdx | 1 + .../known-value-checks/index.mdx | 1 + .../known-value-checks/tuple.mdx | 123 ++++++++++++++++++ 4 files changed, 129 insertions(+) create mode 100644 website/docs/plugin/testing/acceptance-tests/known-value-checks/tuple.mdx diff --git a/website/data/plugin-testing-nav-data.json b/website/data/plugin-testing-nav-data.json index bb974004..bac221d3 100644 --- a/website/data/plugin-testing-nav-data.json +++ b/website/data/plugin-testing-nav-data.json @@ -125,6 +125,10 @@ { "title": "String", "path": "acceptance-tests/known-value-checks/string" + }, + { + "title": "Tuple", + "path": "acceptance-tests/known-value-checks/tuple" } ] }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/custom.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/custom.mdx index de0b6401..b577eb8c 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/custom.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/custom.mdx @@ -71,3 +71,4 @@ Refer to the following built-in known value checks for implementations that hand * [ObjectExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#ObjectExact) * [SetExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#SetExact) * [StringExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#StringExact) +* [TupleExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TupleExact) diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/index.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/index.mdx index 2d06a3d7..62db10d5 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/index.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/index.mdx @@ -49,4 +49,5 @@ The following table shows the correspondence between [knownvalue.Check](https:// | [Object Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/object) | `schema.ObjectAttribute` | N/A | | [Set Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/set) | `schema.SetAttribute` | `schema.TypeSet` | | [String Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/string) | `schema.StringAttribute` | `schema.TypeString` | +| [Tuple Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/tuple) | `schema.DynamicAttribute` | N/A | diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/tuple.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/tuple.mdx new file mode 100644 index 00000000..95129a23 --- /dev/null +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/tuple.mdx @@ -0,0 +1,123 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Known Values' +description: >- + Tuple Value Checks for use with Plan Checks. +--- + +# Tuple Known Value Checks + + + +Provider developers will only encounter tuples when testing [dynamic data values](/terraform/plugin/framework/handling-data/dynamic-data). + + + +The known value checks that are available for tuple values are: + +* [TupleExact](/terraform/plugin/testing/acceptance-tests/known-value-checks/tuple#tupleexact-check) +* [TuplePartial](/terraform/plugin/testing/acceptance-tests/known-value-checks/tuple#tuplepartial-check) +* [TupleSizeExact](/terraform/plugin/testing/acceptance-tests/known-value-checks/tuple#tuplesizeexact-check) + +## `TupleExact` Check + +The [TupleExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TupleExact) check tests that a resource attribute, or output value has an order-dependent, matching collection of element values. + +Example usage of [TupleExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TupleExact) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/plan-checks/resource) plan check. + +```go +func TestExpectKnownValue_CheckPlan_Tuple(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example resource containing a required dynamic attribute named "example_attribute" + Config: `resource "test_resource" "one" { + example_attribute = [true, "hello world"] + }`, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("example_attribute"), + knownvalue.TupleExact([]knownvalue.Check{ + knownvalue.Bool(true), + knownvalue.StringExact("hello world"), + }), + ), + }, + }, + }, + }, + }) +} +``` + +## `TuplePartial` Check + +The [TuplePartial](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TuplePartial) check tests that a resource attribute, or output value has matching element values for the specified collection indices. + +Example usage of [TuplePartial](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TuplePartial) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/plan-checks/resource) plan check. In this example, only the second element within the tuple, the element defined at index `1`, is checked. + +```go +func TestExpectKnownValue_CheckPlan_TuplePartial(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example resource containing a required dynamic attribute named "example_attribute" + Config: `resource "test_resource" "one" { + example_attribute = [true, "hello world"] + }`, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("example_attribute"), + knownvalue.TuplePartial(map[int]knownvalue.Check{ + 1: knownvalue.StringExact("hello world"), + }), + ), + }, + }, + }, + }, + }) +} +``` + +## `TupleSizeExact` Check + +The [TupleSizeExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TupleSizeExact) check tests that a resource attribute, or output value contains the specified number of elements. + +Example usage of [TupleSizeExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#TupleSizeExact) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/plan-checks/resource) plan check. + +```go +func TestExpectKnownValue_CheckPlan_TupleElements(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example resource containing a required dynamic attribute named "example_attribute" + Config: `resource "test_resource" "one" { + example_attribute = [true, "hello world"] + }`, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("example_attribute"), + knownvalue.TupleSizeExact(2), + ), + }, + }, + }, + }, + }) +} +``` From cfa5aa015c29e8cd1603ad939b4e2ed44d7049b0 Mon Sep 17 00:00:00 2001 From: Austin Valle Date: Mon, 25 Mar 2024 17:57:45 -0400 Subject: [PATCH 4/4] test name dup --- knownvalue/list_partial_test.go | 2 +- knownvalue/tuple_partial_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/knownvalue/list_partial_test.go b/knownvalue/list_partial_test.go index dd95b49e..31cb6163 100644 --- a/knownvalue/list_partial_test.go +++ b/knownvalue/list_partial_test.go @@ -125,7 +125,7 @@ func TestListValuePartial_CheckValue(t *testing.T) { } } -func TestListValuePartialPartial_String(t *testing.T) { +func TestListValuePartial_String(t *testing.T) { t.Parallel() got := knownvalue.ListPartial(map[int]knownvalue.Check{ diff --git a/knownvalue/tuple_partial_test.go b/knownvalue/tuple_partial_test.go index d7485756..4935c9f3 100644 --- a/knownvalue/tuple_partial_test.go +++ b/knownvalue/tuple_partial_test.go @@ -123,7 +123,7 @@ func TestTuplePartial_CheckValue(t *testing.T) { } } -func TestTuplePartialPartial_String(t *testing.T) { +func TestTuplePartial_String(t *testing.T) { t.Parallel() got := knownvalue.TuplePartial(map[int]knownvalue.Check{