Skip to content

Commit afa8402

Browse files
authored
helper/resource: Additional Go documentation for TypeSet TestCheckFunc (#916)
Reference: hashicorp/terraform-plugin-sdk#911 Additional glow up for the `TypeSet` functions, similar to the others.
1 parent ab84f8b commit afa8402

File tree

2 files changed

+291
-28
lines changed

2 files changed

+291
-28
lines changed

helper/resource/testing_sets.go

Lines changed: 132 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,25 +14,46 @@ const (
1414
sentinelIndex = "*"
1515
)
1616

17-
// TestCheckTypeSetElemNestedAttrs is a TestCheckFunc that accepts a resource
18-
// name, an attribute path, which should use the sentinel value '*' for indexing
19-
// into a TypeSet. The function verifies that an element matches the whole value
20-
// map.
17+
// TestCheckTypeSetElemNestedAttrs ensures a subset map of values is stored in
18+
// state for the given name and key combination of attributes nested under a
19+
// list or set block. Use this TestCheckFunc in preference over non-set
20+
// variants to simplify testing code and ensure compatibility with indicies,
21+
// which can easily change with schema changes. State value checking is only
22+
// recommended for testing Computed attributes and attribute defaults.
2123
//
22-
// You may check for unset keys, however this will also match keys set to empty
23-
// string. Please provide a map with at least 1 non-empty value.
24+
// For managed resources, the name parameter is a combination of the resource
25+
// type, a period (.), and the name label. The name for the below example
26+
// configuration would be "myprovider_thing.example".
2427
//
25-
// map[string]string{
26-
// "key1": "value",
27-
// "key2": "",
28-
// }
28+
// resource "myprovider_thing" "example" { ... }
2929
//
30-
// Use this function over SDK provided TestCheckFunctions when validating a
31-
// TypeSet where its elements are a nested object with their own attrs/values.
30+
// For data sources, the name parameter is a combination of the keyword "data",
31+
// a period (.), the data source type, a period (.), and the name label. The
32+
// name for the below example configuration would be
33+
// "data.myprovider_thing.example".
34+
//
35+
// data "myprovider_thing" "example" { ... }
36+
//
37+
// The key parameter is an attribute path in Terraform CLI 0.11 and earlier
38+
// "flatmap" syntax. Keys start with the attribute name of a top-level
39+
// attribute. Use the sentinel value '*' to replace the element indexing into
40+
// a list or set. The sentinel value can be used for each list or set index, if
41+
// there are multiple lists or sets in the attribute path.
42+
//
43+
// The values parameter is the map of attribute names to attribute values
44+
// expected to be nested under the list or set.
45+
//
46+
// You may check for unset nested attributes, however this will also match keys
47+
// set to an empty string. Use a map with at least 1 non-empty value.
3248
//
33-
// Please note, if the provided value map is not granular enough, there exists
34-
// the possibility you match an element you were not intending to, in the TypeSet.
35-
// Provide a full mapping of attributes to be sure the unique element exists.
49+
// map[string]string{
50+
// "key1": "value",
51+
// "key2": "",
52+
// }
53+
//
54+
// If the values map is not granular enough, it is possible to match an element
55+
// you were not intending to in the set. Provide the most complete mapping of
56+
// attributes possible to be sure the unique element exists.
3657
func TestCheckTypeSetElemNestedAttrs(name, attr string, values map[string]string) TestCheckFunc {
3758
return func(s *terraform.State) error {
3859
is, err := primaryInstanceState(s, name)
@@ -64,17 +85,47 @@ func TestCheckTypeSetElemNestedAttrs(name, attr string, values map[string]string
6485
}
6586
}
6687

67-
// TestMatchTypeSetElemNestedAttrs is a TestCheckFunc similar to TestCheckTypeSetElemNestedAttrs
68-
// with the exception that it verifies that an element matches a *regexp.Regexp.
88+
// TestMatchTypeSetElemNestedAttrs ensures a subset map of values, compared by
89+
// regular expressions, is stored in state for the given name and key
90+
// combination of attributes nested under a list or set block. Use this
91+
// TestCheckFunc in preference over non-set variants to simplify testing code
92+
// and ensure compatibility with indicies, which can easily change with schema
93+
// changes. State value checking is only recommended for testing Computed
94+
// attributes and attribute defaults.
95+
//
96+
// For managed resources, the name parameter is a combination of the resource
97+
// type, a period (.), and the name label. The name for the below example
98+
// configuration would be "myprovider_thing.example".
99+
//
100+
// resource "myprovider_thing" "example" { ... }
101+
//
102+
// For data sources, the name parameter is a combination of the keyword "data",
103+
// a period (.), the data source type, a period (.), and the name label. The
104+
// name for the below example configuration would be
105+
// "data.myprovider_thing.example".
106+
//
107+
// data "myprovider_thing" "example" { ... }
108+
//
109+
// The key parameter is an attribute path in Terraform CLI 0.11 and earlier
110+
// "flatmap" syntax. Keys start with the attribute name of a top-level
111+
// attribute. Use the sentinel value '*' to replace the element indexing into
112+
// a list or set. The sentinel value can be used for each list or set index, if
113+
// there are multiple lists or sets in the attribute path.
69114
//
70-
// You may check for unset keys, however this will also match keys set to empty
71-
// string. Please provide a map with at least 1 non-empty value e.g.
115+
// The values parameter is the map of attribute names to regular expressions
116+
// for matching attribute values expected to be nested under the list or set.
72117
//
73-
// map[string]*regexp.Regexp{
74-
// "key1": regexp.MustCompile("value"),
75-
// "key2": regexp.MustCompile(""),
76-
// }
118+
// You may check for unset nested attributes, however this will also match keys
119+
// set to an empty string. Use a map with at least 1 non-empty value.
77120
//
121+
// map[string]*regexp.Regexp{
122+
// "key1": regexp.MustCompile(`^value`),
123+
// "key2": regexp.MustCompile(`^$`),
124+
// }
125+
//
126+
// If the values map is not granular enough, it is possible to match an element
127+
// you were not intending to in the set. Provide the most complete mapping of
128+
// attributes possible to be sure the unique element exists.
78129
func TestMatchTypeSetElemNestedAttrs(name, attr string, values map[string]*regexp.Regexp) TestCheckFunc {
79130
return func(s *terraform.State) error {
80131
is, err := primaryInstanceState(s, name)
@@ -113,6 +164,39 @@ func TestMatchTypeSetElemNestedAttrs(name, attr string, values map[string]*regex
113164
//
114165
// Use this function over SDK provided TestCheckFunctions when validating a
115166
// TypeSet where its elements are a simple value
167+
168+
// TestCheckTypeSetElemAttr ensures a specific value is stored in state for the
169+
// given name and key combination under a list or set. Use this TestCheckFunc
170+
// in preference over non-set variants to simplify testing code and ensure
171+
// compatibility with indicies, which can easily change with schema changes.
172+
// State value checking is only recommended for testing Computed attributes and
173+
// attribute defaults.
174+
//
175+
// For managed resources, the name parameter is a combination of the resource
176+
// type, a period (.), and the name label. The name for the below example
177+
// configuration would be "myprovider_thing.example".
178+
//
179+
// resource "myprovider_thing" "example" { ... }
180+
//
181+
// For data sources, the name parameter is a combination of the keyword "data",
182+
// a period (.), the data source type, a period (.), and the name label. The
183+
// name for the below example configuration would be
184+
// "data.myprovider_thing.example".
185+
//
186+
// data "myprovider_thing" "example" { ... }
187+
//
188+
// The key parameter is an attribute path in Terraform CLI 0.11 and earlier
189+
// "flatmap" syntax. Keys start with the attribute name of a top-level
190+
// attribute. Use the sentinel value '*' to replace the element indexing into
191+
// a list or set. The sentinel value can be used for each list or set index, if
192+
// there are multiple lists or sets in the attribute path.
193+
//
194+
// The value parameter is the stringified data to check at the given key. Use
195+
// the following attribute type rules to set the value:
196+
//
197+
// - Boolean: "false" or "true".
198+
// - Float/Integer: Stringified number, such as "1.2" or "123".
199+
// - String: No conversion necessary.
116200
func TestCheckTypeSetElemAttr(name, attr, value string) TestCheckFunc {
117201
return func(s *terraform.State) error {
118202
is, err := primaryInstanceState(s, name)
@@ -129,12 +213,32 @@ func TestCheckTypeSetElemAttr(name, attr, value string) TestCheckFunc {
129213
}
130214
}
131215

132-
// TestCheckTypeSetElemAttrPair is a TestCheckFunc that verifies a pair of name/key
133-
// combinations are equal where the first uses the sentinel value to index into a
134-
// TypeSet.
216+
// TestCheckTypeSetElemAttrPair ensures value equality in state between the
217+
// first given name and key combination and the second name and key
218+
// combination. State value checking is only recommended for testing Computed
219+
// attributes and attribute defaults.
220+
//
221+
// For managed resources, the name parameter is a combination of the resource
222+
// type, a period (.), and the name label. The name for the below example
223+
// configuration would be "myprovider_thing.example".
224+
//
225+
// resource "myprovider_thing" "example" { ... }
226+
//
227+
// For data sources, the name parameter is a combination of the keyword "data",
228+
// a period (.), the data source type, a period (.), and the name label. The
229+
// name for the below example configuration would be
230+
// "data.myprovider_thing.example".
231+
//
232+
// data "myprovider_thing" "example" { ... }
233+
//
234+
// The first and second names may use any combination of managed resources
235+
// and/or data sources.
135236
//
136-
// E.g., TestCheckTypeSetElemAttrPair("aws_autoscaling_group.bar", "availability_zones.*", "data.aws_availability_zones.available", "names.0")
137-
// E.g., TestCheckTypeSetElemAttrPair("aws_spot_fleet_request.bar", "launch_specification.*.instance_type", "data.data.aws_ec2_instance_type_offering.available", "instance_type")
237+
// The key parameter is an attribute path in Terraform CLI 0.11 and earlier
238+
// "flatmap" syntax. Keys start with the attribute name of a top-level
239+
// attribute. Use the sentinel value '*' to replace the element indexing into
240+
// a list or set. The sentinel value can be used for each list or set index, if
241+
// there are multiple lists or sets in the attribute path.
138242
func TestCheckTypeSetElemAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc {
139243
return func(s *terraform.State) error {
140244
isFirst, err := primaryInstanceState(s, nameFirst)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
package resource_test
2+
3+
import (
4+
"regexp"
5+
6+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
7+
)
8+
9+
func ExampleTestCheckTypeSetElemAttr() {
10+
// This function is typically implemented in a TestStep type Check field,
11+
// wrapped with ComposeAggregateTestCheckFunc to combine results from
12+
// multiple checks.
13+
//
14+
// Given the following example configuration:
15+
//
16+
// resource "example_thing" "test" {
17+
// example_set_attribute = ["value1", "value2", "value3"]
18+
// }
19+
//
20+
// The following TestCheckTypeSetElemAttr can be written to assert against
21+
// the expected state values.
22+
//
23+
// NOTE: State value checking is only necessary for Computed attributes,
24+
// as the testing framework will automatically return test failures
25+
// for configured attributes that mismatch the saved state, however
26+
// this configuration and test is shown for illustrative purposes.
27+
resource.TestCheckTypeSetElemAttr("example_thing.test", "example_set_attribute.*", "value1")
28+
resource.TestCheckTypeSetElemAttr("example_thing.test", "example_set_attribute.*", "value2")
29+
resource.TestCheckTypeSetElemAttr("example_thing.test", "example_set_attribute.*", "value3")
30+
}
31+
32+
func ExampleTestCheckTypeSetElemNestedAttrs() {
33+
// This function is typically implemented in a TestStep type Check field,
34+
// wrapped with ComposeAggregateTestCheckFunc to combine results from
35+
// multiple checks.
36+
//
37+
// Given the following example configuration:
38+
//
39+
// resource "example_thing" "test" {
40+
// example_set_block {
41+
// key1 = "value1a"
42+
// key2 = "value2a"
43+
// key3 = "value3a"
44+
// }
45+
//
46+
// example_set_block {
47+
// key1 = "value1b"
48+
// key2 = "value2b"
49+
// key3 = "value3b"
50+
// }
51+
// }
52+
//
53+
// The following TestCheckTypeSetElemNestedAttrs can be written to assert
54+
// against the expected state values.
55+
//
56+
// NOTE: State value checking is only necessary for Computed attributes,
57+
// as the testing framework will automatically return test failures
58+
// for configured attributes that mismatch the saved state, however
59+
// this configuration and test is shown for illustrative purposes.
60+
resource.TestCheckTypeSetElemNestedAttrs(
61+
"example_thing.test",
62+
"example_set_block.*",
63+
map[string]string{
64+
"key1": "value1a",
65+
"key2": "value2a",
66+
"key3": "value3a",
67+
},
68+
)
69+
resource.TestCheckTypeSetElemNestedAttrs(
70+
"example_thing.test",
71+
"example_set_block.*",
72+
map[string]string{
73+
"key1": "value1b",
74+
"key2": "value2b",
75+
"key3": "value3b",
76+
},
77+
)
78+
}
79+
80+
func ExampleTestCheckTypeSetElemAttrPair() {
81+
// This function is typically implemented in a TestStep type Check field,
82+
// wrapped with ComposeAggregateTestCheckFunc to combine results from
83+
// multiple checks.
84+
//
85+
// Given the following example configuration:
86+
//
87+
// data "example_lookup" "test" {
88+
// example_string_attribute = "test-value"
89+
// }
90+
//
91+
// resource "example_thing" "test" {
92+
// example_set_attribute = [
93+
// data.example_lookup.test.example_string_attribute,
94+
// "another-test-value",
95+
// ]
96+
// }
97+
//
98+
// The following TestCheckTypeSetElemAttrPair can be written to assert
99+
// against the expected state values.
100+
//
101+
// NOTE: State value checking is only necessary for Computed attributes,
102+
// as the testing framework will automatically return test failures
103+
// for configured attributes that mismatch the saved state, however
104+
// this configuration and test is shown for illustrative purposes.
105+
resource.TestCheckTypeSetElemAttrPair(
106+
"example_thing.test",
107+
"example_set_attribute.*",
108+
"data.example_lookup.test",
109+
"example_string_attribute",
110+
)
111+
}
112+
113+
func ExampleTestMatchTypeSetElemNestedAttrs() {
114+
// This function is typically implemented in a TestStep type Check field,
115+
// wrapped with ComposeAggregateTestCheckFunc to combine results from
116+
// multiple checks.
117+
//
118+
// Given the following example configuration:
119+
//
120+
// resource "example_thing" "test" {
121+
// example_set_block {
122+
// key1 = "value1a"
123+
// key2 = "value2a"
124+
// key3 = "value3a"
125+
// }
126+
//
127+
// example_set_block {
128+
// key1 = "value1b"
129+
// key2 = "value2b"
130+
// key3 = "value3b"
131+
// }
132+
// }
133+
//
134+
// The following TestMatchTypeSetElemNestedAttrs can be written to assert
135+
// against the expected state values.
136+
//
137+
// NOTE: State value checking is only necessary for Computed attributes,
138+
// as the testing framework will automatically return test failures
139+
// for configured attributes that mismatch the saved state, however
140+
// this configuration and test is shown for illustrative purposes.
141+
resource.TestMatchTypeSetElemNestedAttrs(
142+
"example_thing.test",
143+
"example_set_block.*",
144+
map[string]*regexp.Regexp{
145+
"key1": regexp.MustCompile(`1a$`),
146+
"key2": regexp.MustCompile(`2a$`),
147+
"key3": regexp.MustCompile(`3a$`),
148+
},
149+
)
150+
resource.TestMatchTypeSetElemNestedAttrs(
151+
"example_thing.test",
152+
"example_set_block.*",
153+
map[string]*regexp.Regexp{
154+
"key1": regexp.MustCompile(`1b$`),
155+
"key2": regexp.MustCompile(`2b$`),
156+
"key3": regexp.MustCompile(`3b$`),
157+
},
158+
)
159+
}

0 commit comments

Comments
 (0)