diff --git a/.changes/unreleased/NOTES-20240122-082628.yaml b/.changes/unreleased/NOTES-20240122-082628.yaml index f05712299..09792a709 100644 --- a/.changes/unreleased/NOTES-20240122-082628.yaml +++ b/.changes/unreleased/NOTES-20240122-082628.yaml @@ -1,7 +1,7 @@ kind: NOTES body: 'plancheck: Deprecated `ExpectNullOutputValue` and `ExpectNullOutputValueAtPath`. Use `ExpectKnownOutputValue` and `ExpectKnownOutputValueAtPath` with - `knownvalue.NullExact` instead' + `knownvalue.Null` instead' time: 2024-01-22T08:26:28.053303Z custom: Issue: "275" diff --git a/.changes/unreleased/NOTES-20240229-112244.yaml b/.changes/unreleased/NOTES-20240229-112244.yaml new file mode 100644 index 000000000..0dbed4b45 --- /dev/null +++ b/.changes/unreleased/NOTES-20240229-112244.yaml @@ -0,0 +1,6 @@ +kind: NOTES +body: 'plancheck: `ExpectKnownValue`, `ExpectKnownOutputValue` and `ExpectKnownOutputValueAtPath` + plan checks are considered experimental and may be altered or removed in a subsequent release' +time: 2024-02-29T11:22:44.292647Z +custom: + Issue: "276" diff --git a/.changes/unreleased/NOTES-20240229-112333.yaml b/.changes/unreleased/NOTES-20240229-112333.yaml new file mode 100644 index 000000000..6076d9415 --- /dev/null +++ b/.changes/unreleased/NOTES-20240229-112333.yaml @@ -0,0 +1,6 @@ +kind: NOTES +body: 'statecheck: `ExpectKnownValue`, `ExpectKnownOutputValue` and `ExpectKnownOutputValueAtPath` + state checks are considered experimental and may be altered or removed in a subsequent release' +time: 2024-02-29T11:23:33.77075Z +custom: + Issue: "276" diff --git a/.changes/unreleased/NOTES-20240229-112401.yaml b/.changes/unreleased/NOTES-20240229-112401.yaml new file mode 100644 index 000000000..15df58a3f --- /dev/null +++ b/.changes/unreleased/NOTES-20240229-112401.yaml @@ -0,0 +1,6 @@ +kind: NOTES +body: 'knownvalue: The `knownvalue` package is considered experimental and may + be altered or removed in a subsequent release' +time: 2024-02-29T11:24:01.600093Z +custom: + Issue: "276" diff --git a/helper/resource/testing.go b/helper/resource/testing.go index 4abdce8d6..f98abda39 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -592,10 +592,7 @@ type TestStep struct { RefreshPlanChecks RefreshPlanChecks // ConfigStateChecks allow assertions to be made against the state file during a Config (apply) test using a state check. - // Custom state checks can be created by implementing the [StateCheck] interface, or by using a StateCheck implementation from the provided [statecheck] package - // - // [StateCheck]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#StateCheck - // [statecheck]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck + // Custom state checks can be created by implementing the [statecheck.StateCheck] interface, or by using a StateCheck implementation from the provided [statecheck] package. ConfigStateChecks []statecheck.StateCheck // PlanOnly can be set to only run `plan` with this configuration, and not @@ -1036,6 +1033,44 @@ func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { // attributes using the special key syntax, checking a list, map, or set // attribute directly is not supported. Use TestCheckResourceAttr with // the special .# or .% key syntax for those situations instead. +// +// An experimental interface exists to potentially replace the +// TestCheckResourceAttrSet functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckResourceAttrSet with that experimental interface, by +// using [ExpectKnownValue] with [knownvalue.NotNull]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_AttributeFound(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.NotNull(), +// ), +// }, +// }, +// }, +// }) +// } func TestCheckResourceAttrSet(name, key string) TestCheckFunc { return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -1135,6 +1170,44 @@ func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key stri // - Boolean: "false" or "true". // - Float/Integer: Stringified number, such as "1.2" or "123". // - String: No conversion necessary. +// +// An experimental interface exists to potentially replace the +// TestCheckResourceAttr functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckResourceAttr with that experimental interface, by +// using [statecheck.ExpectKnownValue]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_Bool(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed boolean attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.Bool(true), +// ), +// }, +// }, +// }, +// }) +// } func TestCheckResourceAttr(name, key, value string) TestCheckFunc { return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -1218,6 +1291,43 @@ func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, // when using TestCheckResourceAttrWith and a value is found for the given name and key. // // When this function returns an error, TestCheckResourceAttrWith will fail the check. +// +// An experimental interface exists to potentially replace the +// CheckResourceAttrWithFunc functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckResourceAttrWith with that experimental interface, by +// using [statecheck.ExpectKnownValue] in combination with +// [knownvalue.StringRegexp]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_String_Custom(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed string attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.StringRegexp(regexp.MustCompile("str")), +// }, +// }, +// }, +// }) +// } type CheckResourceAttrWithFunc func(value string) error // TestCheckResourceAttrWith ensures a value stored in state for the @@ -1255,6 +1365,43 @@ type CheckResourceAttrWithFunc func(value string) error // and it's provided with the attribute value to apply a custom checking logic, // if it was found in the state. The function must return an error for the // check to fail, or `nil` to succeed. +// +// An experimental interface exists to potentially replace the +// TestCheckResourceAttrWith functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckResourceAttrWith with that experimental interface, by +// using [statecheck.ExpectKnownValue] in combination with +// [knownvalue.StringRegexp]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_String_Custom(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed string attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.StringRegexp(regexp.MustCompile("str")), +// }, +// }, +// }, +// }) +// } func TestCheckResourceAttrWith(name, key string, checkValueFunc CheckResourceAttrWithFunc) TestCheckFunc { return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -1307,6 +1454,43 @@ func TestCheckResourceAttrWith(name, key string, checkValueFunc CheckResourceAtt // attributes using the special key syntax, checking a list, map, or set // attribute directly is not supported. Use TestCheckResourceAttr with // the special .# or .% key syntax for those situations instead. +// +// An experimental interface exists to potentially replace the +// TestCheckNoResourceAttr functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckNoResourceAttr with that experimental interface, by +// using [statecheck.ExpectKnownValue] with [knownvalue.Null]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_AttributeNull(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed attribute named "computed_attribute" that has a null value +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.Null(), +// ), +// }, +// }, +// }, +// }) +// } func TestCheckNoResourceAttr(name, key string) TestCheckFunc { return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -1411,6 +1595,43 @@ func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key strin // using the regexp.MustCompile() function, which will automatically ensure the // regular expression is supported by the Go regular expression handlers during // compilation. +// +// An experimental interface exists to potentially replace the +// TestMatchResourceAttr functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestMatchResourceAttr with that experimental interface, by +// using [statecheck.ExpectKnownValue] in combination with +// [knownvalue.StringRegexp]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_String_Custom(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed string attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.StringRegexp(regexp.MustCompile("str")), +// }, +// }, +// }, +// }) +// } func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { return checkIfIndexesIntoTypeSet(key, func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -1612,6 +1833,100 @@ func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst strin } // TestCheckOutput checks an output in the Terraform configuration +// +// An experimental interface exists to potentially replace the +// TestCheckOutput functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckOutput with that experimental interface, by +// using [statecheck.ExpectKnownOutputValue]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfversion" +// ) +// +// func TestExpectKnownOutputValue_CheckState_Bool(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// TerraformVersionChecks: []tfversion.TerraformVersionCheck{ +// tfversion.SkipBelow(tfversion.Version1_8_0), +// }, +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example provider containing a provider-defined function named "bool" +// Config: `output "test" { +// value = provider::example::bool(true) +// }`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownOutputValue("test", knownvalue.Bool(true)), +// }, +// }, +// }, +// }) +// } +// +// An experimental interface exists to potentially replace the +// TestCheckOutput functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckOutput with that experimental interface, by using +// [statecheck.ExpectKnownOutputValueAtPath]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownOutputValueAtPath_CheckState_Bool(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed boolean attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {} +// +// // Generally, it is not necessary to use an output to test a resource attribute, +// // the resource attribute should be tested directly instead, by inspecting the +// // value of the resource attribute. For instance: +// // +// // ConfigStateChecks: []statecheck.StateCheck{ +// // statecheck.ExpectKnownValue( +// // "test_resource.one", +// // tfjsonpath.New("computed_attribute"), +// // knownvalue.Bool(true), +// // ), +// // }, +// // +// // This is only shown as an example. +// output test_resource_one_output { +// value = test_resource.one +// }`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownOutputValueAtPath( +// "test_resource_one_output", +// tfjsonpath.New("computed_attribute"), +// knownvalue.Bool(true), +// ), +// }, +// }, +// }, +// }) +// } func TestCheckOutput(name, value string) TestCheckFunc { return func(s *terraform.State) error { ms := s.RootModule() @@ -1632,6 +1947,64 @@ func TestCheckOutput(name, value string) TestCheckFunc { } } +// TestMatchOutput ensures a value matching a regular expression is +// stored in state for the given name. State value checking is only +// recommended for testing Computed attributes and attribute defaults. +// +// An experimental interface exists to potentially replace the +// TestMatchOutput functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestMatchOutput with that experimental interface, by using +// [statecheck.ExpectKnownOutputValueAtPath] in combination with +// [knownvalue.StringRegexp]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownOutputValueAtPath_CheckState_String_Custom(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed string attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {} +// +// // Generally, it is not necessary to use an output to test a resource attribute, +// // the resource attribute should be tested directly instead, by inspecting the +// // value of the resource attribute. For instance: +// // +// // ConfigStateChecks: []statecheck.StateCheck{ +// // statecheck.ExpectKnownValue( +// // "test_resource.one", +// // tfjsonpath.New("computed_attribute"), +// // knownvalue.StringRegexp(regexp.MustCompile("str")), +// // ), +// // }, +// // +// // This is only shown as an example. +// output test_resource_one_output { +// value = test_resource.one +// }`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownOutputValueAtPath( +// "test_resource_one_output", +// tfjsonpath.New("computed_attribute"), +// knownvalue.StringRegexp(regexp.MustCompile("str"), +// ), +// }, +// }, +// }, +// }) +// } func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc { return func(s *terraform.State) error { ms := s.RootModule() diff --git a/helper/resource/testing_sets.go b/helper/resource/testing_sets.go index a304bfc4e..851122204 100644 --- a/helper/resource/testing_sets.go +++ b/helper/resource/testing_sets.go @@ -57,6 +57,46 @@ const ( // If the values map is not granular enough, it is possible to match an element // you were not intending to in the set. Provide the most complete mapping of // attributes possible to be sure the unique element exists. +// +// An experimental interface exists to potentially replace the +// TestCheckTypeSetElemNestedAttrs functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckTypeSetElemNestedAttrs with that experimental interface, by using +// [statecheck.ExpectKnownValue] in combination with [knownvalue.SetPartial]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_SetPartial(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed set attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.SetPartial([]knownvalue.Check{ +// knownvalue.StringExact("value2"), +// }), +// ), +// }, +// }, +// }, +// }) +// } func TestCheckTypeSetElemNestedAttrs(name, attr string, values map[string]string) TestCheckFunc { return func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -129,6 +169,56 @@ func TestCheckTypeSetElemNestedAttrs(name, attr string, values map[string]string // If the values map is not granular enough, it is possible to match an element // you were not intending to in the set. Provide the most complete mapping of // attributes possible to be sure the unique element exists. +// +// If the values map is not granular enough, it is possible to match an element +// you were not intending to in the set. Provide the most complete mapping of +// attributes possible to be sure the unique element exists. +// +// An experimental interface exists to potentially replace the +// TestMatchTypeSetElemNestedAttrs functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestMatchTypeSetElemNestedAttrs with that experimental interface, by using +// [statecheck.ExpectKnownValue] in combination with [knownvalue.SetExact], +// with a nested [knownvalue.StringRegexp]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_SetNestedBlock_Custom(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a set nested block name "block" which contains a computed string attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("block"), +// knownvalue.SetExact([]knownvalue.Check{ +// knownvalue.MapExact(map[string]knownvalue.Check{ +// "computed_attribute": knownvalue.StringRegexp(regexp.MustCompile("str")), +// }), +// knownvalue.MapExact(map[string]knownvalue.Check{ +// "computed_attribute": knownvalue.StringRegexp(regexp.MustCompile("rts")), +// }), +// }), +// ), +// }, +// }, +// }, +// }) +// } func TestMatchTypeSetElemNestedAttrs(name, attr string, values map[string]*regexp.Regexp) TestCheckFunc { return func(s *terraform.State) error { is, err := primaryInstanceState(s, name) @@ -200,6 +290,47 @@ func TestMatchTypeSetElemNestedAttrs(name, attr string, values map[string]*regex // - Boolean: "false" or "true". // - Float/Integer: Stringified number, such as "1.2" or "123". // - String: No conversion necessary. +// +// An experimental interface exists to potentially replace the +// TestCheckTypeSetElemAttr functionality in the future and feedback +// would be appreciated. This example performs the same check as +// TestCheckTypeSetElemAttr with that experimental interface, by using +// [statecheck.ExpectKnownValue] in combination with [knownvalue.SetExact]: +// +// package example_test +// +// import ( +// "testing" +// +// "github.com/hashicorp/terraform-plugin-testing/helper/resource" +// "github.com/hashicorp/terraform-plugin-testing/knownvalue" +// "github.com/hashicorp/terraform-plugin-testing/statecheck" +// "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +// ) +// +// func TestExpectKnownValue_CheckState_Set(t *testing.T) { +// t.Parallel() +// +// resource.Test(t, resource.TestCase{ +// // Provider definition omitted. +// Steps: []resource.TestStep{ +// { +// // Example resource containing a computed set attribute named "computed_attribute" +// Config: `resource "test_resource" "one" {}`, +// ConfigStateChecks: []statecheck.StateCheck{ +// statecheck.ExpectKnownValue( +// "test_resource.one", +// tfjsonpath.New("computed_attribute"), +// knownvalue.SetExact([]knownvalue.Check{ +// knownvalue.StringExact("value2"), +// knownvalue.StringExact("value1"), +// }), +// ), +// }, +// }, +// }, +// }) +// } func TestCheckTypeSetElemAttr(name, attr, value string) TestCheckFunc { return func(s *terraform.State) error { is, err := primaryInstanceState(s, name) diff --git a/knownvalue/not_null.go b/knownvalue/not_null.go new file mode 100644 index 000000000..d7ae68904 --- /dev/null +++ b/knownvalue/not_null.go @@ -0,0 +1,32 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package knownvalue + +import ( + "fmt" +) + +var _ Check = notNull{} + +type notNull struct{} + +// CheckValue determines whether the passed value is nil. +func (v notNull) CheckValue(other any) error { + if other == nil { + return fmt.Errorf("expected non-nil value for NotNull check, got: %T", other) + } + + return nil +} + +// String returns the string representation of notNull. +func (v notNull) String() string { + return "not-null" +} + +// NotNull returns a Check for asserting the value passed +// to the CheckValue method is not nil. +func NotNull() notNull { + return notNull{} +} diff --git a/knownvalue/not_null_test.go b/knownvalue/not_null_test.go new file mode 100644 index 000000000..908c4b769 --- /dev/null +++ b/knownvalue/not_null_test.go @@ -0,0 +1,61 @@ +// 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 TestNotNullValue_CheckValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + self knownvalue.Check + other any + expectedError error + }{ + "zero-nil": { + self: knownvalue.NotNull(), + expectedError: fmt.Errorf("expected non-nil value for NotNull check, got: "), + }, + "not-nil": { + self: knownvalue.NotNull(), + other: nil, + expectedError: fmt.Errorf("expected non-nil value for NotNull check, got: "), + }, + "equal": { + self: knownvalue.NotNull(), + other: 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 TestNotNullValue_String(t *testing.T) { + t.Parallel() + + got := knownvalue.NotNull().String() + + if diff := cmp.Diff(got, "not-null"); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } +} diff --git a/knownvalue/null.go b/knownvalue/null.go index a8bbaee9f..24e6b7e2b 100644 --- a/knownvalue/null.go +++ b/knownvalue/null.go @@ -14,7 +14,7 @@ type null struct{} // CheckValue determines whether the passed value is nil. func (v null) CheckValue(other any) error { if other != nil { - return fmt.Errorf("expected value nil for Null check, got: %T", other) + return fmt.Errorf("expected nil value for Null check, got: %T", other) } return nil @@ -25,8 +25,8 @@ func (v null) String() string { return "null" } -// Null returns a Check for asserting equality nil -// and the value passed to the CheckValue method. +// Null returns a Check for asserting the value passed +// to the CheckValue method is nil. func Null() null { return null{} } diff --git a/knownvalue/null_test.go b/knownvalue/null_test.go index e7bb5531d..a9cc33d97 100644 --- a/knownvalue/null_test.go +++ b/knownvalue/null_test.go @@ -23,14 +23,10 @@ func TestNullValue_CheckValue(t *testing.T) { "zero-nil": { self: knownvalue.Null(), }, - "zero-other": { - self: knownvalue.Null(), - other: nil, // checking against the underlying value field zero-value - }, "not-nil": { self: knownvalue.Null(), other: false, - expectedError: fmt.Errorf("expected value nil for Null check, got: bool"), + expectedError: fmt.Errorf("expected nil value for Null check, got: bool"), }, "equal": { self: knownvalue.Null(), diff --git a/knownvalue/string_regexp.go b/knownvalue/string_regexp.go new file mode 100644 index 000000000..782e29747 --- /dev/null +++ b/knownvalue/string_regexp.go @@ -0,0 +1,45 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package knownvalue + +import ( + "fmt" + "regexp" +) + +var _ Check = stringRegexp{} + +type stringRegexp struct { + regex *regexp.Regexp +} + +// CheckValue determines whether the passed value is of type string, and +// contains a sequence of bytes that match the regular expression supplied +// to StringRegexp. +func (v stringRegexp) CheckValue(other any) error { + otherVal, ok := other.(string) + + if !ok { + return fmt.Errorf("expected string value for StringRegexp check, got: %T", other) + } + + if !v.regex.MatchString(otherVal) { + return fmt.Errorf("expected regex match %s for StringRegexp check, got: %s", v.regex.String(), otherVal) + } + + return nil +} + +// String returns the string representation of the value. +func (v stringRegexp) String() string { + return v.regex.String() +} + +// StringRegexp returns a Check for asserting equality between the +// supplied regular expression and a value passed to the CheckValue method. +func StringRegexp(regex *regexp.Regexp) stringRegexp { + return stringRegexp{ + regex: regex, + } +} diff --git a/knownvalue/string_regexp_test.go b/knownvalue/string_regexp_test.go new file mode 100644 index 000000000..172a72286 --- /dev/null +++ b/knownvalue/string_regexp_test.go @@ -0,0 +1,75 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package knownvalue_test + +import ( + "fmt" + "regexp" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/hashicorp/terraform-plugin-testing/knownvalue" +) + +func TestStringRegexp_CheckValue(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + self knownvalue.Check + other any + expectedError error + }{ + "zero-nil": { + self: knownvalue.StringRegexp(regexp.MustCompile("")), + expectedError: fmt.Errorf("expected string value for StringRegexp check, got: "), + }, + "zero-other": { + self: knownvalue.StringRegexp(regexp.MustCompile("")), + other: "", // checking against the underlying value field zero-value + }, + "nil": { + self: knownvalue.StringRegexp(regexp.MustCompile("str")), + expectedError: fmt.Errorf("expected string value for StringRegexp check, got: "), + }, + "wrong-type": { + self: knownvalue.StringRegexp(regexp.MustCompile("str")), + other: 1.234, + expectedError: fmt.Errorf("expected string value for StringRegexp check, got: float64"), + }, + "not-equal": { + self: knownvalue.StringRegexp(regexp.MustCompile("str")), + other: "rts", + expectedError: fmt.Errorf("expected regex match str for StringRegexp check, got: rts"), + }, + "equal": { + self: knownvalue.StringRegexp(regexp.MustCompile("str")), + other: "str", + }, + } + + 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 TestStringRegexp_String(t *testing.T) { + t.Parallel() + + got := knownvalue.StringRegexp(regexp.MustCompile("^str[0-9a-z]")).String() + + if diff := cmp.Diff(got, "^str[0-9a-z]"); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } +} diff --git a/plancheck/expect_known_output_value_at_path.go b/plancheck/expect_known_output_value_at_path.go index ffb8ee334..42f07a151 100644 --- a/plancheck/expect_known_output_value_at_path.go +++ b/plancheck/expect_known_output_value_at_path.go @@ -60,7 +60,8 @@ func (e expectKnownOutputValueAtPath) CheckPlan(ctx context.Context, req CheckPl } // ExpectKnownOutputValueAtPath returns a plan check that asserts that the specified output at the given path -// has a known type and value. +// has a known type and value. Prior to Terraform v1.3.0 a planned output is marked as fully unknown +// if any attribute is unknown. func ExpectKnownOutputValueAtPath(outputAddress string, outputPath tfjsonpath.Path, knownValue knownvalue.Check) PlanCheck { return expectKnownOutputValueAtPath{ outputAddress: outputAddress, diff --git a/plancheck/expect_known_output_value_at_path_test.go b/plancheck/expect_known_output_value_at_path_test.go index fc643523d..a6eca3356 100644 --- a/plancheck/expect_known_output_value_at_path_test.go +++ b/plancheck/expect_known_output_value_at_path_test.go @@ -140,6 +140,100 @@ func TestExpectKnownOutputValueAtPath_CheckPlan_AttributeValueNull(t *testing.T) }) } +func TestExpectKnownOutputValueAtPath_CheckPlan_AttributeValueNotNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + // Prior to Terraform v1.3.0 a planned output is marked as fully unknown + // if any attribute is unknown. The id attribute within the test provider + // is unknown. + // Reference: https://github.com/hashicorp/terraform/blob/v1.3/CHANGELOG.md#130-september-21-2022 + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_3_0), + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + float_attribute = 1.23 + int_attribute = 123 + list_attribute = ["value1", "value2"] + list_nested_block { + list_nested_block_attribute = "str" + } + map_attribute = { + key1 = "value1" + } + set_attribute = ["value1", "value2"] + set_nested_block { + set_nested_block_attribute = "str" + } + string_attribute = "str" + } + + output test_resource_one_output { + value = test_resource.one + } + `, + ConfigPlanChecks: r.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("bool_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("float_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("int_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("list_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("list_nested_block"), + knownvalue.ListSizeExact(1), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("map_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("set_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("set_nested_block"), + knownvalue.SetSizeExact(1), + ), + plancheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("string_attribute"), + knownvalue.NotNull(), + ), + }, + }, + }, + }, + }) +} + func TestExpectKnownOutputValueAtPath_CheckPlan_Bool(t *testing.T) { t.Parallel() diff --git a/plancheck/expect_known_output_value_test.go b/plancheck/expect_known_output_value_test.go index da45a00d0..504c30c8f 100644 --- a/plancheck/expect_known_output_value_test.go +++ b/plancheck/expect_known_output_value_test.go @@ -137,6 +137,107 @@ func TestExpectKnownOutputValue_CheckPlan_AttributeValueNull(t *testing.T) { }) } +func TestExpectKnownOutputValue_CheckPlan_AttributeValueNotNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + float_attribute = 1.23 + int_attribute = 123 + list_attribute = ["value1", "value2"] + list_nested_block { + list_nested_block_attribute = "str" + } + map_attribute = { + key1 = "value1" + } + set_attribute = ["value1", "value2"] + set_nested_block { + set_nested_block_attribute = "str" + } + string_attribute = "str" + } + output bool_output { + value = test_resource.one.bool_attribute + } + output float64_output { + value = test_resource.one.float_attribute + } + output int64_output { + value = test_resource.one.int_attribute + } + output list_output { + value = test_resource.one.list_attribute + } + output list_nested_block_output { + value = test_resource.one.list_nested_block + } + output map_output { + value = test_resource.one.map_attribute + } + output set_output { + value = test_resource.one.set_attribute + } + output set_nested_block_output { + value = test_resource.one.set_nested_block + } + output string_output { + value = test_resource.one.string_attribute + } + `, + ConfigPlanChecks: r.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownOutputValue( + "bool_output", + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValue( + "float64_output", + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValue( + "int64_output", + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValue( + "list_output", + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValue( + "list_nested_block_output", + knownvalue.ListSizeExact(1), + ), + plancheck.ExpectKnownOutputValue( + "map_output", + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValue( + "set_output", + knownvalue.NotNull(), + ), + plancheck.ExpectKnownOutputValue( + "set_nested_block_output", + knownvalue.SetSizeExact(1), + ), + plancheck.ExpectKnownOutputValue( + "string_output", + knownvalue.NotNull(), + ), + }, + }, + }, + }, + }) +} + func TestExpectKnownOutputValue_CheckPlan_Bool(t *testing.T) { t.Parallel() diff --git a/plancheck/expect_known_value_test.go b/plancheck/expect_known_value_test.go index 85405f1e8..63a68d7e9 100644 --- a/plancheck/expect_known_value_test.go +++ b/plancheck/expect_known_value_test.go @@ -116,6 +116,88 @@ func TestExpectKnownValue_CheckPlan_AttributeValueNull(t *testing.T) { }) } +func TestExpectKnownValue_CheckPlan_AttributeValueNotNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + float_attribute = 1.23 + int_attribute = 123 + list_attribute = ["value1", "value2"] + list_nested_block { + list_nested_block_attribute = "str" + } + map_attribute = { + key1 = "value1" + } + set_attribute = ["value1", "value2"] + set_nested_block { + set_nested_block_attribute = "str" + } + string_attribute = "str" + }`, + ConfigPlanChecks: r.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("bool_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("float_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("int_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("list_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("list_nested_block"), + knownvalue.ListSizeExact(1), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("map_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("set_attribute"), + knownvalue.NotNull(), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("set_nested_block"), + knownvalue.SetSizeExact(1), + ), + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("string_attribute"), + knownvalue.NotNull(), + ), + }, + }, + }, + }, + }) +} + func TestExpectKnownValue_CheckPlan_Bool(t *testing.T) { t.Parallel() diff --git a/plancheck/expect_null_output_value.go b/plancheck/expect_null_output_value.go index 1fd27b17e..92fbf89dd 100644 --- a/plancheck/expect_null_output_value.go +++ b/plancheck/expect_null_output_value.go @@ -65,11 +65,8 @@ func (e expectNullOutputValue) CheckPlan(ctx context.Context, req CheckPlanReque // 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. // -// Deprecated: Use ExpectKnownOutputValue with knownvalue.NullExact instead. +// Deprecated: Use [plancheck.ExpectKnownOutputValue] with [knownvalue.Null] instead. // ExpectNullOutputValue will be removed in the next major version release. -// -// [ExpectKnownOutputValue]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownOutputValue -// [NullExact]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#NullExact func ExpectNullOutputValue(outputAddress string) PlanCheck { return expectNullOutputValue{ outputAddress: outputAddress, diff --git a/plancheck/expect_null_output_value_at_path.go b/plancheck/expect_null_output_value_at_path.go index 4929ec64f..69f237d5f 100644 --- a/plancheck/expect_null_output_value_at_path.go +++ b/plancheck/expect_null_output_value_at_path.go @@ -66,11 +66,8 @@ func (e expectNullOutputValueAtPath) CheckPlan(ctx context.Context, req CheckPla // 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. // -// Deprecated: Use ExpectKnownOutputValueAtPath with knownvalue.NullExact instead. +// Deprecated: Use [plancheck.ExpectKnownOutputValueAtPath] with [knownvalue.Null] instead. // ExpectNullOutputValueAtPath will be removed in the next major version release. -// -// [ExpectKnownOutputValueAtPath]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownOutputValueAtPath -// [NullExact]: https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#NullExact func ExpectNullOutputValueAtPath(outputAddress string, valuePath tfjsonpath.Path) PlanCheck { return expectNullOutputValueAtPath{ outputAddress: outputAddress, diff --git a/statecheck/expect_known_output_value_at_path_example_test.go b/statecheck/expect_known_output_value_at_path_example_test.go new file mode 100644 index 000000000..a6bb4bc12 --- /dev/null +++ b/statecheck/expect_known_output_value_at_path_example_test.go @@ -0,0 +1,42 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package statecheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func ExampleExpectKnownOutputValueAtPath() { + // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. + t := &testing.T{} + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + } + + output test_resource_one_output { + value = test_resource.one + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("bool_attribute"), + knownvalue.Bool(true), + ), + }, + }, + }, + }) +} diff --git a/statecheck/expect_known_output_value_at_path_test.go b/statecheck/expect_known_output_value_at_path_test.go index 83df97b48..0b6013bcd 100644 --- a/statecheck/expect_known_output_value_at_path_test.go +++ b/statecheck/expect_known_output_value_at_path_test.go @@ -121,6 +121,91 @@ func TestExpectKnownOutputValueAtPath_CheckState_AttributeValueNull(t *testing.T }) } +func TestExpectKnownOutputValueAtPath_CheckState_AttributeValueNotNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + float_attribute = 1.23 + int_attribute = 123 + list_attribute = ["value1", "value2"] + list_nested_block { + list_nested_block_attribute = "str" + } + map_attribute = { + key1 = "value1" + } + set_attribute = ["value1", "value2"] + set_nested_block { + set_nested_block_attribute = "str" + } + string_attribute = "str" + } + + output test_resource_one_output { + value = test_resource.one + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("bool_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("float_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("int_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("list_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("list_nested_block"), + knownvalue.ListSizeExact(1), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("map_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("set_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("set_nested_block"), + knownvalue.SetSizeExact(1), + ), + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("string_attribute"), + knownvalue.NotNull(), + ), + }, + }, + }, + }) +} + func TestExpectKnownOutputValueAtPath_CheckState_Bool(t *testing.T) { t.Parallel() @@ -1406,6 +1491,36 @@ func TestExpectKnownOutputValueAtPath_CheckState_String(t *testing.T) { }) } +func TestExpectKnownOutputValueAtPath_CheckState_String_Custom(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + string_attribute = "string" + } + + output test_resource_one_output { + value = test_resource.one + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValueAtPath( + "test_resource_one_output", + tfjsonpath.New("string_attribute"), + StringContains("str")), + }, + }, + }, + }) +} + func TestExpectKnownOutputValueAtPath_CheckState_String_KnownValueWrongType(t *testing.T) { t.Parallel() diff --git a/statecheck/expect_known_output_value_example_test.go b/statecheck/expect_known_output_value_example_test.go new file mode 100644 index 000000000..2d3e3275e --- /dev/null +++ b/statecheck/expect_known_output_value_example_test.go @@ -0,0 +1,40 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package statecheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" +) + +func ExampleExpectKnownOutputValue() { + // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. + t := &testing.T{} + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + } + + output bool_output { + value = test_resource.one.bool_attribute + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "bool_output", + knownvalue.Bool(true), + ), + }, + }, + }, + }) +} diff --git a/statecheck/expect_known_output_value_test.go b/statecheck/expect_known_output_value_test.go index c1cf98cf3..62d9067fe 100644 --- a/statecheck/expect_known_output_value_test.go +++ b/statecheck/expect_known_output_value_test.go @@ -82,6 +82,105 @@ func TestExpectKnownOutputValue_CheckState_AttributeValueNull(t *testing.T) { }) } +func TestExpectKnownOutputValue_CheckState_AttributeValueNotNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + float_attribute = 1.23 + int_attribute = 123 + list_attribute = ["value1", "value2"] + list_nested_block { + list_nested_block_attribute = "str" + } + map_attribute = { + key1 = "value1" + } + set_attribute = ["value1", "value2"] + set_nested_block { + set_nested_block_attribute = "str" + } + string_attribute = "str" + } + output bool_output { + value = test_resource.one.bool_attribute + } + output float64_output { + value = test_resource.one.float_attribute + } + output int64_output { + value = test_resource.one.int_attribute + } + output list_output { + value = test_resource.one.list_attribute + } + output list_nested_block_output { + value = test_resource.one.list_nested_block + } + output map_output { + value = test_resource.one.map_attribute + } + output set_output { + value = test_resource.one.set_attribute + } + output set_nested_block_output { + value = test_resource.one.set_nested_block + } + output string_output { + value = test_resource.one.string_attribute + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "bool_output", + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValue( + "float64_output", + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValue( + "int64_output", + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValue( + "list_output", + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValue( + "list_nested_block_output", + knownvalue.ListSizeExact(1), + ), + statecheck.ExpectKnownOutputValue( + "map_output", + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValue( + "set_output", + knownvalue.NotNull(), + ), + statecheck.ExpectKnownOutputValue( + "set_nested_block_output", + knownvalue.SetSizeExact(1), + ), + statecheck.ExpectKnownOutputValue( + "string_output", + knownvalue.NotNull(), + ), + }, + }, + }, + }) +} + func TestExpectKnownOutputValue_CheckState_Bool(t *testing.T) { t.Parallel() @@ -1331,6 +1430,35 @@ func TestExpectKnownOutputValue_CheckState_String(t *testing.T) { }) } +func TestExpectKnownOutputValue_CheckState_String_Custom(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + string_attribute = "string" + } + + output string_output { + value = test_resource.one.string_attribute + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownOutputValue( + "string_output", + StringContains("str")), + }, + }, + }, + }) +} + func TestExpectKnownOutputValue_CheckState_String_KnownValueWrongType(t *testing.T) { t.Parallel() diff --git a/statecheck/expect_known_value_example_test.go b/statecheck/expect_known_value_example_test.go new file mode 100644 index 000000000..ef26d64c3 --- /dev/null +++ b/statecheck/expect_known_value_example_test.go @@ -0,0 +1,38 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package statecheck_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func ExampleExpectKnownValue() { + // A typical test would accept *testing.T as a function parameter, for instance `func TestSomething(t *testing.T) { ... }`. + t := &testing.T{} + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("bool_attribute"), + knownvalue.Bool(true), + ), + }, + }, + }, + }) +} diff --git a/statecheck/expect_known_value_test.go b/statecheck/expect_known_value_test.go index f9c89bed0..f02b8cd09 100644 --- a/statecheck/expect_known_value_test.go +++ b/statecheck/expect_known_value_test.go @@ -8,6 +8,7 @@ import ( "fmt" "math/big" "regexp" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -100,7 +101,7 @@ func TestExpectKnownValue_CheckState_AttributeValueNull(t *testing.T) { statecheck.ExpectKnownValue( "test_resource.one", tfjsonpath.New("set_nested_block"), - knownvalue.ListExact([]knownvalue.Check{}), + knownvalue.SetExact([]knownvalue.Check{}), ), statecheck.ExpectKnownValue( "test_resource.one", @@ -113,6 +114,86 @@ func TestExpectKnownValue_CheckState_AttributeValueNull(t *testing.T) { }) } +func TestExpectKnownValue_CheckState_AttributeValueNotNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + bool_attribute = true + float_attribute = 1.23 + int_attribute = 123 + list_attribute = ["value1", "value2"] + list_nested_block { + list_nested_block_attribute = "str" + } + map_attribute = { + key1 = "value1" + } + set_attribute = ["value1", "value2"] + set_nested_block { + set_nested_block_attribute = "str" + } + string_attribute = "str" + }`, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("bool_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("float_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("int_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("list_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("list_nested_block"), + knownvalue.ListSizeExact(1), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("map_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("set_attribute"), + knownvalue.NotNull(), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("set_nested_block"), + knownvalue.SetSizeExact(1), + ), + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("string_attribute"), + knownvalue.NotNull(), + ), + }, + }, + }, + }) +} + func TestExpectKnownValue_CheckState_Bool(t *testing.T) { t.Parallel() @@ -1160,6 +1241,45 @@ func TestExpectKnownValue_CheckState_SetNestedBlock(t *testing.T) { }) } +func TestExpectKnownValue_CheckState_SetNestedBlock_Custom(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + set_nested_block { + set_nested_block_attribute = "string" + } + set_nested_block { + set_nested_block_attribute = "girts" + } + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("set_nested_block"), + knownvalue.SetExact([]knownvalue.Check{ + knownvalue.MapExact(map[string]knownvalue.Check{ + "set_nested_block_attribute": StringContains("str"), + }), + knownvalue.MapExact(map[string]knownvalue.Check{ + "set_nested_block_attribute": StringContains("rts"), + }), + }), + ), + }, + }, + }, + }) +} + func TestExpectKnownValue_CheckState_SetNestedBlockPartial(t *testing.T) { t.Parallel() @@ -1254,6 +1374,62 @@ func TestExpectKnownValue_CheckState_String(t *testing.T) { }) } +func TestExpectKnownValue_CheckState_String_Custom(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + ProviderFactories: map[string]func() (*schema.Provider, error){ + "test": func() (*schema.Provider, error) { //nolint:unparam // required signature + return testProvider(), nil + }, + }, + Steps: []r.TestStep{ + { + Config: `resource "test_resource" "one" { + string_attribute = "string" + } + `, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("string_attribute"), + StringContains("tri")), + }, + }, + }, + }) +} + +var _ knownvalue.Check = stringContains{} + +type stringContains struct { + value string +} + +func (v stringContains) CheckValue(other any) error { + otherVal, ok := other.(string) + + if !ok { + return fmt.Errorf("expected string value for StringContains check, got: %T", other) + } + + if !strings.Contains(otherVal, v.value) { + return fmt.Errorf("expected string %q to contain %q for StringContains check", otherVal, v.value) + } + + return nil +} + +func (v stringContains) String() string { + return v.value +} + +func StringContains(value string) stringContains { + return stringContains{ + value: value, + } +} + func TestExpectKnownValue_CheckState_String_KnownValueWrongType(t *testing.T) { t.Parallel() @@ -1371,6 +1547,12 @@ func testProvider() *schema.Provider { "test_resource": { CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics { d.SetId("test") + + err := d.Set("string_computed_attribute", "computed") + if err != nil { + return diag.Errorf("error setting string_computed_attribute: %s", err) + } + return nil }, UpdateContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { @@ -1444,6 +1626,10 @@ func testProvider() *schema.Provider { Optional: true, Type: schema.TypeString, }, + "string_computed_attribute": { + Computed: true, + Type: schema.TypeString, + }, }, }, }, diff --git a/website/data/plugin-testing-nav-data.json b/website/data/plugin-testing-nav-data.json index f7ae6d8b5..bb9740043 100644 --- a/website/data/plugin-testing-nav-data.json +++ b/website/data/plugin-testing-nav-data.json @@ -102,6 +102,10 @@ "title": "Map", "path": "acceptance-tests/known-value-checks/map" }, + { + "title": "NotNull", + "path": "acceptance-tests/known-value-checks/not-null" + }, { "title": "Null", "path": "acceptance-tests/known-value-checks/null" diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/bool.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/bool.mdx index a996b569a..a3f8be559 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/bool.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/bool.mdx @@ -24,14 +24,13 @@ func TestExpectKnownValue_CheckPlan_Bool(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true - }`, + // Example resource containing a computed boolean attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("bool_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Bool(true), ), }, 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 892433ea6..de0b6401e 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 @@ -15,35 +15,35 @@ type Check interface { } ``` -For example, a `StringValueContains` implementation could look as follows: +For example, a `StringContains` implementation could look as follows: ```go -var _ Check = StringValueContains{} +var _ knownvalue.Check = stringContains{} -type StringValueContains struct { +type stringContains struct { value string } -func (v StringValueContains) CheckValue(other any) error { +func (v stringContains) CheckValue(other any) error { otherVal, ok := other.(string) if !ok { - return fmt.Errorf("expected string value for StringValueContains check, got: %T", other) + return fmt.Errorf("expected string value for StringContains check, got: %T", other) } - if !strings.Contains(v.value, otherVal) { - return fmt.Errorf("expected string %q to contain %q for StringValueContains check", otherVal, v.value) + if !strings.Contains(otherVal, v.value) { + return fmt.Errorf("expected string %q to contain %q for StringContains check", otherVal, v.value) } return nil } -func (v StringValueContains) String() string { +func (v stringContains) String() string { return v.value } -func StringValueMatch(value string) StringValue { - return StringValueContains{ +func StringContains(value string) stringContains { + return stringContains{ value: value, } } diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/float64.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/float64.mdx index 7a22133ef..af7de4d29 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/float64.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/float64.mdx @@ -24,14 +24,13 @@ func TestExpectKnownValue_CheckPlan_Float64(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - float_attribute = 1.23 - }`, + // Example resource containing a computed float64 attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("float_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Float64Exact(1.23), ), }, 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 3da43b44c..2d06a3d79 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 @@ -36,15 +36,17 @@ knownvalue.ListExact([]knownvalue.Check{ The following table shows the correspondence between [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types, and attributes. -| Known Value Check Type | Framework Attribute Type | SDKv2 Attribute Type | -|-----------------------------------------------------------------------------------------------------|---------------------------|----------------------| -| [Bool Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/bool) | `schema.BoolAttribute` | `schema.TypeBool` | -| [Float64 Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/float64) | `schema.Float64Attribute` | `schema.TypeFloat` | -| [Int64 Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/int64) | `schema.Int64Attribute` | `schema.TypeInt` | -| [List Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/list) | `schema.ListAttribute` | `schema.TypeList` | -| [Map Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/map) | `schema.MapAttribute` | `schema.TypeMap` | -| [Number Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/number) | `schema.NumberAttribute` | N/A | -| [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` | +| Known Value Check Type | Framework Attribute Type | SDKv2 Attribute Type | +|------------------------------------------------------------------------------------------------------|---------------------------|----------------------| +| [Bool Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/bool) | `schema.BoolAttribute` | `schema.TypeBool` | +| [Float64 Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/float64) | `schema.Float64Attribute` | `schema.TypeFloat` | +| [Int64 Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/int64) | `schema.Int64Attribute` | `schema.TypeInt` | +| [List Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/list) | `schema.ListAttribute` | `schema.TypeList` | +| [Map Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/map) | `schema.MapAttribute` | `schema.TypeMap` | +| [NotNull Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/not-null) | All | All | +| [Null Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/null) | All | All | +| [Number Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks/number) | `schema.NumberAttribute` | N/A | +| [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` | diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/int64.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/int64.mdx index ab3b61829..af647e701 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/int64.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/int64.mdx @@ -24,14 +24,13 @@ func TestExpectKnownValue_CheckPlan_Int64(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - int_attribute = 123 - }`, + // Example resource containing a computed int64 attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("int_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Int64Exact(123), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/list.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/list.mdx index fad959b43..92963b91e 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/list.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/list.mdx @@ -26,17 +26,13 @@ func TestExpectKnownValue_CheckPlan_List(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - list_attribute = [ - "value1", - "value2" - ] - }`, + // Example resource containing a computed list attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("list_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.ListExact([]knownvalue.Check{ knownvalue.StringExact("value1"), knownvalue.StringExact("value2"), @@ -64,17 +60,13 @@ func TestExpectKnownValue_CheckPlan_ListPartial(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - list_attribute = [ - "value1", - "value2" - ] - }`, + // Example resource containing a computed list attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("list_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.ListPartial(map[int]knownvalue.Check{ 0: knownvalue.StringExact("value1"), }), @@ -101,17 +93,13 @@ func TestExpectKnownValue_CheckPlan_ListElements(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - list_attribute = [ - "value1", - "value2" - ] - }`, + // Example resource containing a computed list attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("list_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.ListSizeExact(2), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/map.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/map.mdx index 482686753..9685249c4 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/map.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/map.mdx @@ -26,17 +26,13 @@ func TestExpectKnownValue_CheckPlan_Map(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - map_attribute = { - key1 = "value1" - key2 = "value2" - } - }`, + // Example resource containing a computed map attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("map_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.MapExact(map[string]knownvalue.Check{ "key1": knownvalue.StringExact("value1"), "key2": knownvalue.StringExact("value2"), @@ -66,17 +62,13 @@ func TestExpectKnownValue_CheckPlan_MapPartial(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - map_attribute = { - key1 = "value1" - key2 = "value2" - } - }`, + // Example resource containing a computed map attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("map_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.MapPartial(map[string]knownvalue.Check{ "key1": knownvalue.StringExact("value1"), }), @@ -103,17 +95,13 @@ func TestExpectKnownValue_CheckPlan_MapElements(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - map_attribute = { - key1 = "value1" - key2 = "value2" - } - }`, + // Example resource containing a computed map attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("map_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.MapSizeExact(2), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/not-null.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/not-null.mdx new file mode 100644 index 000000000..b6e39030f --- /dev/null +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/not-null.mdx @@ -0,0 +1,40 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Known Values' +description: >- + NotNull Value Checks for use with Plan Checks or State Checks. +--- + +# NotNull Known Value Checks + +The known value checks that are available for values that are not null are: + +* [NotNull](/terraform/plugin/testing/acceptance-tests/known-value-checks/null#notnull-check) + +## `NotNull` Check + +The [NotNull](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#NotNull) check tests that a resource attribute, or output value is not null (i.e., any known value). + +Example usage of [NotNull](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#NotNull) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/state-checks/resource) state check. + +```go +func TestExpectKnownValue_CheckState_AttributeValueNull(t *testing.T) { + t.Parallel() + + r.Test(t, r.TestCase{ + // Provider definition omitted. + Steps: []r.TestStep{ + { + // Example resource containing a computed attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, + ConfigStateChecks: []statecheck.StateCheck{ + statecheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("computed_attribute"), + knownvalue.NotNull(), + ), + }, + }, + }, + }) +} +``` diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/null.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/null.mdx index 0ab575e39..8cd4bee00 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/null.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/null.mdx @@ -24,11 +24,12 @@ func TestExpectKnownValue_CheckState_AttributeValueNull(t *testing.T) { // Provider definition omitted. Steps: []r.TestStep{ { + // Example resource containing a computed attribute named "computed_attribute" Config: `resource "test_resource" "one" {}`, ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("bool_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Null(), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/number.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/number.mdx index 6aee9e7cc..fa4b6dcbb 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/number.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/number.mdx @@ -30,14 +30,13 @@ func TestExpectKnownValue_CheckPlan_Number(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - number_attribute = 123 - }`, + // Example resource containing a computed number attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("number_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.NumberExact(num), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/object.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/object.mdx index 0d7229aed..68569dcde 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/object.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/object.mdx @@ -25,17 +25,13 @@ func TestExpectKnownValue_CheckPlan_Object(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - object_attribute = { - attr1 = "value1" - attr2 = "value2" - } - }`, + // Example resource containing a computed object attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("object_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.ObjectExact(map[string]knownvalue.Check{ "attr1": knownvalue.StringExact("value1"), "attr2": knownvalue.StringExact("value2"), @@ -65,17 +61,13 @@ func TestExpectKnownValue_CheckPlan_ObjectPartial(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - object_attribute = { - attr1 = "value1" - attr2 = "value2" - } - }`, + // Example resource containing a computed object attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("object_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.ObjectPartial(map[string]knownvalue.Check{ "attr1": knownvalue.StringExact("value1"), }), diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/set.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/set.mdx index 23acb33a6..48d906926 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/set.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/set.mdx @@ -26,17 +26,13 @@ func TestExpectKnownValue_CheckPlan_Set(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - set_attribute = [ - "value1", - "value2" - ] - }`, + // Example resource containing a computed set attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("set_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.SetExact([]knownvalue.Check{ knownvalue.StringExact("value2"), knownvalue.StringExact("value1"), @@ -64,17 +60,13 @@ func TestExpectKnownValue_CheckPlan_SetPartial(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - set_attribute = [ - "value1", - "value2" - ] - }`, + // Example resource containing a computed set attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("set_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.SetPartial([]knownvalue.Check{ knownvalue.StringExact("value2"), }), @@ -101,17 +93,13 @@ func TestExpectKnownValue_CheckPlan_SetElements(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - set_attribute = [ - "value1", - "value2" - ] - }`, + // Example resource containing a computed set attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("set_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.SetSizeExact(2), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/known-value-checks/string.mdx b/website/docs/plugin/testing/acceptance-tests/known-value-checks/string.mdx index bbe2fd80c..ce92df3cd 100644 --- a/website/docs/plugin/testing/acceptance-tests/known-value-checks/string.mdx +++ b/website/docs/plugin/testing/acceptance-tests/known-value-checks/string.mdx @@ -9,6 +9,7 @@ description: >- The known value checks that are available for string values are: * [StringExact](/terraform/plugin/testing/acceptance-tests/known-value-checks/string#stringexact-check) +* [StringRegexp](/terraform/plugin/testing/acceptance-tests/known-value-checks/string#stringregexp-check) ## `StringExact` Check @@ -17,21 +18,20 @@ The [StringExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testi Example usage of [StringExact](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#StringExact) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/plan-checks/resource) plan check. ```go -func TestExpectKnownValue_CheckPlan_String(t *testing.T) { +func TestExpectKnownValue_CheckPlan_StringExact(t *testing.T) { t.Parallel() resource.Test(t, resource.TestCase{ // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - string_attribute = "str" - }`, + // Example resource containing a computed string attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("string_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.StringExact("str")), }, }, @@ -40,3 +40,33 @@ func TestExpectKnownValue_CheckPlan_String(t *testing.T) { }) } ``` + +## `StringRegexp` Check + +The [StringRegexp](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#StringRegexp) check tests that a resource attribute, or output value has a string value which matches the supplied regular expression. + +Example usage of [StringRegexp](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#StringRegexp) in an [ExpectKnownValue](/terraform/plugin/testing/acceptance-tests/plan-checks/resource) plan check. + +```go +func TestExpectKnownValue_CheckPlan_StringRegexp(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example resource containing a computed string attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownValue( + "test_resource.one", + tfjsonpath.New("computed_attribute"), + knownvalue.StringRegexp(regexp.MustCompile("str"))), + }, + }, + }, + }, + }) +} +``` diff --git a/website/docs/plugin/testing/acceptance-tests/plan-checks/index.mdx b/website/docs/plugin/testing/acceptance-tests/plan-checks/index.mdx index b989b786d..9c1960b54 100644 --- a/website/docs/plugin/testing/acceptance-tests/plan-checks/index.mdx +++ b/website/docs/plugin/testing/acceptance-tests/plan-checks/index.mdx @@ -28,28 +28,37 @@ Refer to: The `terraform-plugin-testing` module provides a package [`plancheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck) with built-in general plan checks for common use-cases: -| Check | Description | -|-----------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| -| [`plancheck.ExpectEmptyPlan()`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectEmptyPlan) | Asserts the entire plan has no operations for apply. | -| [`plancheck.ExpectNonEmptyPlan()`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectNonEmptyPlan) | Asserts the entire plan contains at least one operation for apply. | +| Check | Description | +|--------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------| +| [`ExpectEmptyPlan`](/terraform/plugin/testing/acceptance-tests/plan-checks#expectemptyplan-plan-check) | Asserts the entire plan has no operations for apply. | +| [`ExpectNonEmptyPlan`](/terraform/plugin/testing/acceptance-tests/plan-checks#expectnonemptyplan-plan-check) | Asserts the entire plan contains at least one operation for apply. | -## Examples using `plancheck.ExpectEmptyPlan` +## `ExpectEmptyPlan` Plan Check One of the built-in plan checks, [`plancheck.ExpectEmptyPlan`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectEmptyPlan), is useful for determining a plan is a no-op prior to, for instance, the `terraform apply` phase. -Given the following example with the [random provider](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string), we have written a test that asserts that `random_string.one` will be destroyed and re-created when the `length` attribute is changed: +Given the following example with the [random provider](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string), we have written a test that asserts that there are no planned changes: ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + func Test_Random_EmptyPlan(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ - ExternalProviders: map[string]r.ExternalProvider{ + resource.Test(t, resource.TestCase{ + ExternalProviders: map[string]resource.ExternalProvider{ "random": { Source: "registry.terraform.io/hashicorp/random", }, }, - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { Config: `resource "random_string" "one" { length = 16 @@ -59,7 +68,7 @@ func Test_Random_EmptyPlan(t *testing.T) { Config: `resource "random_string" "one" { length = 16 }`, - ConfigPlanChecks: r.ConfigPlanChecks{ + ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectEmptyPlan(), }, @@ -68,4 +77,52 @@ func Test_Random_EmptyPlan(t *testing.T) { }, }) } +``` + +## `ExpectNonEmptyPlan` Plan Check + +One of the built-in plan checks, [`plancheck.ExpectNonEmptyPlan`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectNonEmptyPlan), is useful for determining whether a plan contains changes prior to, for instance, the `terraform apply` phase. + +The following example, which uses the built-in [terraform_data resource](https://developer.hashicorp.com/terraform/language/resources/terraform-data), asserts that there are planned changes: + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_ExpectNonEmptyPlan_ResourceChanges(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_4_0), + }, + ExternalProviders: map[string]resource.ExternalProvider{ + "terraform": {Source: "terraform.io/builtin/terraform"}, + }, + Steps: []resource.TestStep{ + { + Config: `resource "terraform_data" "one" { + triggers_replace = ["original"] + }`, + }, + { + Config: `resource "terraform_data" "one" { + triggers_replace = ["new"] + }`, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectNonEmptyPlan(), + }, + }, + }, + }, + }) +} ``` \ No newline at end of file diff --git a/website/docs/plugin/testing/acceptance-tests/plan-checks/output.mdx b/website/docs/plugin/testing/acceptance-tests/plan-checks/output.mdx index ce8c548af..3f0b97bf7 100644 --- a/website/docs/plugin/testing/acceptance-tests/plan-checks/output.mdx +++ b/website/docs/plugin/testing/acceptance-tests/plan-checks/output.mdx @@ -9,41 +9,51 @@ description: >- The `terraform-plugin-testing` module provides a package [`plancheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck) with built-in output value plan checks for common use-cases: -| Check | Description | -|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| -| [`plancheck.ExpectKnownOutputValue(address, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownOutputValue) | Asserts the output at the specified address has the specified type, and value. | -| [`plancheck.ExpectKnownOutputValueAtPath(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownOutputValueAtPath) | Asserts the output at the specified address, and path has the specified type, and value. | -| [`plancheck.ExpectNullOutputValue(address)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectNullOutputValue) | Asserts the output at the specified address has a null value. | -| [`plancheck.ExpectNullOutputValueAtPath(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectNullOutputValueAtPath) | Asserts the output at the specified address, and path has a null value. | -| [`plancheck.ExpectUnknownOutputValue(address)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownOutputValue) | Asserts the output at the specified address has an unknown value. | -| [`plancheck.ExpectUnknownOutputValueAtPath(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownOutputValueAtPath) | Asserts the output at the specified address, and path has an unknown value. | +| Check | Description | +|-------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| +| [`ExpectKnownOutputValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectknownoutputvalue-plan-check) | Asserts the output at the specified address has the specified type, and value. | +| [`ExpectKnownOutputValueAtPath`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectknownoutputvalueatpath-plan-check) | Asserts the output at the specified address, and path has the specified type, and value. | +| [`ExpectNullOutputValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectnulloutputvalue-plan-check) | Asserts the output at the specified address has a null value. | +| [`ExpectNullOutputValueAtPath`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectnulloutputvalueatpath-plan-check) | Asserts the output at the specified address, and path has a null value. | +| [`ExpectUnknownOutputValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectunknownoutputvalue-plan-check) | Asserts the output at the specified address has an unknown value. | +| [`ExpectUnknownOutputValueAtPath`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectknownoutputvalueatpath-plan-check) | Asserts the output at the specified address, and path has an unknown value. | -## Example using `plancheck.ExpectKnownOutputValue` +## `ExpectKnownOutputValue` Plan Check The [`plancheck.ExpectKnownOutputValue(address, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownOutputValue) plan check verifies that a specific output value has a known type, and value. Refer to [Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks) for details, and examples of the available [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types that can be used with the `ExpectKnownOutputValue` plan check. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + func TestExpectKnownOutputValue_CheckPlan_Bool(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_8_0), + }, // Provider definition omitted. - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true - } - - output bool_output { - value = test_resource.one.bool_attribute - } - `, - ConfigPlanChecks: r.ConfigPlanChecks{ + Config: ` + output "test" { + value = provider::example::bool(true) + }`, + ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownOutputValue( - "bool_output", + "test", knownvalue.Bool(true), ), }, @@ -54,34 +64,153 @@ func TestExpectKnownOutputValue_CheckPlan_Bool(t *testing.T) { } ``` -## Example using `plancheck.ExpectKnownOutputValueAtPath` +## `ExpectKnownOutputValueAtPath` Plan Check The [`plancheck.ExpectKnownOutputValueAtPath(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownOutputValueAtPath) plan check verifies that a specific output value at a defined path has a known type, and value. +~> **Note**: Prior to Terraform v1.3.0 a planned output is marked as fully unknown if any attribute is unknown. + Refer to [Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks) for details, and examples of the available [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types that can be used with the `ExpectKnownOutputValueAtPath` plan check. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + func TestExpectKnownOutputValue_CheckPlan_Bool(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example resource containing a computed boolean attribute named "computed_attribute" + Config: `resource "test_resource" "one" {} + + // Generally, it is not necessary to use an output to test a resource attribute, + // the resource attribute should be tested directly instead. This is only shown as + // an example. + // + // ConfigPlanChecks: resource.ConfigPlanChecks{ + // PreApply: []plancheck.PlanCheck{ + // plancheck.ExpectKnownValue( + // "test_resource.one", + // tfjsonpath.New("computed_attribute"), + // knownvalue.Bool(true), + // ), + // }, + // }, + // + // This is only shown as an example. + output test { + value = test_resource.one + }`, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectKnownOutputValueAtPath( + "test", + tfjsonpath.New("computed_attribute"), + knownvalue.Bool(true), + ), + }, + }, + }, + }, + }) +} +``` + +## `ExpectNullOutputValue` Plan Check + +~> **Note**: `ExpectNullOutputValue` is deprecated. Use [`ExpectKnownOutputValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectknownoutputvalue-plan-check) with [`knownvalue.Null()`](/terraform/plugin/testing/acceptance-tests/known-value-checks/null) instead. + +The built-in [`plancheck.ExpectNullOutputValue(address)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectNullOutputValue) plan check determines whether an output at the specified address has a null value. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + +func Test_ExpectNullOutputValue_StringAttribute_NullConfig(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ // Provider definition omitted. - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true + Config: `resource "test_resource" "test" { + string_attribute = null } - output bool_output { - value = test_resource.one.bool_attribute + output "string_attribute" { + value = test_resource.test.string_attribute } `, - ConfigPlanChecks: r.ConfigPlanChecks{ + ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ - plancheck.ExpectKnownOutputValue( - "bool_output", - knownvalue.Bool(true), - ), + plancheck.ExpectNullOutputValue("string_attribute"), + }, + }, + }, + }, + }) +} +``` + +## `ExpectNullOutputValueAtPath` Plan Check + +~> **Note**: `ExpectNullOutputValueAtPath` is deprecated. Use [`ExpectKnownOutputValueAtPath`](/terraform/plugin/testing/acceptance-tests/plan-checks/output#expectknownoutputvalueatpath-plan-check) with [`knownvalue.Null()`](/terraform/plugin/testing/acceptance-tests/known-value-checks/null) instead. + +The built-in [`plancheck.ExpectNullOutputValueAtPath(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectNullOutputValueAtPath) plan check determines whether an output at the specified address, and path has a null value. | + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_ExpectNullOutputValueAtPath_StringAttribute_NullConfig(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Prior to Terraform v1.3.0 a planned output is marked as fully unknown + // if any attribute is unknown. The id attribute within the test provider + // is unknown. + // Reference: https://github.com/hashicorp/terraform/blob/v1.3/CHANGELOG.md#130-september-21-2022 + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_3_0), + }, + // Provider definition omitted. + Steps: []resource.TestStep{ + { + Config: `resource "test_resource" "test" { + string_attribute = null + } + + output "resource" { + value = test_resource.test + } + `, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectNullOutputValueAtPath("resource", tfjsonpath.New("string_attribute")), }, }, }, @@ -90,13 +219,22 @@ func TestExpectKnownOutputValue_CheckPlan_Bool(t *testing.T) { } ``` -## Example using `plancheck.ExpectUnknownOutputValue` +## `ExpectUnknownOutputValue` Plan Check One of the built-in plan checks, [`plancheck.ExpectUnknownOutputValue`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownOutputValue), determines whether an output value is unknown, for example, prior to the `terraform apply` phase. The following uses the [time_offset](https://registry.terraform.io/providers/hashicorp/time/latest/docs/resources/offset) resource from the [time provider](https://registry.terraform.io/providers/hashicorp/time/latest), to illustrate usage of the [`plancheck.ExpectUnknownOutputValue`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownOutputValue), and verifies that `day` is unknown. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" +) + func Test_Time_Unknown(t *testing.T) { t.Parallel() @@ -126,11 +264,21 @@ func Test_Time_Unknown(t *testing.T) { } ``` -## Example using `plancheck.ExpectUnknownOutputValueAtPath` +## `ExpectUnknownOutputValueAtPath` Plan Check Output values can contain objects or collections as well as primitive (e.g., string) values. Output value plan checks provide two forms for the plan checks, for example `ExpectUnknownOutputValue()`, and `ExpectUnknownOutputValueAtPath()`. The `Expect<...>OutputValueAtPath()` form is used to access a value contained within an object or collection, as illustrated in the following example. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + func Test_Time_Unknown(t *testing.T) { t.Parallel() diff --git a/website/docs/plugin/testing/acceptance-tests/plan-checks/resource.mdx b/website/docs/plugin/testing/acceptance-tests/plan-checks/resource.mdx index 2ef10e073..b96ae68bb 100644 --- a/website/docs/plugin/testing/acceptance-tests/plan-checks/resource.mdx +++ b/website/docs/plugin/testing/acceptance-tests/plan-checks/resource.mdx @@ -9,36 +9,45 @@ description: >- The `terraform-plugin-testing` module provides a package [`plancheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck) with built-in managed resource, and data source plan checks for common use-cases: -| Check | Description | -|---------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -| [`plancheck.ExpectKnownValue(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownValue) | Asserts the specified attribute at the given managed resource, or data source, has the specified type, and value. | -| [`plancheck.ExpectResourceAction(address, operation)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectResourceAction) | Asserts the given managed resource, or data source, has the specified operation for apply. | -| [`plancheck.ExpectSensitiveValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectSensitiveValue) | Asserts the specified attribute at the given managed resource, or data source, has a sensitive value. | -| [`plancheck.ExpectUnknownValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownValue) | Asserts the specified attribute at the given managed resource, or data source, has an unknown value. | +| Check | Description | +|---------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| +| [`ExpectKnownValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/resource#expectknownvalue-plan-check) | Asserts the specified attribute at the given managed resource, or data source, has the specified type, and value. | +| [`ExpectResourceAction`](/terraform/plugin/testing/acceptance-tests/plan-checks/resource#expectresourceaction-plan-check) | Asserts the given managed resource, or data source, has the specified operation for apply. | +| [`ExpectSensitiveValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/resource#expectsensitivevalue-plan-check) | Asserts the specified attribute at the given managed resource, or data source, has a sensitive value. | +| [`ExpectUnknownValue`](/terraform/plugin/testing/acceptance-tests/plan-checks/resource#expectunknownvalue-plan-check) | Asserts the specified attribute at the given managed resource, or data source, has an unknown value. | -## Example using `plancheck.ExpectKnownValue` +## `ExpectKnownValue` Plan Check The [`plancheck.ExpectKnownValue(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectKnownValue) plan check provides a basis for asserting that a specific resource attribute has a known type, and value. Refer to [Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks) for details, and examples of the available [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types that can be used with the `ExpectKnownValue` plan check. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + func TestExpectKnownValue_CheckPlan_String(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ - // Provider definition omitted. - Steps: []r.TestStep{ + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - string_attribute = "str" - } - `, - ConfigPlanChecks: r.ConfigPlanChecks{ + // Example resource containing a computed string attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, + ConfigPlanChecks: resource.ConfigPlanChecks{ PreApply: []plancheck.PlanCheck{ plancheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("string_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.StringExact("str")), }, }, @@ -48,7 +57,7 @@ func TestExpectKnownValue_CheckPlan_String(t *testing.T) { } ``` -## Examples using `plancheck.ExpectResourceAction` +## `ExpectResourceAction` Plan Check One of the built-in plan checks, [`plancheck.ExpectResourceAction`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectResourceAction), is useful for determining the exact action type a resource will under-go during, say, the `terraform apply` phase. @@ -137,6 +146,7 @@ func Test_Time_UpdateInPlace(t *testing.T) { ``` Multiple plan checks can be combined if you want to assert multiple resource actions: + ```go package example_test @@ -183,3 +193,94 @@ func Test_Time_UpdateInPlace_and_NoOp(t *testing.T) { }) } ``` + +## `ExpectSensitiveValue` Plan Check + +The built-in [`plancheck.ExpectSensitiveValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectSensitiveValue) plan check is used to determine whether the specified attribute at the given managed resource, or data source, has a sensitive value. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_ExpectSensitiveValue_SensitiveStringAttribute(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_0_0), // Change.AfterSensitive + }, + // Provider definition omitted. + Steps: []resource.TestStep{ + { + Config: ` + resource "test_resource" "one" { + sensitive_string_attribute = "test" + } + `, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectSensitiveValue("test_resource.one", + tfjsonpath.New("sensitive_string_attribute")), + }, + }, + }, + }, + }) +} +``` + +## `ExpectUnknownValue` Plan Check + +The built-in [`plancheck.ExpectUnknownValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/plancheck#ExpectUnknownValue) plan check is used to determine whether the specified attribute at the given managed resource, or data source, has an unknown value. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/plancheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +func Test_ExpectUnknownValue_StringAttribute(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + ExternalProviders: map[string]resource.ExternalProvider{ + "time": { + Source: "registry.terraform.io/hashicorp/time", + }, + }, + // Provider definition for `test` omitted. + Steps: []resource.TestStep{ + { + Config: `resource "time_static" "one" {} + + resource "test_resource" "two" { + string_attribute = time_static.one.rfc3339 + } + `, + ConfigPlanChecks: resource.ConfigPlanChecks{ + PreApply: []plancheck.PlanCheck{ + plancheck.ExpectUnknownValue("test_resource.two", tfjsonpath.New("string_attribute")), + }, + }, + }, + }, + }) +} +``` \ No newline at end of file diff --git a/website/docs/plugin/testing/acceptance-tests/state-checks/custom.mdx b/website/docs/plugin/testing/acceptance-tests/state-checks/custom.mdx index 230923cd2..46cd3877e 100644 --- a/website/docs/plugin/testing/acceptance-tests/state-checks/custom.mdx +++ b/website/docs/plugin/testing/acceptance-tests/state-checks/custom.mdx @@ -106,14 +106,12 @@ func TestExpectKnownValue_CheckState_Bool(t *testing.T) { // Provider definition omitted. Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true - } - `, + // Example resource containing a computed boolean attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("bool_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Bool(true), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/state-checks/output.mdx b/website/docs/plugin/testing/acceptance-tests/state-checks/output.mdx index cad493698..434e7486b 100644 --- a/website/docs/plugin/testing/acceptance-tests/state-checks/output.mdx +++ b/website/docs/plugin/testing/acceptance-tests/state-checks/output.mdx @@ -9,36 +9,46 @@ description: >- The `terraform-plugin-testing` module provides a package [`statecheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck) with built-in output value state checks for common use-cases: -| Check | Description | -|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| -| [`statecheck.ExpectKnownOutputValue(address, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectKnownOutputValue) | Asserts the output at the specified address has the specified type, and value. | -| [`statecheck.ExpectKnownOutputValueAtPath(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectKnownOutputValueAtPath) | Asserts the output at the specified address, and path has the specified type, and value. | +| Check | Description | +|-------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| +| [`ExpectKnownOutputValue`](/terraform/plugin/testing/acceptance-tests/state-checks/output#expectknownoutputvalue-state-check) | Asserts the output at the specified address has the specified type, and value. | +| [`ExpectKnownOutputValueAtPath`](/terraform/plugin/testing/acceptance-tests/state-checks/output#expectknownoutputvalueatpath-state-check) | Asserts the output at the specified address, and path has the specified type, and value. | -## Example using `statecheck.ExpectKnownOutputValue` +## `ExpectKnownOutputValue` State Check The [`statecheck.ExpectKnownOutputValue(address, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectKnownOutputValue) state check verifies that a specific output value has a known type, and value. Refer to [Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks) for details, and examples of the available [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types that can be used with the `ExpectKnownOutputValue` state check. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + func TestExpectKnownOutputValue_CheckState_Bool(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ + resource.UnitTest(t, resource.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_8_0), + }, // Provider definition omitted. - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true - } - - output bool_output { - value = test_resource.one.bool_attribute - } - `, + Config: ` + output "test" { + value = provider::example::bool(true) + }`, ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownOutputValue( - "bool_output", + "test", knownvalue.Bool(true), ), }, @@ -48,32 +58,57 @@ func TestExpectKnownOutputValue_CheckState_Bool(t *testing.T) { } ``` -## Example using `statecheck.ExpectKnownOutputValueAtPath` +## `ExpectKnownOutputValueAtPath` State Check The [`statecheck.ExpectKnownOutputValueAtPath(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectKnownOutputValueAtPath) state check verifies that a specific output value at a defined path has a known type, and value. Refer to [Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks) for details, and examples of the available [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types that can be used with the `ExpectKnownOutputValueAtPath` state check. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + func TestExpectKnownOutputValueAtPath_CheckState_Bool(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ + resource.Test(t, resource.TestCase{ // Provider definition omitted. - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true - } + // Example resource containing a computed boolean attribute named "computed_attribute" + Config: `resource "test_resource" "one" {} + // Generally, it is not necessary to use an output to test a resource attribute, + // the resource attribute should be tested directly instead. This is only shown as + // an example. + // Generally, it is not necessary to use an output to test a resource attribute, + // the resource attribute should be tested directly instead, by inspecting the + // value of the resource attribute. For instance: + // + // ConfigStateChecks: []statecheck.StateCheck{ + // statecheck.ExpectKnownValue( + // "test_resource.one", + // tfjsonpath.New("computed_attribute"), + // knownvalue.Bool(true), + // ), + // }, + // + // This is only shown as an example. output test_resource_one_output { value = test_resource.one - } - `, + }`, ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownOutputValueAtPath( "test_resource_one_output", - tfjsonpath.New("bool_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Bool(true), ), }, diff --git a/website/docs/plugin/testing/acceptance-tests/state-checks/resource.mdx b/website/docs/plugin/testing/acceptance-tests/state-checks/resource.mdx index 18109b54d..a3e94cbbe 100644 --- a/website/docs/plugin/testing/acceptance-tests/state-checks/resource.mdx +++ b/website/docs/plugin/testing/acceptance-tests/state-checks/resource.mdx @@ -9,33 +9,42 @@ description: >- The `terraform-plugin-testing` module provides a package [`statecheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck) with built-in managed resource, and data source state checks for common use-cases: -| Check | Description | -|------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| -| [`statecheck.ExpectKnownValue(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectKnownValue) | Asserts the specified attribute at the given managed resource, or data source, has the specified type, and value. | -| [`statecheck.ExpectSensitiveValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectSensitiveValue) | Asserts the specified attribute at the given managed resource, or data source, has a sensitive value. | +| Check | Description | +|-----------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [`ExpectKnownValue`](/terraform/plugin/testing/acceptance-tests/state-checks/resource#expectknownvalue-state-check) | Asserts the specified attribute at the given managed resource, or data source, has the specified type, and value. | +| [`ExpectSensitiveValue`](/terraform/plugin/testing/acceptance-tests/state-checks/resource#expectsensitivevalue-state-check) | Asserts the specified attribute at the given managed resource, or data source, has a sensitive value. | -## Example using `statecheck.ExpectKnownValue` +## `ExpectKnownValue` State Check The [`statecheck.ExpectKnownValue(address, path, value)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectKnownValue) state check provides a basis for asserting that a specific resource attribute has a known type, and value. Refer to [Known Value Checks](/terraform/plugin/testing/acceptance-tests/known-value-checks) for details, and examples of the available [knownvalue.Check](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/knownvalue#Check) types that can be used with the `ExpectKnownValue` state check. ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + func TestExpectKnownValue_CheckState_Bool(t *testing.T) { t.Parallel() - r.Test(t, r.TestCase{ + resource.Test(t, resource.TestCase{ // Provider definition omitted. - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { - Config: `resource "test_resource" "one" { - bool_attribute = true - } - `, + // Example resource containing a computed boolean attribute named "computed_attribute" + Config: `resource "test_resource" "one" {}`, ConfigStateChecks: []statecheck.StateCheck{ statecheck.ExpectKnownValue( "test_resource.one", - tfjsonpath.New("bool_attribute"), + tfjsonpath.New("computed_attribute"), knownvalue.Bool(true), ), }, @@ -45,22 +54,33 @@ func TestExpectKnownValue_CheckState_Bool(t *testing.T) { } ``` -## Example using `statecheck.ExpectSensitiveValue` +## `ExpectSensitiveValue` State Check The [`statecheck.ExpectSensitiveValue(address, path)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/statecheck#ExpectSensitiveValue) state check provides a basis for asserting that a specific resource attribute is marked as sensitive. -> **Note:** In this example, a [TerraformVersionCheck](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#TerraformVersionCheck) is being used to prevent execution of this test prior to Terraform version `1.4.6` (refer to the release notes for Terraform [v1.4.6](https://github.com/hashicorp/terraform/releases/tag/v1.4.6)). ```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/statecheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + func Test_ExpectSensitiveValue_SensitiveStringAttribute(t *testing.T) { t.Parallel() - r.UnitTest(t, r.TestCase{ + resource.UnitTest(t, resource.TestCase{ TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.SkipBelow(tfversion.Version1_4_6), // StateResource.SensitiveValues }, // Provider definition omitted. - Steps: []r.TestStep{ + Steps: []resource.TestStep{ { Config: ` resource "test_resource" "one" {