Skip to content

Commit

Permalink
Implementing expect unknown and null output value plan checks (#220)
Browse files Browse the repository at this point in the history
* Implementing expect unknown output value plan check (#219)

* Linting (#219)

* Implementing expect null output value plan check (#219)

* Updating plan checks docs (#219)

* Adding changelog entries (#219)

* Apply suggestions from code review

Co-authored-by: Selena Goods <[email protected]>

* Updates following code review (#219)

* Adding copyright (#219)

* Splitting unknown and null output value plan checks into Expect<Null|Unknown>OutputValue and Expect<Null|Unknown>OutputValueAtPath (#219)

* Adding changelog entries (#219)

* Updating docs (#219)

* Adding copyright headers (#219)

* Updating docs and tests following code review (#219)

* Rewording to explicitly mention data sources (#219)

---------

Co-authored-by: Selena Goods <[email protected]>
  • Loading branch information
bendbennett and SBGoods authored Nov 28, 2023
1 parent 6f2d420 commit 5f5c3b7
Show file tree
Hide file tree
Showing 20 changed files with 2,580 additions and 130 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20231122-084316.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'plancheck: Added `ExpectUnknownOutputValue` built-in plan check, which asserts
that a given output value at a specified address is unknown'
time: 2023-11-22T08:43:16.202152Z
custom:
Issue: "220"
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20231122-084409.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'plancheck: Added `ExpectUnknownOutputValueAtPath` built-in plan check, which
asserts that a given output value at a specified address, and path is unknown'
time: 2023-11-22T08:44:09.522934Z
custom:
Issue: "220"
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20231122-084501.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'plancheck: Added `ExpectNullOutputValue` built-in plan check, which asserts
that a given output value at a specified address is null'
time: 2023-11-22T08:45:01.330523Z
custom:
Issue: "220"
6 changes: 6 additions & 0 deletions .changes/unreleased/FEATURES-20231122-084552.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: FEATURES
body: 'plancheck: Added `ExpectNullOutputValueAtPath` built-in plan check, which asserts
that a given output value at a specified address, and path is null'
time: 2023-11-22T08:45:52.856267Z
custom:
Issue: "220"
71 changes: 71 additions & 0 deletions plancheck/expect_null_output_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package plancheck

import (
"context"
"fmt"

tfjson "github.com/hashicorp/terraform-json"

"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
)

var _ PlanCheck = expectNullOutputValue{}

type expectNullOutputValue struct {
outputAddress string
}

// CheckPlan implements the plan check logic.
func (e expectNullOutputValue) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) {
var change *tfjson.Change

for address, oc := range req.Plan.OutputChanges {
if e.outputAddress == address {
change = oc

break
}
}

if change == nil {
resp.Error = fmt.Errorf("%s - Output not found in plan OutputChanges", e.outputAddress)

return
}

var result any
var err error

switch {
case change.Actions.Create():
result, err = tfjsonpath.Traverse(change.After, tfjsonpath.Path{})
default:
result, err = tfjsonpath.Traverse(change.Before, tfjsonpath.Path{})
}

if err != nil {
resp.Error = err

return
}

if result != nil {
resp.Error = fmt.Errorf("attribute at path is not null")

return
}
}

// ExpectNullOutputValue returns a plan check that asserts that the specified output has a null value.
//
// Due to implementation differences between the terraform-plugin-sdk and the terraform-plugin-framework, representation of null
// values may differ. For example, terraform-plugin-sdk based providers may have less precise representations of null values, such
// as marking whole maps as null rather than individual element values.
func ExpectNullOutputValue(outputAddress string) PlanCheck {
return expectNullOutputValue{
outputAddress: outputAddress,
}
}
73 changes: 73 additions & 0 deletions plancheck/expect_null_output_value_at_path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package plancheck

import (
"context"
"fmt"

tfjson "github.com/hashicorp/terraform-json"

"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
)

var _ PlanCheck = expectNullOutputValueAtPath{}

type expectNullOutputValueAtPath struct {
outputAddress string
valuePath tfjsonpath.Path
}

// CheckPlan implements the plan check logic.
func (e expectNullOutputValueAtPath) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) {
var change *tfjson.Change

for address, oc := range req.Plan.OutputChanges {
if e.outputAddress == address {
change = oc

break
}
}

if change == nil {
resp.Error = fmt.Errorf("%s - Output not found in plan OutputChanges", e.outputAddress)

return
}

var result any
var err error

switch {
case change.Actions.Create():
result, err = tfjsonpath.Traverse(change.After, e.valuePath)
default:
result, err = tfjsonpath.Traverse(change.Before, e.valuePath)
}

if err != nil {
resp.Error = err

return
}

if result != nil {
resp.Error = fmt.Errorf("attribute at path is not null")

return
}
}

// ExpectNullOutputValueAtPath returns a plan check that asserts that the specified output has a null value.
//
// Due to implementation differences between the terraform-plugin-sdk and the terraform-plugin-framework, representation of null
// values may differ. For example, terraform-plugin-sdk based providers may have less precise representations of null values, such
// as marking whole maps as null rather than individual element values.
func ExpectNullOutputValueAtPath(outputAddress string, valuePath tfjsonpath.Path) PlanCheck {
return expectNullOutputValueAtPath{
outputAddress: outputAddress,
valuePath: valuePath,
}
}
Loading

0 comments on commit 5f5c3b7

Please sign in to comment.