blob: 86f9667bffa1e3cbcf6d820ea283d8f4ceceaadd [file] [log] [blame]
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package acctest
import (
"context"
"errors"
"fmt"
"slices"
"testing"
"time"
tfjson "github.com/hashicorp/terraform-json"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/terraform"
transport_tpg "github.com/hashicorp/terraform-provider-google-beta/google-beta/transport"
)
// General test utils
var _ plancheck.PlanCheck = expectNoDelete{}
type expectNoDelete struct{}
func (e expectNoDelete) CheckPlan(ctx context.Context, req plancheck.CheckPlanRequest, resp *plancheck.CheckPlanResponse) {
var result error
for _, rc := range req.Plan.ResourceChanges {
if slices.Contains(rc.Change.Actions, tfjson.ActionDelete) {
result = errors.Join(result, fmt.Errorf("expected no deletion of resources, but %s has planned deletion", rc.Address))
}
}
resp.Error = result
}
func ExpectNoDelete() plancheck.PlanCheck {
return expectNoDelete{}
}
// TestExtractResourceAttr navigates a test's state to find the specified resource (or data source) attribute and makes the value
// accessible via the attributeValue string pointer.
func TestExtractResourceAttr(resourceName string, attributeName string, attributeValue *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[resourceName] // To find a datasource, include `data.` at the start of the resourceName value
if !ok {
return fmt.Errorf("resource name %s not found in state", resourceName)
}
attrValue, ok := rs.Primary.Attributes[attributeName]
if !ok {
return fmt.Errorf("attribute %s not found in resource %s state", attributeName, resourceName)
}
*attributeValue = attrValue
return nil
}
}
// GetTestRegion has the same logic as the provider's GetRegion, to be used in tests.
func GetTestRegion(is *terraform.InstanceState, config *transport_tpg.Config) (string, error) {
if res, ok := is.Attributes["region"]; ok {
return res, nil
}
if config.Region != "" {
return config.Region, nil
}
return "", fmt.Errorf("%q: required field is not set", "region")
}
// GetTestProject has the same logic as the provider's GetProject, to be used in tests.
func GetTestProject(is *terraform.InstanceState, config *transport_tpg.Config) (string, error) {
if res, ok := is.Attributes["project"]; ok {
return res, nil
}
if config.Project != "" {
return config.Project, nil
}
return "", fmt.Errorf("%q: required field is not set", "project")
}
// Some tests fail during VCR. One common case is race conditions when creating resources.
// If a test config adds two fine-grained resources with the same parent it is undefined
// which will be created first, causing VCR to fail ~50% of the time
func SkipIfVcr(t *testing.T) {
if IsVcrEnabled() {
t.Skipf("VCR enabled, skipping test: %s", t.Name())
}
}
func SleepInSecondsForTest(t int) resource.TestCheckFunc {
return func(s *terraform.State) error {
time.Sleep(time.Duration(t) * time.Second)
return nil
}
}
// TestCheckAttributeValuesEqual compares two string pointers, which have been used to retrieve attribute values from the test's state.
func TestCheckAttributeValuesEqual(i *string, j *string) resource.TestCheckFunc {
return func(s *terraform.State) error {
if testStringValue(i) != testStringValue(j) {
return fmt.Errorf("attribute values are different, got %s and %s", testStringValue(i), testStringValue(j))
}
return nil
}
}
// testStringValue returns string values from string pointers, handling nil pointers.
func testStringValue(sPtr *string) string {
if sPtr == nil {
return ""
}
return *sPtr
}