From ee90ef9b69f9a13d8eb04c6a279226f5e218df9f Mon Sep 17 00:00:00 2001 From: SBGoods Date: Thu, 1 Jun 2023 17:26:24 -0400 Subject: [PATCH 01/19] Add `tfversion` package and `TerraformVersionCheck` interface --- tfversion/version_check.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tfversion/version_check.go diff --git a/tfversion/version_check.go b/tfversion/version_check.go new file mode 100644 index 000000000..f2029f446 --- /dev/null +++ b/tfversion/version_check.go @@ -0,0 +1,36 @@ +package tfversion + +import ( + "context" + + "github.com/hashicorp/go-version" +) + +// TerraformVersionCheck is the interface for writing check logic against the Terraform CLI version. +// The Terraform CLI version is determined by the binary selected by the TF_ACC_TERRAFORM_PATH environment +// variable value, installed by the TF_ACC_TERRAFORM_VERSION value, or already existing based on the PATH environment +// variable. This logic is executed at the beginning of the TestCase before any TestStep is executed. +// +// This package contains some built-in functionality that implements the interface, otherwise consumers can use this +// interface for implementing their own custom logic. +type TerraformVersionCheck interface { + // CheckTerraformVersion should implement the logic to either pass, error (failing the test), or skip (passing the test). + CheckTerraformVersion(context.Context, CheckTFVersionRequest, *CheckTFVersionResponse) +} + +// CheckTFVersionRequest is the request received for the CheckTerraformVersion method of the +// TerraformVersionCheck interface. The response of that method is CheckTFVersionResponse. +type CheckTFVersionRequest struct { + // TerraformVersion is the version associated with the selected Terraform CLI binary. + TerraformVersion *version.Version +} + +// CheckTFVersionResponse is the response returned for the CheckTerraformVersion method of the +// TerraformVersionCheck interface. The request of that method is CheckTFVersionRequest. +type CheckTFVersionResponse struct { + // Error will result in failing the test with a given error message. + Error error + + // Skip will result in passing the test with a given skip message. + Skip string +} From e98cbad4301ce15b1348ba28228433ba6df26fea Mon Sep 17 00:00:00 2001 From: SBGoods Date: Thu, 1 Jun 2023 17:29:05 -0400 Subject: [PATCH 02/19] Add `TerraformVersionChecks` field and implement checks in `Test()` --- helper/resource/testing.go | 18 ++++++++++++++++++ helper/resource/tfversion_checks.go | 28 ++++++++++++++++++++++++++++ internal/plugintest/helper.go | 15 +++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 helper/resource/tfversion_checks.go diff --git a/helper/resource/testing.go b/helper/resource/testing.go index 71dbeb921..ca2ed6c3a 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -25,6 +25,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/plancheck" "github.com/hashicorp/terraform-plugin-testing/terraform" + "github.com/hashicorp/terraform-plugin-testing/tfversion" "github.com/hashicorp/terraform-plugin-testing/internal/addrs" "github.com/hashicorp/terraform-plugin-testing/internal/logging" @@ -323,6 +324,12 @@ type TestCase struct { // acceptance tests, such as verifying that keys are setup. PreCheck func() + // TerraformVersionChecks is a list of checks to run against + // the Terraform CLI version which is running the testing. + // Each check is executed in order, respecting the first skip + // or fail response, unless the Any() meta check is also used. + TerraformVersionChecks []tfversion.TerraformVersionCheck + // ProviderFactories can be specified for the providers that are valid. // // This can also be specified at the TestStep level to enable per-step @@ -824,6 +831,17 @@ func Test(t testing.T, c TestCase) { } }(helper) + // Run the TerraformVersionChecks if we have it. + // This is done after creating the helper because a working directory is required + // to retrieve the Terraform version. + if c.TerraformVersionChecks != nil { + logging.HelperResourceDebug(ctx, "Calling TestCase runTFVersionChecks") + + runTFVersionChecks(ctx, t, helper.TerraformVersion(), c.TerraformVersionChecks) + + logging.HelperResourceDebug(ctx, "Called TestCase runTFVersionChecks") + } + runNewTest(ctx, t, c, helper) logging.HelperResourceDebug(ctx, "Finished TestCase") diff --git a/helper/resource/tfversion_checks.go b/helper/resource/tfversion_checks.go new file mode 100644 index 000000000..4f22d3b35 --- /dev/null +++ b/helper/resource/tfversion_checks.go @@ -0,0 +1,28 @@ +package resource + +import ( + "context" + + "github.com/hashicorp/go-version" + "github.com/mitchellh/go-testing-interface" + + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func runTFVersionChecks(ctx context.Context, t testing.T, terraformVersion *version.Version, terraformVersionChecks []tfversion.TerraformVersionCheck) { + t.Helper() + + for _, tfVersionCheck := range terraformVersionChecks { + resp := tfversion.CheckTFVersionResponse{} + tfVersionCheck.CheckTerraformVersion(ctx, tfversion.CheckTFVersionRequest{TerraformVersion: terraformVersion}, &resp) + + if resp.Error != nil { + t.Fatalf(resp.Error.Error()) + } + + if resp.Skip != "" { + t.Skip(resp.Skip) + } + } + +} diff --git a/internal/plugintest/helper.go b/internal/plugintest/helper.go index ef14a0596..b5ffe1f02 100644 --- a/internal/plugintest/helper.go +++ b/internal/plugintest/helper.go @@ -10,6 +10,7 @@ import ( "os" "strings" + "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-exec/tfexec" "github.com/hashicorp/terraform-plugin-testing/internal/logging" @@ -42,6 +43,7 @@ type Helper struct { // for tests that use fixture files. sourceDir string terraformExec string + terraformVer *version.Version // execTempDir is created during DiscoverConfig to store any downloaded // binaries @@ -78,11 +80,19 @@ func InitHelper(ctx context.Context, config *Config) (*Helper, error) { return nil, fmt.Errorf("failed to create temporary directory for test helper: %s", err) } + tf, err := tfexec.NewTerraform(baseDir, config.TerraformExec) + if err != nil { + return nil, fmt.Errorf("unable to create terraform-exec instance: %w", err) + } + + tfVersion, _, _ := tf.Version(ctx, false) + return &Helper{ baseDir: baseDir, sourceDir: config.SourceDir, terraformExec: config.TerraformExec, execTempDir: config.execTempDir, + terraformVer: tfVersion, }, nil } @@ -301,3 +311,8 @@ func (h *Helper) WorkingDirectory() string { func (h *Helper) TerraformExecPath() string { return h.terraformExec } + +// TerraformVersion returns the Terraform CLI version being used when running tests. +func (h *Helper) TerraformVersion() *version.Version { + return h.terraformVer +} From 8faa4ea9b9262f981d71db35c123ddde3d285b31 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Thu, 1 Jun 2023 17:31:51 -0400 Subject: [PATCH 03/19] Extract `testExpectTFatal()` to internal/plugintest package --- helper/resource/testcase_providers_test.go | 10 +++--- helper/resource/testing_test.go | 39 --------------------- helper/resource/teststep_providers_test.go | 8 ++--- internal/plugintest/util.go | 40 ++++++++++++++++++++++ 4 files changed, 50 insertions(+), 47 deletions(-) diff --git a/helper/resource/testcase_providers_test.go b/helper/resource/testcase_providers_test.go index 9ad10cad5..486ef1b5c 100644 --- a/helper/resource/testcase_providers_test.go +++ b/helper/resource/testcase_providers_test.go @@ -14,6 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" ) func TestTestCaseProviderConfig(t *testing.T) { @@ -332,7 +334,7 @@ func TestTest_TestCase_ExternalProvidersAndProviderFactories_NonHashiCorpNamespa func TestTest_TestCase_ExternalProviders_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ ExternalProviders: map[string]ExternalProvider{ "testnonexistent": { @@ -368,7 +370,7 @@ func TestTest_TestCase_ProtoV5ProviderFactories(t *testing.T) { func TestTest_TestCase_ProtoV5ProviderFactories_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ ProtoV5ProviderFactories: map[string]func() (tfprotov5.ProviderServer, error){ "test": func() (tfprotov5.ProviderServer, error) { //nolint:unparam // required signature @@ -404,7 +406,7 @@ func TestTest_TestCase_ProtoV6ProviderFactories(t *testing.T) { func TestTest_TestCase_ProtoV6ProviderFactories_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature @@ -440,7 +442,7 @@ func TestTest_TestCase_ProviderFactories(t *testing.T) { func TestTest_TestCase_ProviderFactories_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ ProviderFactories: map[string]func() (*schema.Provider, error){ "test": func() (*schema.Provider, error) { //nolint:unparam // required signature diff --git a/helper/resource/testing_test.go b/helper/resource/testing_test.go index 1546bfc3f..221706bfb 100644 --- a/helper/resource/testing_test.go +++ b/helper/resource/testing_test.go @@ -27,45 +27,6 @@ func init() { } } -// testExpectTFatal provides a wrapper for logic which should call -// (*testing.T).Fatal() or (*testing.T).Fatalf(). -// -// Since we do not want the wrapping test to fail when an expected test error -// occurs, it is required that the testLogic passed in uses -// github.com/mitchellh/go-testing-interface.RuntimeT instead of the real -// *testing.T. -// -// If Fatal() or Fatalf() is not called in the logic, the real (*testing.T).Fatal() will -// be called to fail the test. -func testExpectTFatal(t *testing.T, testLogic func()) { - t.Helper() - - var recoverIface interface{} - - func() { - defer func() { - recoverIface = recover() - }() - - testLogic() - }() - - if recoverIface == nil { - t.Fatalf("expected t.Fatal(), got none") - } - - recoverStr, ok := recoverIface.(string) - - if !ok { - t.Fatalf("expected string from recover(), got: %v (%T)", recoverIface, recoverIface) - } - - // this string is hardcoded in github.com/mitchellh/go-testing-interface - if !strings.HasPrefix(recoverStr, "testing.T failed, see logs for output") { - t.Fatalf("expected t.Fatal(), got: %s", recoverStr) - } -} - func TestParallelTest(t *testing.T) { t.Parallel() diff --git a/helper/resource/teststep_providers_test.go b/helper/resource/teststep_providers_test.go index 44960abc0..114395578 100644 --- a/helper/resource/teststep_providers_test.go +++ b/helper/resource/teststep_providers_test.go @@ -1053,7 +1053,7 @@ func TestTest_TestStep_ExternalProviders_DifferentVersions(t *testing.T) { func TestTest_TestStep_ExternalProviders_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ Steps: []TestStep{ { @@ -1354,7 +1354,7 @@ func TestTest_TestStep_ProtoV5ProviderFactories(t *testing.T) { func TestTest_TestStep_ProtoV5ProviderFactories_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ Steps: []TestStep{ { @@ -1390,7 +1390,7 @@ func TestTest_TestStep_ProtoV6ProviderFactories(t *testing.T) { func TestTest_TestStep_ProtoV6ProviderFactories_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ Steps: []TestStep{ { @@ -1426,7 +1426,7 @@ func TestTest_TestStep_ProviderFactories(t *testing.T) { func TestTest_TestStep_ProviderFactories_Error(t *testing.T) { t.Parallel() - testExpectTFatal(t, func() { + plugintest.TestExpectTFatal(t, func() { Test(&mockT{}, TestCase{ Steps: []TestStep{ { diff --git a/internal/plugintest/util.go b/internal/plugintest/util.go index 067dc74af..acccb3bcf 100644 --- a/internal/plugintest/util.go +++ b/internal/plugintest/util.go @@ -10,6 +10,7 @@ import ( "path" "path/filepath" "strings" + "testing" ) func symlinkFile(src string, dest string) error { @@ -137,3 +138,42 @@ func CopyDir(src, dest, baseDirName string) error { return nil } + +// TestExpectTFatal provides a wrapper for logic which should call +// (*testing.T).Fatal() or (*testing.T).Fatalf(). +// +// Since we do not want the wrapping test to fail when an expected test error +// occurs, it is required that the testLogic passed in uses +// github.com/mitchellh/go-testing-interface.RuntimeT instead of the real +// *testing.T. +// +// If Fatal() or Fatalf() is not called in the logic, the real (*testing.T).Fatal() will +// be called to fail the test. +func TestExpectTFatal(t *testing.T, testLogic func()) { + t.Helper() + + var recoverIface interface{} + + func() { + defer func() { + recoverIface = recover() + }() + + testLogic() + }() + + if recoverIface == nil { + t.Fatalf("expected t.Fatal(), got none") + } + + recoverStr, ok := recoverIface.(string) + + if !ok { + t.Fatalf("expected string from recover(), got: %v (%T)", recoverIface, recoverIface) + } + + // this string is hardcoded in github.com/mitchellh/go-testing-interface + if !strings.HasPrefix(recoverStr, "testing.T failed, see logs for output") { + t.Fatalf("expected t.Fatal(), got: %s", recoverStr) + } +} From 3228aa097930c3965e1cf632c85a5f1c1ab6ded5 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Thu, 1 Jun 2023 17:32:30 -0400 Subject: [PATCH 04/19] Implement built-in version checks --- tfversion/all.go | 39 +++++++++++++ tfversion/all_test.go | 94 +++++++++++++++++++++++++++++++ tfversion/any.go | 56 ++++++++++++++++++ tfversion/any_test.go | 91 ++++++++++++++++++++++++++++++ tfversion/require_above.go | 32 +++++++++++ tfversion/require_above_test.go | 59 +++++++++++++++++++ tfversion/require_below.go | 32 +++++++++++ tfversion/require_below_test.go | 60 ++++++++++++++++++++ tfversion/require_between.go | 35 ++++++++++++ tfversion/require_between_test.go | 93 ++++++++++++++++++++++++++++++ tfversion/require_not.go | 29 ++++++++++ tfversion/require_not_test.go | 56 ++++++++++++++++++ tfversion/skip_above.go | 32 +++++++++++ tfversion/skip_above_test.go | 59 +++++++++++++++++++ tfversion/skip_below.go | 32 +++++++++++ tfversion/skip_below_test.go | 57 +++++++++++++++++++ tfversion/skip_between.go | 35 ++++++++++++ tfversion/skip_between_test.go | 88 +++++++++++++++++++++++++++++ tfversion/skip_if.go | 29 ++++++++++ tfversion/skip_if_test.go | 53 +++++++++++++++++ 20 files changed, 1061 insertions(+) create mode 100644 tfversion/all.go create mode 100644 tfversion/all_test.go create mode 100644 tfversion/any.go create mode 100644 tfversion/any_test.go create mode 100644 tfversion/require_above.go create mode 100644 tfversion/require_above_test.go create mode 100644 tfversion/require_below.go create mode 100644 tfversion/require_below_test.go create mode 100644 tfversion/require_between.go create mode 100644 tfversion/require_between_test.go create mode 100644 tfversion/require_not.go create mode 100644 tfversion/require_not_test.go create mode 100644 tfversion/skip_above.go create mode 100644 tfversion/skip_above_test.go create mode 100644 tfversion/skip_below.go create mode 100644 tfversion/skip_below_test.go create mode 100644 tfversion/skip_between.go create mode 100644 tfversion/skip_between_test.go create mode 100644 tfversion/skip_if.go create mode 100644 tfversion/skip_if_test.go diff --git a/tfversion/all.go b/tfversion/all.go new file mode 100644 index 000000000..2ac648652 --- /dev/null +++ b/tfversion/all.go @@ -0,0 +1,39 @@ +package tfversion + +import ( + "context" +) + +// All will return the first non-nil error or non-empty skip message +// if any of the given checks return a non-nil error or non-empty skip message. +// Otherwise, it will return a nil error and empty skip message (run the test) +func All(terraformVersionChecks ...TerraformVersionCheck) TerraformVersionCheck { + return allCheck{ + terraformVersionChecks: terraformVersionChecks, + } +} + +// allCheck implements the TerraformVersionCheck interface +type allCheck struct { + terraformVersionChecks []TerraformVersionCheck +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (a allCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + for _, subCheck := range a.terraformVersionChecks { + checkResp := CheckTFVersionResponse{} + + subCheck.CheckTerraformVersion(ctx, CheckTFVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) + + if checkResp.Error != nil { + resp.Error = checkResp.Error + return + } + + if checkResp.Skip != "" { + resp.Skip = checkResp.Skip + return + } + } +} diff --git a/tfversion/all_test.go b/tfversion/all_test.go new file mode 100644 index 000000000..5ad1321a9 --- /dev/null +++ b/tfversion/all_test.go @@ -0,0 +1,94 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + testinginterface "github.com/mitchellh/go-testing-interface" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_All_RunTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} + +func Test_All_SkipTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.0.7") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), + tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.0.7"))), + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} + +func Test_All_Error(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} diff --git a/tfversion/any.go b/tfversion/any.go new file mode 100644 index 000000000..4b7f11a7c --- /dev/null +++ b/tfversion/any.go @@ -0,0 +1,56 @@ +package tfversion + +import ( + "context" + "strings" + + "github.com/hashicorp/terraform-plugin-testing/internal/errorshim" +) + +// Any will return a nil error and empty skip message (run the test) +// if any of the given checks return a nil error and empty skip message. +// Otherwise, it will return all errors and fail the test if any of the given +// checks return a non-nil error, or it will return all skip messages +// and skip (pass) the test. +func Any(terraformVersionChecks ...TerraformVersionCheck) TerraformVersionCheck { + return anyCheck{ + terraformVersionChecks: terraformVersionChecks, + } +} + +// anyCheck implements the TerraformVersionCheck interface +type anyCheck struct { + terraformVersionChecks []TerraformVersionCheck +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (a anyCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + var joinedErrors error + strBuilder := strings.Builder{} + + for _, subCheck := range a.terraformVersionChecks { + checkResp := CheckTFVersionResponse{} + + subCheck.CheckTerraformVersion(ctx, CheckTFVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) + + if checkResp.Error == nil && checkResp.Skip == "" { + resp.Error = nil + resp.Skip = "" + return + } + + if checkResp.Error != nil { + // TODO: Once Go 1.20 is the minimum supported version for this module, replace with `errors.Join` function + // - https://github.com/hashicorp/terraform-plugin-testing/issues/99 + joinedErrors = errorshim.Join(joinedErrors, checkResp.Error) + } + + if checkResp.Skip != "" { + strBuilder.WriteString(checkResp.Skip) + strBuilder.WriteString("\n") + } + } + + resp.Error = joinedErrors + resp.Skip = strings.TrimSpace(strBuilder.String()) +} diff --git a/tfversion/any_test.go b/tfversion/any_test.go new file mode 100644 index 000000000..50a2220eb --- /dev/null +++ b/tfversion/any_test.go @@ -0,0 +1,91 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + testinginterface "github.com/mitchellh/go-testing-interface" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_Any_RunTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.Any( + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} + +func Test_Any_SkipTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.Any( + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), + tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} + +func Test_Any_Error(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.Any( + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} diff --git a/tfversion/require_above.go b/tfversion/require_above.go new file mode 100644 index 000000000..ab937dd89 --- /dev/null +++ b/tfversion/require_above.go @@ -0,0 +1,32 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// RequireAbove will fail the test if the Terraform CLI +// version is below the given version. For example, if given +// version.Must(version.NewVersion("0.15.0")), then 0.14.x or +// any other prior minor versions will fail the test. +func RequireAbove(minimumVersion *version.Version) TerraformVersionCheck { + return requireAboveCheck{ + minimumVersion: minimumVersion, + } +} + +// requireAboveCheck implements the TerraformVersionCheck interface +type requireAboveCheck struct { + minimumVersion *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (r requireAboveCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.LessThan(r.minimumVersion) { + resp.Error = fmt.Errorf("expected Terraform CLI version above %s but detected version is %s", + r.minimumVersion, req.TerraformVersion) + } +} diff --git a/tfversion/require_above_test.go b/tfversion/require_above_test.go new file mode 100644 index 000000000..78cf0f082 --- /dev/null +++ b/tfversion/require_above_test.go @@ -0,0 +1,59 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func Test_RequireAbove(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(version.Must(version.NewVersion("1.1.0"))), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} + +func Test_RequireAbove_Error(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.0.7") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(version.Must(version.NewVersion("1.1.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} diff --git a/tfversion/require_below.go b/tfversion/require_below.go new file mode 100644 index 000000000..4c07a2417 --- /dev/null +++ b/tfversion/require_below.go @@ -0,0 +1,32 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// RequireBelow will fail the test if the Terraform CLI +// version is above the given version. For example, if given +// version.Must(version.NewVersion("0.15.0")), then versions 0.15.x and +// above will fail the test. +func RequireBelow(maximumVersion *version.Version) TerraformVersionCheck { + return requireBelowCheck{ + maximumVersion: maximumVersion, + } +} + +// requireBelowCheck implements the TerraformVersionCheck interface +type requireBelowCheck struct { + maximumVersion *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s requireBelowCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.GreaterThan(s.maximumVersion) { + resp.Error = fmt.Errorf("expected Terraform CLI version below %s but detected version is %s", + s.maximumVersion, req.TerraformVersion) + } +} diff --git a/tfversion/require_below_test.go b/tfversion/require_below_test.go new file mode 100644 index 000000000..53fd84c22 --- /dev/null +++ b/tfversion/require_below_test.go @@ -0,0 +1,60 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func Test_RequireBelow(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.2.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireBelow(version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: ` + terraform { + experiments = [module_variable_optional_attrs] + } + `, + }, + }, + }) +} + +func Test_RequireBelow_Error(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.4.0") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireBelow(version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} diff --git a/tfversion/require_between.go b/tfversion/require_between.go new file mode 100644 index 000000000..46517335b --- /dev/null +++ b/tfversion/require_between.go @@ -0,0 +1,35 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// RequireBetween will fail the test if the Terraform CLI +// version is outside the given minimum (exclusive) and maximum (inclusive). +// For example, if given a minimum version of version.Must(version.NewVersion("0.15.0")) +// and a maximum version of version.Must(version.NewVersion("1.0.0")), then 0.15.x or +// any other prior versions and versions greater than 1.0.0 will fail the test. +func RequireBetween(minimumVersion, maximumVersion *version.Version) TerraformVersionCheck { + return requireBetweenCheck{ + minimumVersion: minimumVersion, + maximumVersion: maximumVersion, + } +} + +// requireBetweenCheck implements the TerraformVersionCheck interface +type requireBetweenCheck struct { + minimumVersion *version.Version + maximumVersion *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s requireBetweenCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.LessThan(s.minimumVersion) || req.TerraformVersion.GreaterThanOrEqual(s.maximumVersion) { + resp.Error = fmt.Errorf("expected Terraform CLI version between %s and %s but detected version is %s", + s.minimumVersion, s.maximumVersion, req.TerraformVersion) + } +} diff --git a/tfversion/require_between_test.go b/tfversion/require_between_test.go new file mode 100644 index 000000000..5533a22ff --- /dev/null +++ b/tfversion/require_between_test.go @@ -0,0 +1,93 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func Test_RequireBetween(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.2.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireBetween(version.Must(version.NewVersion("1.2.0")), version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: ` + terraform { + experiments = [module_variable_optional_attrs] + } + + locals { + ex_var = "hello" + } + + output "example" { + value = "output" + precondition { + condition = local.ex_var != "hi" + error_message = "precondition_error" + } + }`, + }, + }, + }) +} + +func Test_RequireBetween_Error_BelowMin(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireBetween(version.Must(version.NewVersion("1.2.0")), version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} + +func Test_RequireBetween_Error_EqToMax(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.3.0") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireBetween(version.Must(version.NewVersion("1.2.0")), version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} diff --git a/tfversion/require_not.go b/tfversion/require_not.go new file mode 100644 index 000000000..052651a8e --- /dev/null +++ b/tfversion/require_not.go @@ -0,0 +1,29 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// RequireNot will fail the test if the Terraform CLI +// version matches the given version. +func RequireNot(version *version.Version) TerraformVersionCheck { + return requireNotCheck{ + version: version, + } +} + +// requireNotCheck implements the TerraformVersionCheck interface +type requireNotCheck struct { + version *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.Equal(s.version) { + resp.Error = fmt.Errorf("unexpected Terraform CLI version: %s", s.version) + } +} diff --git a/tfversion/require_not_test.go b/tfversion/require_not_test.go new file mode 100644 index 000000000..b68d51a11 --- /dev/null +++ b/tfversion/require_not_test.go @@ -0,0 +1,56 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func Test_RequireNot(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.4.3") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) +} + +func Test_RequireNot_Error(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + plugintest.TestExpectTFatal(t, func() { + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) + }) +} diff --git a/tfversion/skip_above.go b/tfversion/skip_above.go new file mode 100644 index 000000000..4b67b4228 --- /dev/null +++ b/tfversion/skip_above.go @@ -0,0 +1,32 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// SkipAbove will skip (pass) the test if the Terraform CLI +// version is below the given version. For example, if given +// version.Must(version.NewVersion("0.15.0")), then 0.14.x or +// any other prior minor versions will skip the test. +func SkipAbove(maximumVersion *version.Version) TerraformVersionCheck { + return skipAboveCheck{ + maximumVersion: maximumVersion, + } +} + +// skipAboveCheck implements the TerraformVersionCheck interface +type skipAboveCheck struct { + maximumVersion *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s skipAboveCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.GreaterThan(s.maximumVersion) { + resp.Skip = fmt.Sprintf("Terraform CLI version %s is above maximum version %s: skipping test", + req.TerraformVersion, s.maximumVersion) + } +} diff --git a/tfversion/skip_above_test.go b/tfversion/skip_above_test.go new file mode 100644 index 000000000..9191378f1 --- /dev/null +++ b/tfversion/skip_above_test.go @@ -0,0 +1,59 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_SkipAbove_SkipTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.3.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipAbove(version.Must(version.NewVersion("1.2.9"))), + }, + Steps: []r.TestStep{ + { + Config: ` + terraform { + experiments = [module_variable_optional_attrs] + } + `, + }, + }, + }) +} + +func Test_SkipAbove_RunTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.2.9") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipAbove(version.Must(version.NewVersion("1.2.9"))), + }, + Steps: []r.TestStep{ + { + Config: ` + terraform { + experiments = [module_variable_optional_attrs] + } + `, + }, + }, + }) +} diff --git a/tfversion/skip_below.go b/tfversion/skip_below.go new file mode 100644 index 000000000..3067b78cf --- /dev/null +++ b/tfversion/skip_below.go @@ -0,0 +1,32 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// SkipBelow will skip (pass) the test if the Terraform CLI +// version is below the given version. For example, if given +// version.Must(version.NewVersion("0.15.0")), then 0.14.x or +// any other prior minor versions will skip the test. +func SkipBelow(minimumVersion *version.Version) TerraformVersionCheck { + return skipBelowCheck{ + minimumVersion: minimumVersion, + } +} + +// skipBelowCheck implements the TerraformVersionCheck interface +type skipBelowCheck struct { + minimumVersion *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s skipBelowCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.LessThan(s.minimumVersion) { + resp.Skip = fmt.Sprintf("Terraform CLI version %s is below minimum version %s: skipping test", + req.TerraformVersion, s.minimumVersion) + } +} diff --git a/tfversion/skip_below_test.go b/tfversion/skip_below_test.go new file mode 100644 index 000000000..b38b89558 --- /dev/null +++ b/tfversion/skip_below_test.go @@ -0,0 +1,57 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_SkipBelow_SkipTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.0.7") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} + +func Test_SkipBelow_RunTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + }, + Steps: []r.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} diff --git a/tfversion/skip_between.go b/tfversion/skip_between.go new file mode 100644 index 000000000..c0be9656b --- /dev/null +++ b/tfversion/skip_between.go @@ -0,0 +1,35 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// SkipBetween will skip the test if the Terraform CLI +// version is between the given minimum (inclusive) and maximum (exclusive). +// For example, if given a minimum version of version.Must(version.NewVersion("0.15.0")) +// and a maximum version of version.Must(version.NewVersion("0.16.0")), then versions 0.15.x +// will skip the test. +func SkipBetween(minimumVersion, maximumVersion *version.Version) TerraformVersionCheck { + return skipBetweenCheck{ + minimumVersion: minimumVersion, + maximumVersion: maximumVersion, + } +} + +// skipBetweenCheck implements the TerraformVersionCheck interface +type skipBetweenCheck struct { + minimumVersion *version.Version + maximumVersion *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s skipBetweenCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.GreaterThanOrEqual(s.minimumVersion) && req.TerraformVersion.LessThan(s.maximumVersion) { + resp.Skip = fmt.Sprintf("Terraform CLI version %s is between %s and %s: skipping test.", + req.TerraformVersion, s.minimumVersion, s.maximumVersion) + } +} diff --git a/tfversion/skip_between_test.go b/tfversion/skip_between_test.go new file mode 100644 index 000000000..829e2bb04 --- /dev/null +++ b/tfversion/skip_between_test.go @@ -0,0 +1,88 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func Test_SkipBetween_SkipTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.2.0") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBetween(version.Must(version.NewVersion("1.2.0")), version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: ` + terraform { + experiments = [module_variable_optional_attrs] + } + + locals { + ex_var = "hello" + } + + output "example" { + value = "output" + precondition { + condition = local.ex_var != "hi" + error_message = "precondition_error" + } + }`, + }, + }, + }) +} + +func Test_SkipBetween_RunTest_AboveMax(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.3.0") + + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBetween(version.Must(version.NewVersion("1.2.0")), version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) +} + +func Test_SkipBetween_RunTest_EqToMin(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.2.0") + + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBetween(version.Must(version.NewVersion("1.2.0")), version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) +} diff --git a/tfversion/skip_if.go b/tfversion/skip_if.go new file mode 100644 index 000000000..f903196b9 --- /dev/null +++ b/tfversion/skip_if.go @@ -0,0 +1,29 @@ +package tfversion + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +// SkipIf will skip (pass) the test if the Terraform CLI +// version matches the given version. +func SkipIf(version *version.Version) TerraformVersionCheck { + return skipIfCheck{ + version: version, + } +} + +// skipIfCheck implements the TerraformVersionCheck interface +type skipIfCheck struct { + version *version.Version +} + +// CheckTerraformVersion satisfies the TerraformVersionCheck interface. +func (s skipIfCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.Equal(s.version) { + resp.Skip = fmt.Sprintf("Terraform CLI version is %s: skipping test.", s.version) + } +} diff --git a/tfversion/skip_if_test.go b/tfversion/skip_if_test.go new file mode 100644 index 000000000..b22e82eff --- /dev/null +++ b/tfversion/skip_if_test.go @@ -0,0 +1,53 @@ +package tfversion_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func Test_SkipIf_SkipTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.4.3") + + r.UnitTest(t, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipIf(version.Must(version.NewVersion("1.4.3"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) +} + +func Test_SkipIf_RunTest(t *testing.T) { //nolint:paralleltest + t.Setenv("TF_ACC_TERRAFORM_VERSION", "1.1.0") + + r.UnitTest(&testinginterface.RuntimeT{}, r.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipIf(version.Must(version.NewVersion("1.2.0"))), + }, + Steps: []r.TestStep{ + { + Config: `//non-empty config`, + }, + }, + }) +} From 52d944345f029dacb7fb984dd75e5a6a8852eacf Mon Sep 17 00:00:00 2001 From: SBGoods Date: Thu, 1 Jun 2023 17:32:46 -0400 Subject: [PATCH 05/19] Add documentation for version checks --- tfversion/doc.go | 2 + .../testing/acceptance-tests/testcase.mdx | 42 +++ .../acceptance-tests/tfversion-checks.mdx | 254 ++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 tfversion/doc.go create mode 100644 website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx diff --git a/tfversion/doc.go b/tfversion/doc.go new file mode 100644 index 000000000..828561dd6 --- /dev/null +++ b/tfversion/doc.go @@ -0,0 +1,2 @@ +// Package tfversion contains the Terraform version check interface, request/response structs, and common version check implementations. +package tfversion diff --git a/website/docs/plugin/testing/acceptance-tests/testcase.mdx b/website/docs/plugin/testing/acceptance-tests/testcase.mdx index 9614e2af3..d5e3a4290 100644 --- a/website/docs/plugin/testing/acceptance-tests/testcase.mdx +++ b/website/docs/plugin/testing/acceptance-tests/testcase.mdx @@ -144,6 +144,39 @@ func testAccPreCheck(t *testing.T) { } ``` +### TerraformVersionChecks + +**Type:** [`[]tfversion.TerraformVersionCheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#TerraformVersionCheck) + +**Default:** `nil` + +**Required:** no + +**TerraformVersionChecks** if non-nil, will be called after any defined PreChecks +but before any test steps are executed. The [Terraform Version Checks](/terraform/plugin/testing/acceptance-tests/tfversion-checks) +are generic checks that check logic against the Terraform CLI version and can +immediately pass or fail a test before any test steps are executed. + +The tfversion package provides built-in checks for common scenarios. + +**Example usage:** + +```go +// File: example/widget_test.go +package example + +func TestAccExampleWidget_basic(t *testing.T) { + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), //built-in check from tfversion package + }, + // ... + }) +} + +``` + ### Providers **Type:** [`map[string]*schema.Provider`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema#Provider) @@ -168,6 +201,9 @@ package example func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + }, Providers: testAccProviders, // ... }) @@ -211,6 +247,9 @@ package example func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + }, Providers: testAccProviders, CheckDestroy: testAccCheckExampleResourceDestroy, // ... @@ -279,6 +318,9 @@ package example func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + }, Providers: testAccProviders, CheckDestroy: testAccCheckExampleResourceDestroy, Steps: []resource.TestStep{ diff --git a/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx b/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx new file mode 100644 index 000000000..a2b813f08 --- /dev/null +++ b/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx @@ -0,0 +1,254 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Terraform Version Checks' +description: >- + Terraform Version Checks are generic checks defined at the TestCase level that check logic against the Terraform CLI version. The testing module + provides built-in Version Checks for common use-cases, but custom Version Checks can also be implemented. +--- + +# Terraform Version Checks + +**Terraform Version Checks** are generic checks defined at the TestCase level that check logic against the Terraform CLI version. The checks are executed at the beginning of the TestCase before any TestStep is executed. + +The Terraform CLI version is determined by the binary selected by the [`TF_ACC_TERRAFORM_PATH`](/terraform/plugin/testing/acceptance-tests#environment-variables) environment variable value, installed by the [`TF_ACC_TERRAFORM_VERSION`](/terraform/plugin/testing/acceptance-tests#environment-variables) value, or already existing based on the `PATH` environment variable. + +A **version check** will either return an error and fail the associated test, return a skip message and pass the associated test immediately by skipping, or it will return nothing and allow the associated test to run. + +## Built-in Version Checks + +The `terraform-plugin-testing` module provides a package [`tfversion`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion) with built-in version checks for common use-cases. There are three types of version checks: Skip Checks, Require Checks, and Collection Checks. + +### Skip Version Checks + +Skip Version Checks will pass the associated test by skipping and provide a skip message if the detected Terraform CLI version satisfies the specified check criteria. + +| Check | Description | +|---------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| [`tfversion.SkipAbove(maximumVersion)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#SkipAbove) | Skips the test if the Terraform CLI version is above the given maximum. | +| [`tfversion.SkipBelow(minimumVersion)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#SkipBelow) | Skips the test if the Terraform CLI version is below the given minimum. | +| [`tfversion.SkipBetween(minimumVersion, maximumVersion)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#SkipBetween) | Skips the test if the Terraform CLI version is between the given minimum (inclusive) and maximum (exclusive). | +| [`tfversion.SkipIf(version)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#SkipIf) | Skips the test if the Terraform CLI version matches the given version. | + +#### Example using `tfversion.SkipBetween` + +The built-in [`tfversion.SkipBetween`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#SkipBetween) version check is useful for skipping all patch versions associated with a minor version. + +In the following example, we have written a test that skips all Terraform CLI patch versions associated with 0.14.0: + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_Skip_TF14(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBetween(version.Must(version.NewVersion("0.14.0")), version.Must(version.NewVersion("0.15.0"))), + }, + Steps: []resource.TestStep{ + { + Config: `//example test config`, + }, + }, + }) +} +``` + +### Require Version Checks + +Require Version Checks will raise an error and fail the associated test if the detected Terraform CLI version does not satisfy the specified check requirements. + +| Check | Description | +|---------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| [`tfversion.RequireAbove(minimumVersion)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#RequireAbove) | Fails the test if the Terraform CLI version is below the given maximum. | +| [`tfversion.RequireBelow(maximumVersion)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#RequireBelow) | Fails the test if the Terraform CLI version is above the given minimum. | +| [`tfversion.RequireBetween(minimumVersion, maximumVersion)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#RequireBetween) | Fails the test if the Terraform CLI version is outside the given minimum (exclusive) and maximum (inclusive). | +| [`tfversion.RequireNot(version)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#RequireNot) | Fails the test if the Terraform CLI version matches the given version. | + + +#### Example using `tfversion.RequireAbove` + +The built-in [`tfversion.RequireAbove`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#RequireAbove) version check is useful for required tests that may use features only available in newer versions of the Terraform ClI. + +In the following example, the test Terraform configuration uses the `nullable` argument for an input variable, a feature that is only available in Terraform CLI versions `1.3.0` and above. The version check will fail the test with a specific error if the detected version is below `1.3.0`. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_Require_TF1_3(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireAbove(version.Must(version.NewVersion("1.3.0"))), + }, + Steps: []resource.TestStep{ + { + Config: `variable "a" { + nullable = true + default = "hello" + }`, + }, + }, + }) +} +``` + +### Collection Version Checks + +Collection Version Checks operate on multiple version checks and can be used to create more complex checks. + +[`tfversion.Any(TerraformVersionChecks...)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#Any) will run the associated test by returning a nil error and empty skip message +if any of the given version sub-checks return a nil error and empty skip message. If none of the sub-checks return a nil error and empty skip message, then the check will return all sub-check errors and fail the associated test. +Otherwise, if none of the sub-checks return a non-nil error, the check will pass the associated test by skipping and return all sub-check skip messages. + +[`tfversion.All(TerraformVersionChecks...)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#All) will either fail or skip the associated test if any of the given sub-checks return a non-nil error or non-empty skip message. The check will return the +first non-nil error or non-empty skip message from the given sub-checks in the order that they are given. Otherwise, if all sub-checks return a nil error and empty skip message, then the check will return a nil error and empty skip message and run the associated test. This check should only be +used in conjunction with `tfversion.Any()` as the behavior provided by this check is applied to the `TerraformVersionChecks` field by default. + +#### Example using `tfversion.Any` + +In the following example, the test will only run if either the Terraform CLI version is above `1.2.0` or if it's below `1.0.0` but not version `0.15.0`, otherwise an error will be returned. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_Require_TF1_3(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { //nolint:unparam // required signature + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.Any( + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), + tfversion.RequireBelow(version.Must(version.NewVersion("1.0.0"))), + ), + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + ), + }, + Steps: []resource.TestStep{ + { + Config: `//example test config`, + }, + }, + }) +} +``` + + +## Custom Version Checks + +The package [`tfversion`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion) also provides the [`TerraformVersionCheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#TerraformVersionCheck) interface, which can be implemented for a custom version check. + +The [`tfversion.CheckTFVersionRequest`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#CheckTFVersionRequest) has a `TerraformVersion` field of type [`*version.Version`](https://pkg.go.dev/github.com/hashicorp/go-version#Version) which contains the version of the Terraform CLI binary running the test. + +The [`tfversion.CheckTFVersionResponse`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#CheckTFVersionResponse) has an `Error` field and a `Skip` field. The behavior of the version check depends on which field is populated. Populating the `Error` field will fail the associated test with the given error. +Populating the `Skip` field will pass the associated test by skipping the test with the given skip message. Only one of these fields should be populated. + +Here is an example implementation of a version check returns an error if the detected Terraform CLI version matches the given version: +```go +package example_test + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-version" +) + +func RequireNot(version *version.Version) TerraformVersionCheck { + return requireNotCheck{ + version: version, + } +} + +type requireNotCheck struct { + version *version.Version +} + +func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { + + if req.TerraformVersion.Equal(s.version) { + resp.Error = fmt.Errorf("unexpected Terraform CLI version: %s", s.version) + } +} +``` + +And example usage: +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func Test_RequireNot(t *testing.T) { + t.Parallel() + + resource.UnitTest(t, resource.TestCase{ + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "test": func() (tfprotov6.ProviderServer, error) { + return nil, nil + }, + }, + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireNot(version.Must(version.NewVersion("0.13.0"))), + }, + Steps: []resource.TestStep{ + { + Config: `//example test config`, + }, + }, + }) +} +``` From e2640db47846459e1a3fdaf3b0c25a1d94f0613f Mon Sep 17 00:00:00 2001 From: SBGoods Date: Thu, 1 Jun 2023 17:52:34 -0400 Subject: [PATCH 06/19] fix typo --- .../docs/plugin/testing/acceptance-tests/tfversion-checks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx b/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx index a2b813f08..d4cc11c52 100644 --- a/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx +++ b/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx @@ -152,7 +152,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func Test_Require_TF1_3(t *testing.T) { +func Test_Any(t *testing.T) { t.Parallel() resource.UnitTest(t, resource.TestCase{ From c551d8ce6c9f0ce8b010a47617262000769a95e0 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Fri, 2 Jun 2023 10:13:55 -0400 Subject: [PATCH 07/19] Add Terraform Version Checks page to website navigation --- website/data/plugin-testing-nav-data.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/website/data/plugin-testing-nav-data.json b/website/data/plugin-testing-nav-data.json index a3699b093..5e6468792 100644 --- a/website/data/plugin-testing-nav-data.json +++ b/website/data/plugin-testing-nav-data.json @@ -17,6 +17,10 @@ "title": "Test Steps", "path": "acceptance-tests/teststep" }, + { + "title": "Terraform Version Checks", + "path": "acceptance-tests/tfversion-checks" + }, { "title": "Plan Checks", "path": "acceptance-tests/plan-checks" From 8e0f3b9b89ad2d7a3eb279689e572f1704322e9b Mon Sep 17 00:00:00 2001 From: SBGoods Date: Fri, 2 Jun 2023 12:32:42 -0400 Subject: [PATCH 08/19] Add comment for `All()` and fix test --- tfversion/all.go | 3 +++ tfversion/all_test.go | 32 +++++++++++++++++++------------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tfversion/all.go b/tfversion/all.go index 2ac648652..b9947ee6e 100644 --- a/tfversion/all.go +++ b/tfversion/all.go @@ -7,6 +7,9 @@ import ( // All will return the first non-nil error or non-empty skip message // if any of the given checks return a non-nil error or non-empty skip message. // Otherwise, it will return a nil error and empty skip message (run the test) +// +// Use of All is only necessary when used in conjunction with Any as the +// TerraformVersionChecks field automatically applies a logical AND. func All(terraformVersionChecks ...TerraformVersionCheck) TerraformVersionCheck { return allCheck{ terraformVersionChecks: terraformVersionChecks, diff --git a/tfversion/all_test.go b/tfversion/all_test.go index 5ad1321a9..ec2539f73 100644 --- a/tfversion/all_test.go +++ b/tfversion/all_test.go @@ -22,10 +22,12 @@ func Test_All_RunTest(t *testing.T) { //nolint:paralleltest }, }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), - tfversion.SkipIf(version.Must(version.NewVersion("1.2.0"))), - tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.Any( + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + ), ), }, Steps: []r.TestStep{ @@ -49,11 +51,13 @@ func Test_All_SkipTest(t *testing.T) { //nolint:paralleltest }, }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), - tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), - tfversion.SkipIf(version.Must(version.NewVersion("1.0.7"))), - tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.Any( + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), + tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.0.7"))), + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + ), ), }, Steps: []r.TestStep{ @@ -78,10 +82,12 @@ func Test_All_Error(t *testing.T) { //nolint:paralleltest }, }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), - tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), - tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + tfversion.Any( + tfversion.All( + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + ), ), }, Steps: []r.TestStep{ From 812a2ffad97e0b2779fa60fa9bf2c47962fc80e5 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Fri, 2 Jun 2023 12:53:39 -0400 Subject: [PATCH 09/19] Add Changie Entries --- .changes/unreleased/FEATURES-20230602-101545.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124403.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124437.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124453.yaml | 7 +++++++ .changes/unreleased/FEATURES-20230602-124514.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124532.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124653.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124711.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124732.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124753.yaml | 6 ++++++ .changes/unreleased/FEATURES-20230602-124807.yaml | 6 ++++++ 11 files changed, 67 insertions(+) create mode 100644 .changes/unreleased/FEATURES-20230602-101545.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124403.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124437.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124453.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124514.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124532.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124653.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124711.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124732.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124753.yaml create mode 100644 .changes/unreleased/FEATURES-20230602-124807.yaml diff --git a/.changes/unreleased/FEATURES-20230602-101545.yaml b/.changes/unreleased/FEATURES-20230602-101545.yaml new file mode 100644 index 000000000..36bf8344b --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-101545.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Introduced new `tfversion` package with interface and built-in Terraform + version check functionality' +time: 2023-06-02T10:15:45.704158-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124403.yaml b/.changes/unreleased/FEATURES-20230602-124403.yaml new file mode 100644 index 000000000..7d3edea04 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124403.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `SkipAbove` built-in version check, which skips the test if + the Terraform CLI version is above the given maximum.' +time: 2023-06-02T12:44:03.123635-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124437.yaml b/.changes/unreleased/FEATURES-20230602-124437.yaml new file mode 100644 index 000000000..e9ef4cd95 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124437.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `SkipBelow` built-in version check, which skips the test if + the Terraform CLI version is below the given minimum.' +time: 2023-06-02T12:44:37.228557-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124453.yaml b/.changes/unreleased/FEATURES-20230602-124453.yaml new file mode 100644 index 000000000..dfb1585e8 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124453.yaml @@ -0,0 +1,7 @@ +kind: FEATURES +body: 'tfversion: Added `SkipBetween` built-in version check, which skips the test + if the Terraform CLI version is between the given minimum (inclusive) and maximum + (exclusive).' +time: 2023-06-02T12:44:53.737283-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124514.yaml b/.changes/unreleased/FEATURES-20230602-124514.yaml new file mode 100644 index 000000000..d01125254 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124514.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `SkipIf` built-in version check, which skips the test if the + Terraform CLI version matches the given version.' +time: 2023-06-02T12:45:14.812485-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124532.yaml b/.changes/unreleased/FEATURES-20230602-124532.yaml new file mode 100644 index 000000000..16390acca --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124532.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `RequireAbove` built-in version check, which fails the test + if the Terraform CLI version is below the given maximum.' +time: 2023-06-02T12:45:32.983833-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124653.yaml b/.changes/unreleased/FEATURES-20230602-124653.yaml new file mode 100644 index 000000000..732c86a43 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124653.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `RequireBelow` built-in version check, which fails the test + if the Terraform CLI version is above the given minimum.' +time: 2023-06-02T12:46:53.705136-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124711.yaml b/.changes/unreleased/FEATURES-20230602-124711.yaml new file mode 100644 index 000000000..7e2ceb4d9 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124711.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `RequireBetween` built-in version check, fails the test if + the Terraform CLI version is outside the given minimum (exclusive) and maximum (inclusive).' +time: 2023-06-02T12:47:11.762061-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124732.yaml b/.changes/unreleased/FEATURES-20230602-124732.yaml new file mode 100644 index 000000000..f802b01c4 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124732.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `RequireNot` built-in version check, which fails the test + if the Terraform CLI version matches the given version.' +time: 2023-06-02T12:47:32.000508-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124753.yaml b/.changes/unreleased/FEATURES-20230602-124753.yaml new file mode 100644 index 000000000..2fee8c19e --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124753.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `Any` built-in version check, which fails the test if none + of the given sub-checks return a nil error and empty skip message.' +time: 2023-06-02T12:47:53.181503-04:00 +custom: + Issue: "128" diff --git a/.changes/unreleased/FEATURES-20230602-124807.yaml b/.changes/unreleased/FEATURES-20230602-124807.yaml new file mode 100644 index 000000000..6c89987f6 --- /dev/null +++ b/.changes/unreleased/FEATURES-20230602-124807.yaml @@ -0,0 +1,6 @@ +kind: FEATURES +body: 'tfversion: Added `All` built-in version check, which fails or skips the test + if any of the given sub-checks return a non-nil error or non-empty skip message.' +time: 2023-06-02T12:48:07.933978-04:00 +custom: + Issue: "128" From 3ab2385e74720642bf49ebfe8a09ff0b33a9576f Mon Sep 17 00:00:00 2001 From: SBGoods Date: Mon, 5 Jun 2023 09:57:22 -0400 Subject: [PATCH 10/19] Add copyright headers --- helper/resource/tfversion_checks.go | 3 +++ tfversion/all.go | 3 +++ tfversion/all_test.go | 3 +++ tfversion/any.go | 3 +++ tfversion/any_test.go | 3 +++ tfversion/doc.go | 3 +++ tfversion/require_above.go | 3 +++ tfversion/require_above_test.go | 3 +++ tfversion/require_below.go | 3 +++ tfversion/require_below_test.go | 3 +++ tfversion/require_between.go | 3 +++ tfversion/require_between_test.go | 3 +++ tfversion/require_not.go | 3 +++ tfversion/require_not_test.go | 3 +++ tfversion/skip_above.go | 3 +++ tfversion/skip_above_test.go | 3 +++ tfversion/skip_below.go | 3 +++ tfversion/skip_below_test.go | 3 +++ tfversion/skip_between.go | 3 +++ tfversion/skip_between_test.go | 3 +++ tfversion/skip_if.go | 3 +++ tfversion/skip_if_test.go | 3 +++ tfversion/version_check.go | 3 +++ 23 files changed, 69 insertions(+) diff --git a/helper/resource/tfversion_checks.go b/helper/resource/tfversion_checks.go index 4f22d3b35..abe4d28a5 100644 --- a/helper/resource/tfversion_checks.go +++ b/helper/resource/tfversion_checks.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package resource import ( diff --git a/tfversion/all.go b/tfversion/all.go index b9947ee6e..d3ff49038 100644 --- a/tfversion/all.go +++ b/tfversion/all.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/all_test.go b/tfversion/all_test.go index ec2539f73..d47d890fd 100644 --- a/tfversion/all_test.go +++ b/tfversion/all_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/any.go b/tfversion/any.go index 4b7f11a7c..6696d562c 100644 --- a/tfversion/any.go +++ b/tfversion/any.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/any_test.go b/tfversion/any_test.go index 50a2220eb..439a73abd 100644 --- a/tfversion/any_test.go +++ b/tfversion/any_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/doc.go b/tfversion/doc.go index 828561dd6..d73b474d2 100644 --- a/tfversion/doc.go +++ b/tfversion/doc.go @@ -1,2 +1,5 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + // Package tfversion contains the Terraform version check interface, request/response structs, and common version check implementations. package tfversion diff --git a/tfversion/require_above.go b/tfversion/require_above.go index ab937dd89..b2b330b2b 100644 --- a/tfversion/require_above.go +++ b/tfversion/require_above.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/require_above_test.go b/tfversion/require_above_test.go index 78cf0f082..6d58f6f09 100644 --- a/tfversion/require_above_test.go +++ b/tfversion/require_above_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/require_below.go b/tfversion/require_below.go index 4c07a2417..3ef25c0ed 100644 --- a/tfversion/require_below.go +++ b/tfversion/require_below.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/require_below_test.go b/tfversion/require_below_test.go index 53fd84c22..71220d997 100644 --- a/tfversion/require_below_test.go +++ b/tfversion/require_below_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/require_between.go b/tfversion/require_between.go index 46517335b..3934c9c9f 100644 --- a/tfversion/require_between.go +++ b/tfversion/require_between.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/require_between_test.go b/tfversion/require_between_test.go index 5533a22ff..72229a1c7 100644 --- a/tfversion/require_between_test.go +++ b/tfversion/require_between_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/require_not.go b/tfversion/require_not.go index 052651a8e..caa4fa4dc 100644 --- a/tfversion/require_not.go +++ b/tfversion/require_not.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/require_not_test.go b/tfversion/require_not_test.go index b68d51a11..dee5968a6 100644 --- a/tfversion/require_not_test.go +++ b/tfversion/require_not_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/skip_above.go b/tfversion/skip_above.go index 4b67b4228..9069886f2 100644 --- a/tfversion/skip_above.go +++ b/tfversion/skip_above.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/skip_above_test.go b/tfversion/skip_above_test.go index 9191378f1..e40ee09f1 100644 --- a/tfversion/skip_above_test.go +++ b/tfversion/skip_above_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/skip_below.go b/tfversion/skip_below.go index 3067b78cf..584186145 100644 --- a/tfversion/skip_below.go +++ b/tfversion/skip_below.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/skip_below_test.go b/tfversion/skip_below_test.go index b38b89558..6154cc30d 100644 --- a/tfversion/skip_below_test.go +++ b/tfversion/skip_below_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/skip_between.go b/tfversion/skip_between.go index c0be9656b..daa05bf2b 100644 --- a/tfversion/skip_between.go +++ b/tfversion/skip_between.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/skip_between_test.go b/tfversion/skip_between_test.go index 829e2bb04..fb15f7659 100644 --- a/tfversion/skip_between_test.go +++ b/tfversion/skip_between_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/skip_if.go b/tfversion/skip_if.go index f903196b9..b735757e9 100644 --- a/tfversion/skip_if.go +++ b/tfversion/skip_if.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( diff --git a/tfversion/skip_if_test.go b/tfversion/skip_if_test.go index b22e82eff..8e9a8be9e 100644 --- a/tfversion/skip_if_test.go +++ b/tfversion/skip_if_test.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion_test import ( diff --git a/tfversion/version_check.go b/tfversion/version_check.go index f2029f446..3fbd20955 100644 --- a/tfversion/version_check.go +++ b/tfversion/version_check.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import ( From 6cd4d8348089d811ea1e467b435fe8a55bb89bcc Mon Sep 17 00:00:00 2001 From: SBGoods Date: Tue, 6 Jun 2023 12:47:36 -0400 Subject: [PATCH 11/19] Add comments to tests --- tfversion/all_test.go | 20 ++++++++++---------- tfversion/any_test.go | 14 +++++++------- tfversion/require_above_test.go | 1 + tfversion/require_below_test.go | 1 + tfversion/require_between_test.go | 2 ++ tfversion/skip_above_test.go | 2 ++ tfversion/skip_below_test.go | 2 ++ tfversion/skip_between_test.go | 2 ++ 8 files changed, 27 insertions(+), 17 deletions(-) diff --git a/tfversion/all_test.go b/tfversion/all_test.go index d47d890fd..4751eeb5c 100644 --- a/tfversion/all_test.go +++ b/tfversion/all_test.go @@ -27,9 +27,9 @@ func Test_All_RunTest(t *testing.T) { //nolint:paralleltest TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), - tfversion.SkipIf(version.Must(version.NewVersion("1.2.0"))), - tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), //returns nil + tfversion.SkipIf(version.Must(version.NewVersion("1.2.0"))), //returns nil + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), //returns nil ), ), }, @@ -56,10 +56,10 @@ func Test_All_SkipTest(t *testing.T) { //nolint:paralleltest TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), - tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), - tfversion.SkipIf(version.Must(version.NewVersion("1.0.7"))), - tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), //returns nil + tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), //returns skip + tfversion.SkipIf(version.Must(version.NewVersion("1.0.7"))), //returns skip + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), //returns nil ), ), }, @@ -87,9 +87,9 @@ func Test_All_Error(t *testing.T) { //nolint:paralleltest TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), - tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), - tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), //returns error + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), //returns skip + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), //returns nil ), ), }, diff --git a/tfversion/any_test.go b/tfversion/any_test.go index 439a73abd..bf5e1f7b1 100644 --- a/tfversion/any_test.go +++ b/tfversion/any_test.go @@ -26,8 +26,8 @@ func Test_Any_RunTest(t *testing.T) { //nolint:paralleltest }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( - tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), - tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), //returns error + tfversion.RequireBelow(version.Must(version.NewVersion("1.2.0"))), //returns nil ), }, Steps: []r.TestStep{ @@ -52,8 +52,8 @@ func Test_Any_SkipTest(t *testing.T) { //nolint:paralleltest }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( - tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), - tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), //returns skip + tfversion.SkipBelow(version.Must(version.NewVersion("1.2.0"))), //returns skip ), }, Steps: []r.TestStep{ @@ -79,9 +79,9 @@ func Test_Any_Error(t *testing.T) { //nolint:paralleltest }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( - tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), - tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), - tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), //returns skip + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), //returns error + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), //returns error ), }, Steps: []r.TestStep{ diff --git a/tfversion/require_above_test.go b/tfversion/require_above_test.go index 6d58f6f09..b78ac8b13 100644 --- a/tfversion/require_above_test.go +++ b/tfversion/require_above_test.go @@ -30,6 +30,7 @@ func Test_RequireAbove(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //nullable argument only available in TF v1.1.0+ Config: `variable "a" { nullable = true default = "hello" diff --git a/tfversion/require_below_test.go b/tfversion/require_below_test.go index 71220d997..9c3e40d8b 100644 --- a/tfversion/require_below_test.go +++ b/tfversion/require_below_test.go @@ -30,6 +30,7 @@ func Test_RequireBelow(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //module_variable_optional_attrs experiment is deprecated in TF v1.3.0 Config: ` terraform { experiments = [module_variable_optional_attrs] diff --git a/tfversion/require_between_test.go b/tfversion/require_between_test.go index 72229a1c7..c15e22006 100644 --- a/tfversion/require_between_test.go +++ b/tfversion/require_between_test.go @@ -30,6 +30,8 @@ func Test_RequireBetween(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //module_variable_optional_attrs experiment is deprecated in TF v1.3.0 + //precondition block is only available in TF v1.2.0+ Config: ` terraform { experiments = [module_variable_optional_attrs] diff --git a/tfversion/skip_above_test.go b/tfversion/skip_above_test.go index e40ee09f1..4390cc9cf 100644 --- a/tfversion/skip_above_test.go +++ b/tfversion/skip_above_test.go @@ -27,6 +27,7 @@ func Test_SkipAbove_SkipTest(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //module_variable_optional_attrs experiment is deprecated in TF v1.3.0 Config: ` terraform { experiments = [module_variable_optional_attrs] @@ -51,6 +52,7 @@ func Test_SkipAbove_RunTest(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //module_variable_optional_attrs experiment is deprecated in TF v1.3.0 Config: ` terraform { experiments = [module_variable_optional_attrs] diff --git a/tfversion/skip_below_test.go b/tfversion/skip_below_test.go index 6154cc30d..e8c43089e 100644 --- a/tfversion/skip_below_test.go +++ b/tfversion/skip_below_test.go @@ -27,6 +27,7 @@ func Test_SkipBelow_SkipTest(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //nullable argument only available in TF v1.1.0+ Config: `variable "a" { nullable = true default = "hello" @@ -50,6 +51,7 @@ func Test_SkipBelow_RunTest(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //nullable argument only available in TF v1.1.0+ Config: `variable "a" { nullable = true default = "hello" diff --git a/tfversion/skip_between_test.go b/tfversion/skip_between_test.go index fb15f7659..098c03d20 100644 --- a/tfversion/skip_between_test.go +++ b/tfversion/skip_between_test.go @@ -29,6 +29,8 @@ func Test_SkipBetween_SkipTest(t *testing.T) { //nolint:paralleltest }, Steps: []r.TestStep{ { + //module_variable_optional_attrs experiment is deprecated in TF v1.3.0 + //precondition block is only available in TF v1.2.0+ Config: ` terraform { experiments = [module_variable_optional_attrs] From 41b34cd9a40f3257e09bdf823c2eb02f16fb7657 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Tue, 6 Jun 2023 13:33:14 -0400 Subject: [PATCH 12/19] Add tfversion_checks_test.go --- helper/resource/tfversion_checks_test.go | 65 ++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 helper/resource/tfversion_checks_test.go diff --git a/helper/resource/tfversion_checks_test.go b/helper/resource/tfversion_checks_test.go new file mode 100644 index 000000000..0bc2238f4 --- /dev/null +++ b/helper/resource/tfversion_checks_test.go @@ -0,0 +1,65 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package resource + +import ( + "context" + "testing" + + "github.com/hashicorp/go-version" + + "github.com/hashicorp/terraform-plugin-testing/internal/plugintest" + "github.com/hashicorp/terraform-plugin-testing/tfversion" + + testinginterface "github.com/mitchellh/go-testing-interface" +) + +func TestRunTFVersionChecks(t *testing.T) { + t.Parallel() + + tests := map[string]struct { + versionChecks []tfversion.TerraformVersionCheck + tfVersion *version.Version + expectError bool + }{ + "run-test": { + versionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), + tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + }, + tfVersion: version.Must(version.NewVersion("1.3.0")), + expectError: false, + }, + "skip-test": { + versionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipIf(version.Must(version.NewVersion("1.1.0"))), + }, + tfVersion: version.Must(version.NewVersion("1.1.0")), + expectError: false, + }, + "fail-test": { + versionChecks: []tfversion.TerraformVersionCheck{ + tfversion.RequireNot(version.Must(version.NewVersion("1.1.0"))), + }, + tfVersion: version.Must(version.NewVersion("1.1.0")), + expectError: true, + }, + } + + for name, test := range tests { + name, test := name, test + + t.Run(name, func(t *testing.T) { + t.Parallel() + + if test.expectError { + plugintest.TestExpectTFatal(t, func() { + runTFVersionChecks(context.Background(), &testinginterface.RuntimeT{}, test.tfVersion, test.versionChecks) + }) + } else { + runTFVersionChecks(context.Background(), t, test.tfVersion, test.versionChecks) + } + }) + } +} From 32ceee82aa7c4831a3a0e5a3a5e2a4b723c8a127 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Wed, 7 Jun 2023 12:08:28 -0400 Subject: [PATCH 13/19] Rename interface request and response to avoid abbreviation --- helper/resource/tfversion_checks.go | 4 ++-- tfversion/all.go | 6 +++--- tfversion/any.go | 6 +++--- tfversion/require_above.go | 2 +- tfversion/require_below.go | 2 +- tfversion/require_between.go | 2 +- tfversion/require_not.go | 2 +- tfversion/skip_above.go | 2 +- tfversion/skip_below.go | 2 +- tfversion/skip_between.go | 2 +- tfversion/skip_if.go | 2 +- tfversion/version_check.go | 14 +++++++------- 12 files changed, 23 insertions(+), 23 deletions(-) diff --git a/helper/resource/tfversion_checks.go b/helper/resource/tfversion_checks.go index abe4d28a5..1bec0abd6 100644 --- a/helper/resource/tfversion_checks.go +++ b/helper/resource/tfversion_checks.go @@ -16,8 +16,8 @@ func runTFVersionChecks(ctx context.Context, t testing.T, terraformVersion *vers t.Helper() for _, tfVersionCheck := range terraformVersionChecks { - resp := tfversion.CheckTFVersionResponse{} - tfVersionCheck.CheckTerraformVersion(ctx, tfversion.CheckTFVersionRequest{TerraformVersion: terraformVersion}, &resp) + resp := tfversion.CheckTerraformVersionResponse{} + tfVersionCheck.CheckTerraformVersion(ctx, tfversion.CheckTerraformVersionRequest{TerraformVersion: terraformVersion}, &resp) if resp.Error != nil { t.Fatalf(resp.Error.Error()) diff --git a/tfversion/all.go b/tfversion/all.go index d3ff49038..78e1f1780 100644 --- a/tfversion/all.go +++ b/tfversion/all.go @@ -25,12 +25,12 @@ type allCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (a allCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (a allCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { for _, subCheck := range a.terraformVersionChecks { - checkResp := CheckTFVersionResponse{} + checkResp := CheckTerraformVersionResponse{} - subCheck.CheckTerraformVersion(ctx, CheckTFVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) + subCheck.CheckTerraformVersion(ctx, CheckTerraformVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) if checkResp.Error != nil { resp.Error = checkResp.Error diff --git a/tfversion/any.go b/tfversion/any.go index 6696d562c..27088e1a5 100644 --- a/tfversion/any.go +++ b/tfversion/any.go @@ -27,14 +27,14 @@ type anyCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (a anyCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (a anyCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { var joinedErrors error strBuilder := strings.Builder{} for _, subCheck := range a.terraformVersionChecks { - checkResp := CheckTFVersionResponse{} + checkResp := CheckTerraformVersionResponse{} - subCheck.CheckTerraformVersion(ctx, CheckTFVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) + subCheck.CheckTerraformVersion(ctx, CheckTerraformVersionRequest{TerraformVersion: req.TerraformVersion}, &checkResp) if checkResp.Error == nil && checkResp.Skip == "" { resp.Error = nil diff --git a/tfversion/require_above.go b/tfversion/require_above.go index b2b330b2b..4734bcf6e 100644 --- a/tfversion/require_above.go +++ b/tfversion/require_above.go @@ -26,7 +26,7 @@ type requireAboveCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (r requireAboveCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (r requireAboveCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.LessThan(r.minimumVersion) { resp.Error = fmt.Errorf("expected Terraform CLI version above %s but detected version is %s", diff --git a/tfversion/require_below.go b/tfversion/require_below.go index 3ef25c0ed..99efa5346 100644 --- a/tfversion/require_below.go +++ b/tfversion/require_below.go @@ -26,7 +26,7 @@ type requireBelowCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s requireBelowCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s requireBelowCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.GreaterThan(s.maximumVersion) { resp.Error = fmt.Errorf("expected Terraform CLI version below %s but detected version is %s", diff --git a/tfversion/require_between.go b/tfversion/require_between.go index 3934c9c9f..b99297928 100644 --- a/tfversion/require_between.go +++ b/tfversion/require_between.go @@ -29,7 +29,7 @@ type requireBetweenCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s requireBetweenCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s requireBetweenCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.LessThan(s.minimumVersion) || req.TerraformVersion.GreaterThanOrEqual(s.maximumVersion) { resp.Error = fmt.Errorf("expected Terraform CLI version between %s and %s but detected version is %s", diff --git a/tfversion/require_not.go b/tfversion/require_not.go index caa4fa4dc..18a9b68d1 100644 --- a/tfversion/require_not.go +++ b/tfversion/require_not.go @@ -24,7 +24,7 @@ type requireNotCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.Equal(s.version) { resp.Error = fmt.Errorf("unexpected Terraform CLI version: %s", s.version) diff --git a/tfversion/skip_above.go b/tfversion/skip_above.go index 9069886f2..ffc69b857 100644 --- a/tfversion/skip_above.go +++ b/tfversion/skip_above.go @@ -26,7 +26,7 @@ type skipAboveCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s skipAboveCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s skipAboveCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.GreaterThan(s.maximumVersion) { resp.Skip = fmt.Sprintf("Terraform CLI version %s is above maximum version %s: skipping test", diff --git a/tfversion/skip_below.go b/tfversion/skip_below.go index 584186145..0b3dffddc 100644 --- a/tfversion/skip_below.go +++ b/tfversion/skip_below.go @@ -26,7 +26,7 @@ type skipBelowCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s skipBelowCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s skipBelowCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.LessThan(s.minimumVersion) { resp.Skip = fmt.Sprintf("Terraform CLI version %s is below minimum version %s: skipping test", diff --git a/tfversion/skip_between.go b/tfversion/skip_between.go index daa05bf2b..fb6e94108 100644 --- a/tfversion/skip_between.go +++ b/tfversion/skip_between.go @@ -29,7 +29,7 @@ type skipBetweenCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s skipBetweenCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s skipBetweenCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.GreaterThanOrEqual(s.minimumVersion) && req.TerraformVersion.LessThan(s.maximumVersion) { resp.Skip = fmt.Sprintf("Terraform CLI version %s is between %s and %s: skipping test.", diff --git a/tfversion/skip_if.go b/tfversion/skip_if.go index b735757e9..6ece5e05d 100644 --- a/tfversion/skip_if.go +++ b/tfversion/skip_if.go @@ -24,7 +24,7 @@ type skipIfCheck struct { } // CheckTerraformVersion satisfies the TerraformVersionCheck interface. -func (s skipIfCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { +func (s skipIfCheck) CheckTerraformVersion(ctx context.Context, req CheckTerraformVersionRequest, resp *CheckTerraformVersionResponse) { if req.TerraformVersion.Equal(s.version) { resp.Skip = fmt.Sprintf("Terraform CLI version is %s: skipping test.", s.version) diff --git a/tfversion/version_check.go b/tfversion/version_check.go index 3fbd20955..554ec2247 100644 --- a/tfversion/version_check.go +++ b/tfversion/version_check.go @@ -18,19 +18,19 @@ import ( // interface for implementing their own custom logic. type TerraformVersionCheck interface { // CheckTerraformVersion should implement the logic to either pass, error (failing the test), or skip (passing the test). - CheckTerraformVersion(context.Context, CheckTFVersionRequest, *CheckTFVersionResponse) + CheckTerraformVersion(context.Context, CheckTerraformVersionRequest, *CheckTerraformVersionResponse) } -// CheckTFVersionRequest is the request received for the CheckTerraformVersion method of the -// TerraformVersionCheck interface. The response of that method is CheckTFVersionResponse. -type CheckTFVersionRequest struct { +// CheckTerraformVersionRequest is the request received for the CheckTerraformVersion method of the +// TerraformVersionCheck interface. The response of that method is CheckTerraformVersionResponse. +type CheckTerraformVersionRequest struct { // TerraformVersion is the version associated with the selected Terraform CLI binary. TerraformVersion *version.Version } -// CheckTFVersionResponse is the response returned for the CheckTerraformVersion method of the -// TerraformVersionCheck interface. The request of that method is CheckTFVersionRequest. -type CheckTFVersionResponse struct { +// CheckTerraformVersionResponse is the response returned for the CheckTerraformVersion method of the +// TerraformVersionCheck interface. The request of that method is CheckTerraformVersionRequest. +type CheckTerraformVersionResponse struct { // Error will result in failing the test with a given error message. Error error From c0d6b90216b082104c5b28632a2a32752eab3663 Mon Sep 17 00:00:00 2001 From: SBGoods Date: Wed, 7 Jun 2023 12:09:59 -0400 Subject: [PATCH 14/19] Add version variables --- tfversion/versions.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tfversion/versions.go diff --git a/tfversion/versions.go b/tfversion/versions.go new file mode 100644 index 000000000..43db72585 --- /dev/null +++ b/tfversion/versions.go @@ -0,0 +1,29 @@ +package tfversion + +import "github.com/hashicorp/go-version" + +// Common use version variables to simplify provider testing implementations. +// This list is not intended to be exhaustive of all Terraform versions, +// however these should at least include cases where Terraform +// introduced new configuration language features. +var ( + // Version0_12_26 is the first Terraform CLI version supported + // by the testing code. + Version0_12_26 *version.Version = version.Must(version.NewVersion("0.12.26")) + + // Major versions + + Version1_0_0 *version.Version = version.Must(version.NewVersion("1.0.0")) + Version2_0_0 *version.Version = version.Must(version.NewVersion("2.0.0")) + + // Minor versions + + Version0_13_0 *version.Version = version.Must(version.NewVersion("0.13.0")) + Version0_14_0 *version.Version = version.Must(version.NewVersion("0.14.0")) + Version0_15_0 *version.Version = version.Must(version.NewVersion("0.15.0")) + Version1_1_0 *version.Version = version.Must(version.NewVersion("1.1.0")) + Version1_2_0 *version.Version = version.Must(version.NewVersion("1.2.0")) + Version1_3_0 *version.Version = version.Must(version.NewVersion("1.3.0")) + Version1_4_0 *version.Version = version.Must(version.NewVersion("1.4.0")) + Version1_5_0 *version.Version = version.Must(version.NewVersion("1.5.0")) +) From 2199054827f3f286588288fac2a4a8c950e8e92c Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 7 Jun 2023 12:12:30 -0400 Subject: [PATCH 15/19] Update documentation to include and use pre-defined version variables Co-authored-by: Brian Flad --- .../testing/acceptance-tests/testcase.mdx | 8 ++-- .../acceptance-tests/tfversion-checks.mdx | 41 +++++++++++-------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/website/docs/plugin/testing/acceptance-tests/testcase.mdx b/website/docs/plugin/testing/acceptance-tests/testcase.mdx index d5e3a4290..98aa9586c 100644 --- a/website/docs/plugin/testing/acceptance-tests/testcase.mdx +++ b/website/docs/plugin/testing/acceptance-tests/testcase.mdx @@ -169,7 +169,7 @@ func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), //built-in check from tfversion package + tfversion.SkipBelow(tfversion.Version1_1_0), // built-in check from tfversion package }, // ... }) @@ -202,7 +202,7 @@ func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + tfversion.SkipBelow(tfversion.Version1_1_0), }, Providers: testAccProviders, // ... @@ -248,7 +248,7 @@ func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + tfversion.SkipBelow(tfversion.Version1_1_0), }, Providers: testAccProviders, CheckDestroy: testAccCheckExampleResourceDestroy, @@ -319,7 +319,7 @@ func TestAccExampleWidget_basic(t *testing.T) { resource.Test(t, resource.TestCase{ PreCheck: func() { testAccPreCheck(t) }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBelow(version.Must(version.NewVersion("1.1.0"))), + tfversion.SkipBelow(tfversion.Version1_1_0), }, Providers: testAccProviders, CheckDestroy: testAccCheckExampleResourceDestroy, diff --git a/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx b/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx index d4cc11c52..cb2865b7a 100644 --- a/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx +++ b/website/docs/plugin/testing/acceptance-tests/tfversion-checks.mdx @@ -13,10 +13,14 @@ The Terraform CLI version is determined by the binary selected by the [`TF_ACC_T A **version check** will either return an error and fail the associated test, return a skip message and pass the associated test immediately by skipping, or it will return nothing and allow the associated test to run. -## Built-in Version Checks +## Built-in Version Checks and Variables The `terraform-plugin-testing` module provides a package [`tfversion`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion) with built-in version checks for common use-cases. There are three types of version checks: Skip Checks, Require Checks, and Collection Checks. +## Version Variables + +The built-in checks in the `tfversion` package typically require the use of the [`github.com/hashicorp/go-version`](https://pkg.go.dev/github.com/hashicorp/go-version) module [`version.Version`](https://pkg.go.dev/github.com/hashicorp/go-version#Version) type. To simplify provider testing implementations, the `tfversion` package provides [built-in variables](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#pkg-variables) for common use case versions, such as each released minor and major Terraform version. These follow the pattern of `Version{MAJOR}_{MINOR}_{PATCH}` with the major, minor, and patch version numbers, such as `Version1_2_0`. + ### Skip Version Checks Skip Version Checks will pass the associated test by skipping and provide a skip message if the detected Terraform CLI version satisfies the specified check criteria. @@ -40,7 +44,6 @@ package example_test import ( "testing" - "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -57,7 +60,7 @@ func Test_Skip_TF14(t *testing.T) { }, }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.SkipBetween(version.Must(version.NewVersion("0.14.0")), version.Must(version.NewVersion("0.15.0"))), + tfversion.SkipBetween(tfversion.Version0_14_0, tfversion.Version0_15_0), }, Steps: []resource.TestStep{ { @@ -92,7 +95,6 @@ package example_test import ( "testing" - "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -109,7 +111,7 @@ func Test_Require_TF1_3(t *testing.T) { }, }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireAbove(version.Must(version.NewVersion("1.3.0"))), + tfversion.RequireAbove(tfversion.Version1_3_0), }, Steps: []resource.TestStep{ { @@ -145,7 +147,6 @@ package example_test import ( "testing" - "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -164,10 +165,10 @@ func Test_Any(t *testing.T) { TerraformVersionChecks: []tfversion.TerraformVersionCheck{ tfversion.Any( tfversion.All( - tfversion.RequireNot(version.Must(version.NewVersion("0.15.0"))), - tfversion.RequireBelow(version.Must(version.NewVersion("1.0.0"))), - ), - tfversion.RequireAbove(version.Must(version.NewVersion("1.2.0"))), + tfversion.RequireNot(tfversion.Version0_15_0), + tfversion.RequireBelow(tfversion.Version1_0_0), + ), + tfversion.RequireAbove(tfversion.Version1_2_0), ), }, Steps: []resource.TestStep{ @@ -184,12 +185,13 @@ func Test_Any(t *testing.T) { The package [`tfversion`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion) also provides the [`TerraformVersionCheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#TerraformVersionCheck) interface, which can be implemented for a custom version check. -The [`tfversion.CheckTFVersionRequest`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#CheckTFVersionRequest) has a `TerraformVersion` field of type [`*version.Version`](https://pkg.go.dev/github.com/hashicorp/go-version#Version) which contains the version of the Terraform CLI binary running the test. +The [`tfversion.CheckTerraformVersionRequest`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#CheckTerraformVersionRequest) has a `TerraformVersion` field of type [`*version.Version`](https://pkg.go.dev/github.com/hashicorp/go-version#Version) which contains the version of the Terraform CLI binary running the test. -The [`tfversion.CheckTFVersionResponse`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#CheckTFVersionResponse) has an `Error` field and a `Skip` field. The behavior of the version check depends on which field is populated. Populating the `Error` field will fail the associated test with the given error. +The [`tfversion.CheckTerraformVersionResponse`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/tfversion#CheckTerraformVersionResponse) has an `Error` field and a `Skip` field. The behavior of the version check depends on which field is populated. Populating the `Error` field will fail the associated test with the given error. Populating the `Skip` field will pass the associated test by skipping the test with the given skip message. Only one of these fields should be populated. Here is an example implementation of a version check returns an error if the detected Terraform CLI version matches the given version: + ```go package example_test @@ -200,9 +202,13 @@ import ( "github.com/hashicorp/go-version" ) -func RequireNot(version *version.Version) TerraformVersionCheck { +// Ensure implementation satisfies the tfversion.TerraformVersionCheck interface. +var _ tfversion.TerraformVersionCheck = requireNotCheck{} + +// RequireNot will fail the test if the given version matches. +func RequireNot(v *version.Version) tfversion.TerraformVersionCheck { return requireNotCheck{ - version: version, + version: v, } } @@ -210,8 +216,7 @@ type requireNotCheck struct { version *version.Version } -func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req CheckTFVersionRequest, resp *CheckTFVersionResponse) { - +func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req tfversion.CheckTerraformVersionRequest, resp *tfversion.CheckTerraformVersionResponse) { if req.TerraformVersion.Equal(s.version) { resp.Error = fmt.Errorf("unexpected Terraform CLI version: %s", s.version) } @@ -219,13 +224,13 @@ func (s requireNotCheck) CheckTerraformVersion(ctx context.Context, req CheckTFV ``` And example usage: + ```go package example_test import ( "testing" - "github.com/hashicorp/go-version" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-testing/helper/resource" @@ -242,7 +247,7 @@ func Test_RequireNot(t *testing.T) { }, }, TerraformVersionChecks: []tfversion.TerraformVersionCheck{ - tfversion.RequireNot(version.Must(version.NewVersion("0.13.0"))), + tfversion.RequireNot(tfversion.Version0_13_0), }, Steps: []resource.TestStep{ { From 5b8d8dbcdc0bc6809320e33546257e75cd284e7f Mon Sep 17 00:00:00 2001 From: SBGoods Date: Wed, 7 Jun 2023 12:13:32 -0400 Subject: [PATCH 16/19] Add copyright header --- tfversion/versions.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tfversion/versions.go b/tfversion/versions.go index 43db72585..6cd04b27f 100644 --- a/tfversion/versions.go +++ b/tfversion/versions.go @@ -1,3 +1,6 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + package tfversion import "github.com/hashicorp/go-version" From dc15bbc11c999db61630db2eae9d5d6561e765b6 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Wed, 7 Jun 2023 12:17:17 -0400 Subject: [PATCH 17/19] Properly handle error Co-authored-by: Brian Flad --- internal/plugintest/helper.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/plugintest/helper.go b/internal/plugintest/helper.go index b5ffe1f02..3c9772cfc 100644 --- a/internal/plugintest/helper.go +++ b/internal/plugintest/helper.go @@ -85,7 +85,11 @@ func InitHelper(ctx context.Context, config *Config) (*Helper, error) { return nil, fmt.Errorf("unable to create terraform-exec instance: %w", err) } - tfVersion, _, _ := tf.Version(ctx, false) + tfVersion, _, err := tf.Version(ctx, false) + + if err != nil { + return nil, fmt.Errorf("error calling terraform version command: %w", err) + } return &Helper{ baseDir: baseDir, From b267337b75becf8b6528c105cf4e469c162ec79e Mon Sep 17 00:00:00 2001 From: SBGoods Date: Mon, 12 Jun 2023 14:45:36 -0400 Subject: [PATCH 18/19] Set golangci-lint run timeout to 5 minutes --- .golangci.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.golangci.yml b/.golangci.yml index 560707ab8..bd09eed57 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -22,4 +22,8 @@ linters: - unconvert - unparam - unused - - vet \ No newline at end of file + - vet + +run: + # Prevent false positive timeouts in CI + timeout: 5m \ No newline at end of file From 75af1bddfedaa0913af16bbfbb288b7d92e6fce9 Mon Sep 17 00:00:00 2001 From: Selena Goods Date: Mon, 12 Jun 2023 15:43:12 -0400 Subject: [PATCH 19/19] Fix wording in logging messages Co-authored-by: Brian Flad --- helper/resource/testing.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/helper/resource/testing.go b/helper/resource/testing.go index ca2ed6c3a..be7c01c6c 100644 --- a/helper/resource/testing.go +++ b/helper/resource/testing.go @@ -835,11 +835,11 @@ func Test(t testing.T, c TestCase) { // This is done after creating the helper because a working directory is required // to retrieve the Terraform version. if c.TerraformVersionChecks != nil { - logging.HelperResourceDebug(ctx, "Calling TestCase runTFVersionChecks") + logging.HelperResourceDebug(ctx, "Calling TestCase Terraform version checks") runTFVersionChecks(ctx, t, helper.TerraformVersion(), c.TerraformVersionChecks) - logging.HelperResourceDebug(ctx, "Called TestCase runTFVersionChecks") + logging.HelperResourceDebug(ctx, "Called TestCase Terraform version checks") } runNewTest(ctx, t, c, helper)