terraform/internal/cloud/backend_query_test.go
2025-09-17 12:29:04 +02:00

141 lines
4.7 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package cloud
import (
"context"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/cli"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/backend/backendrun"
"github.com/hashicorp/terraform/internal/command/arguments"
"github.com/hashicorp/terraform/internal/command/clistate"
"github.com/hashicorp/terraform/internal/command/jsonformat"
"github.com/hashicorp/terraform/internal/command/views"
"github.com/hashicorp/terraform/internal/depsfile"
"github.com/hashicorp/terraform/internal/initwd"
"github.com/hashicorp/terraform/internal/states/statemgr"
"github.com/hashicorp/terraform/internal/terminal"
)
func testOperationQuery(t *testing.T, configDir string) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
return testOperationQueryWithTimeout(t, configDir, 0)
}
func testOperationQueryWithTimeout(t *testing.T, configDir string, timeout time.Duration) (*backendrun.Operation, func(), func(*testing.T) *terminal.TestOutput) {
t.Helper()
_, configLoader, configCleanup := initwd.MustLoadConfigForTests(t, configDir, "tests")
streams, done := terminal.StreamsForTesting(t)
view := views.NewView(streams)
stateLockerView := views.NewStateLocker(arguments.ViewHuman, view)
operationView := views.NewQueryOperation(arguments.ViewHuman, false, view)
// Many of our tests use an overridden "null" provider that's just in-memory
// inside the test process, not a separate plugin on disk.
depLocks := depsfile.NewLocks()
depLocks.SetProviderOverridden(addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/null"))
return &backendrun.Operation{
ConfigDir: configDir,
ConfigLoader: configLoader,
StateLocker: clistate.NewLocker(timeout, stateLockerView),
Type: backendrun.OperationTypePlan,
View: operationView,
DependencyLocks: depLocks,
Query: true,
}, configCleanup, done
}
func TestCloud_queryBasic(t *testing.T) {
b, bCleanup := testBackendWithName(t)
defer bCleanup()
op, configCleanup, done := testOperationQuery(t, "./testdata/query")
defer configCleanup()
defer done(t)
op.Workspace = testBackendSingleWorkspaceName
run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("error starting operation: %v", err)
}
<-run.Done()
if run.Result != backendrun.OperationSuccess {
t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String())
}
output := b.CLI.(*cli.MockUi).OutputWriter.String()
if !strings.Contains(output, "Running query in HCP Terraform") {
t.Fatalf("expected HCP Terraform header in output: %s", output)
}
if !strings.Contains(output, "list.concept_pet.pets id=") {
t.Fatalf("expected query results in output: %s", output)
}
stateMgr, _ := b.StateMgr(testBackendSingleWorkspaceName)
// An error suggests that the state was not unlocked after the operation finished
if _, err := stateMgr.Lock(statemgr.NewLockInfo()); err != nil {
t.Fatalf("unexpected error locking state after successful plan: %s", err.Error())
}
}
func TestCloud_queryJSONBasic(t *testing.T) {
b, bCleanup := testBackendWithName(t)
defer bCleanup()
stream, close := terminal.StreamsForTesting(t)
b.renderer = &jsonformat.Renderer{
Streams: stream,
Colorize: mockColorize(),
}
op, configCleanup, done := testOperationQuery(t, "./testdata/query-json-basic")
defer configCleanup()
defer done(t)
op.Workspace = testBackendSingleWorkspaceName
mockSROWorkspace(t, b, op.Workspace)
run, err := b.Operation(context.Background(), op)
if err != nil {
t.Fatalf("error starting operation: %v", err)
}
<-run.Done()
if run.Result != backendrun.OperationSuccess {
t.Fatalf("operation failed: %s", b.CLI.(*cli.MockUi).ErrorWriter.String())
}
outp := close(t)
gotOut := outp.Stdout()
expectedOut := `list.concept_pet.pets id=large-roughy,legs=2 This is a large-roughy
list.concept_pet.pets id=able-werewolf,legs=5 This is a able-werewolf
list.concept_pet.pets id=complete-gannet,legs=6 This is a complete-gannet
list.concept_pet.pets id=charming-beagle,legs=3 This is a charming-beagle
list.concept_pet.pets id=legal-lamprey,legs=2 This is a legal-lamprey
`
if diff := cmp.Diff(expectedOut, gotOut); diff != "" {
t.Fatalf("expected query results output to be %s, got %s: diff: %s", expectedOut, gotOut, diff)
}
stateMgr, _ := b.StateMgr(testBackendSingleWorkspaceName)
// An error suggests that the state was not unlocked after the operation finished
if _, err := stateMgr.Lock(statemgr.NewLockInfo()); err != nil {
t.Fatalf("unexpected error locking state after successful plan: %s", err.Error())
}
}