Skip to content

Commit 17de7ec

Browse files
extract: add ExtractBoolValue
This introduces a new state check that extracts an underlying attribute value by traversing the state with tfjsonpath using a specified path.
1 parent aae37aa commit 17de7ec

File tree

2 files changed

+200
-0
lines changed

2 files changed

+200
-0
lines changed

statecheck/extract_bool_value.go

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
package statecheck
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
tfjson "github.com/hashicorp/terraform-json"
8+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
9+
)
10+
11+
var _ StateCheck = extractBoolValue{}
12+
13+
type extractBoolValue struct {
14+
resourceAddress string
15+
attributePath tfjsonpath.Path
16+
targetVar *bool
17+
}
18+
19+
func (e extractBoolValue) CheckState(ctx context.Context, req CheckStateRequest, resp *CheckStateResponse) {
20+
var resource *tfjson.StateResource
21+
22+
if req.State == nil {
23+
resp.Error = fmt.Errorf("state is nil")
24+
25+
return
26+
}
27+
28+
if req.State.Values == nil {
29+
resp.Error = fmt.Errorf("state does not contain any state values")
30+
31+
return
32+
}
33+
34+
if req.State.Values.RootModule == nil {
35+
resp.Error = fmt.Errorf("state does not contain a root module")
36+
37+
return
38+
}
39+
40+
for _, r := range req.State.Values.RootModule.Resources {
41+
if e.resourceAddress == r.Address {
42+
resource = r
43+
44+
break
45+
}
46+
}
47+
48+
if resource == nil {
49+
resp.Error = fmt.Errorf("%s - Resource not found in state", e.resourceAddress)
50+
51+
return
52+
}
53+
54+
result, err := tfjsonpath.Traverse(resource.AttributeValues, e.attributePath)
55+
if err != nil {
56+
resp.Error = err
57+
58+
return
59+
}
60+
61+
if result == nil {
62+
resp.Error = fmt.Errorf("attribute '%s' not found in '%s'", e.attributePath, e.resourceAddress)
63+
64+
return
65+
}
66+
67+
switch t := result.(type) {
68+
case bool:
69+
*e.targetVar = t
70+
default:
71+
resp.Error = fmt.Errorf("invalid type for attribute '%s' in '%s'. Expected: bool, Got: %T", e.attributePath, e.resourceAddress, t)
72+
return
73+
}
74+
}
75+
76+
func ExtractBoolValue(resourceAddress string, attributePath tfjsonpath.Path, targetVar *bool) StateCheck {
77+
return extractBoolValue{
78+
resourceAddress: resourceAddress,
79+
attributePath: attributePath,
80+
targetVar: targetVar,
81+
}
82+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package statecheck_test
5+
6+
import (
7+
"fmt"
8+
"regexp"
9+
"testing"
10+
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
12+
13+
r "github.com/hashicorp/terraform-plugin-testing/helper/resource"
14+
"github.com/hashicorp/terraform-plugin-testing/statecheck"
15+
"github.com/hashicorp/terraform-plugin-testing/terraform"
16+
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
17+
)
18+
19+
func TestExtractBoolValue_CheckState_Basic(t *testing.T) {
20+
t.Parallel()
21+
22+
var targetVar bool
23+
24+
r.Test(t, r.TestCase{
25+
ProviderFactories: map[string]func() (*schema.Provider, error){
26+
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
27+
return testProvider(), nil
28+
},
29+
},
30+
Steps: []r.TestStep{
31+
{
32+
Config: `resource "test_resource" "one" {
33+
bool_attribute = false
34+
}
35+
`,
36+
ConfigStateChecks: []statecheck.StateCheck{
37+
statecheck.ExtractBoolValue(
38+
"test_resource.one",
39+
tfjsonpath.New("bool_attribute"),
40+
&targetVar,
41+
),
42+
},
43+
Check: testAccAssertBoolEquals(false, targetVar),
44+
},
45+
},
46+
})
47+
}
48+
49+
func TestExtractBoolValue_CheckState_KnownValueWrongType(t *testing.T) {
50+
t.Parallel()
51+
52+
var targetVar bool
53+
54+
r.Test(t, r.TestCase{
55+
ProviderFactories: map[string]func() (*schema.Provider, error){
56+
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
57+
return testProvider(), nil
58+
},
59+
},
60+
Steps: []r.TestStep{
61+
{
62+
Config: `resource "test_resource" "one" {
63+
float_attribute = 1.23
64+
}
65+
`,
66+
ConfigStateChecks: []statecheck.StateCheck{
67+
statecheck.ExtractBoolValue(
68+
"test_resource.one",
69+
tfjsonpath.New("float_attribute"),
70+
&targetVar,
71+
),
72+
},
73+
ExpectError: regexp.MustCompile(`invalid type for attribute \'float_attribute\' in \'test_resource\.one\'. Expected: bool, Got: json\.Number`),
74+
},
75+
},
76+
})
77+
}
78+
79+
func TestExtractBoolValue_CheckState_Null(t *testing.T) {
80+
t.Parallel()
81+
82+
var targetVar bool
83+
84+
r.Test(t, r.TestCase{
85+
ProviderFactories: map[string]func() (*schema.Provider, error){
86+
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
87+
return testProvider(), nil
88+
},
89+
},
90+
Steps: []r.TestStep{
91+
{
92+
Config: `resource "test_resource" "one" {
93+
bool_attribute = null
94+
}
95+
`,
96+
ConfigStateChecks: []statecheck.StateCheck{
97+
statecheck.ExtractBoolValue(
98+
"test_resource.one",
99+
tfjsonpath.New("bool_attribute"),
100+
&targetVar,
101+
),
102+
},
103+
ExpectError: regexp.MustCompile(`invalid type for attribute \'float_attribute\' in \'test_resource\.one\'. Expected: bool, Got: json\.Number`),
104+
},
105+
},
106+
})
107+
}
108+
109+
// testAccAssertBoolEquals compares the expected and target bool values.
110+
func testAccAssertBoolEquals(expected bool, targetVar bool) r.TestCheckFunc {
111+
return func(s *terraform.State) error {
112+
if targetVar != expected {
113+
return fmt.Errorf("expected targetVar to be %v, got %v", expected, targetVar)
114+
}
115+
116+
return nil
117+
}
118+
}

0 commit comments

Comments
 (0)