mirror of https://github.com/hashicorp/terraform
Tests for query command outputs (#37343)
parent
92db9b8805
commit
36836fd456
@ -0,0 +1,299 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: BUSL-1.1
|
||||
|
||||
package command
|
||||
|
||||
import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
testing_provider "github.com/hashicorp/terraform/internal/providers/testing"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
directory string
|
||||
expectedOut string
|
||||
expectedErr []string
|
||||
initCode int
|
||||
}{
|
||||
{
|
||||
name: "basic query",
|
||||
directory: "basic",
|
||||
expectedOut: `list.test_instance.example id=test-instance-1 Test Instance 1
|
||||
list.test_instance.example id=test-instance-2 Test Instance 2
|
||||
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "query referencing local variable",
|
||||
directory: "with-locals",
|
||||
expectedOut: `list.test_instance.example id=test-instance-1 Test Instance 1
|
||||
list.test_instance.example id=test-instance-2 Test Instance 2
|
||||
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "config with no query block",
|
||||
directory: "no-list-block",
|
||||
expectedOut: "",
|
||||
expectedErr: []string{`
|
||||
Error: No resources to query
|
||||
|
||||
The configuration does not contain any resources that can be queried.
|
||||
`},
|
||||
},
|
||||
{
|
||||
name: "missing query file",
|
||||
directory: "missing-query-file",
|
||||
expectedOut: "",
|
||||
expectedErr: []string{`
|
||||
Error: No resources to query
|
||||
|
||||
The configuration does not contain any resources that can be queried.
|
||||
`},
|
||||
},
|
||||
{
|
||||
name: "missing configuration",
|
||||
directory: "missing-configuration",
|
||||
expectedOut: "",
|
||||
expectedErr: []string{`
|
||||
Error: No configuration files
|
||||
|
||||
Query requires a query configuration to be present. Create a Terraform query
|
||||
configuration file (.tfquery.hcl file) and try again.
|
||||
`},
|
||||
},
|
||||
{
|
||||
name: "invalid query syntax",
|
||||
directory: "invalid-syntax",
|
||||
expectedOut: "",
|
||||
initCode: 1,
|
||||
expectedErr: []string{`
|
||||
Error: Unsupported block type
|
||||
|
||||
on query.tfquery.hcl line 11:
|
||||
11: resource "test_instance" "example" {
|
||||
|
||||
Blocks of type "resource" are not expected here.
|
||||
`},
|
||||
},
|
||||
}
|
||||
|
||||
for _, ts := range tests {
|
||||
t.Run(ts.name, func(t *testing.T) {
|
||||
td := t.TempDir()
|
||||
testCopyDir(t, testFixturePath(path.Join("query", ts.directory)), td)
|
||||
t.Chdir(td)
|
||||
providerSource, close := newMockProviderSource(t, map[string][]string{
|
||||
"hashicorp/test": {"1.0.0"},
|
||||
})
|
||||
defer close()
|
||||
|
||||
p := queryFixtureProvider()
|
||||
view, done := testView(t)
|
||||
meta := Meta{
|
||||
testingOverrides: metaOverridesForProvider(p),
|
||||
View: view,
|
||||
AllowExperimentalFeatures: true,
|
||||
ProviderSource: providerSource,
|
||||
}
|
||||
|
||||
init := &InitCommand{Meta: meta}
|
||||
code := init.Run(nil)
|
||||
output := done(t)
|
||||
if code != ts.initCode {
|
||||
t.Fatalf("expected status code %d but got %d: %s", ts.initCode, code, output.All())
|
||||
}
|
||||
|
||||
view, done = testView(t)
|
||||
meta.View = view
|
||||
|
||||
c := &QueryCommand{Meta: meta}
|
||||
args := []string{"-no-color"}
|
||||
code = c.Run(args)
|
||||
output = done(t)
|
||||
actual := output.All()
|
||||
if len(ts.expectedErr) == 0 {
|
||||
if code != 0 {
|
||||
t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
|
||||
}
|
||||
|
||||
// Check that we have query output
|
||||
if diff := cmp.Diff(ts.expectedOut, actual); diff != "" {
|
||||
t.Errorf("expected query output to contain %q, \ngot: %q, \ndiff: %s", ts.expectedOut, actual, diff)
|
||||
}
|
||||
|
||||
} else {
|
||||
for _, expected := range ts.expectedErr {
|
||||
if diff := cmp.Diff(expected, actual); diff != "" {
|
||||
t.Errorf("expected error message to contain '%s', \ngot: %s, \ndiff: %s", expected, actual, diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func queryFixtureProvider() *testing_provider.MockProvider {
|
||||
p := testProvider()
|
||||
instanceListSchema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"data": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"config": {
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"ami": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
}
|
||||
databaseListSchema := &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"data": {
|
||||
Type: cty.DynamicPseudoType,
|
||||
Computed: true,
|
||||
},
|
||||
},
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"config": {
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"engine": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Nesting: configschema.NestingSingle,
|
||||
},
|
||||
},
|
||||
}
|
||||
p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"test_instance": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Computed: true,
|
||||
},
|
||||
"ami": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"test_database": {
|
||||
Body: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {
|
||||
Type: cty.String,
|
||||
Computed: true,
|
||||
},
|
||||
"engine": {
|
||||
Type: cty.String,
|
||||
Optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ListResourceTypes: map[string]providers.Schema{
|
||||
"test_instance": {Body: instanceListSchema},
|
||||
"test_database": {Body: databaseListSchema},
|
||||
},
|
||||
}
|
||||
|
||||
// Mock the ListResources method for query operations
|
||||
p.ListResourceFn = func(request providers.ListResourceRequest) providers.ListResourceResponse {
|
||||
// Check the config to determine what kind of response to return
|
||||
wholeConfigMap := request.Config.AsValueMap()
|
||||
|
||||
configMap := wholeConfigMap["config"]
|
||||
|
||||
// For empty results test case //TODO: Remove?
|
||||
if ami, ok := wholeConfigMap["ami"]; ok && ami.AsString() == "ami-nonexistent" {
|
||||
return providers.ListResourceResponse{
|
||||
Result: cty.ObjectVal(map[string]cty.Value{
|
||||
"data": cty.ListVal([]cty.Value{}),
|
||||
"config": configMap,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
switch request.TypeName {
|
||||
case "test_instance":
|
||||
return providers.ListResourceResponse{
|
||||
Result: cty.ObjectVal(map[string]cty.Value{
|
||||
"data": cty.ListVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"identity": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test-instance-1"),
|
||||
}),
|
||||
"state": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test-instance-1"),
|
||||
"ami": cty.StringVal("ami-12345"),
|
||||
}),
|
||||
"display_name": cty.StringVal("Test Instance 1"),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"identity": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test-instance-2"),
|
||||
}),
|
||||
"state": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test-instance-2"),
|
||||
"ami": cty.StringVal("ami-67890"),
|
||||
}),
|
||||
"display_name": cty.StringVal("Test Instance 2"),
|
||||
}),
|
||||
}),
|
||||
"config": configMap,
|
||||
}),
|
||||
}
|
||||
case "test_database":
|
||||
return providers.ListResourceResponse{
|
||||
Result: cty.ObjectVal(map[string]cty.Value{
|
||||
"data": cty.ListVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"identity": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test-db-1"),
|
||||
}),
|
||||
"state": cty.ObjectVal(map[string]cty.Value{
|
||||
"id": cty.StringVal("test-db-1"),
|
||||
"engine": cty.StringVal("mysql"),
|
||||
}),
|
||||
"display_name": cty.StringVal("Test Database 1"),
|
||||
}),
|
||||
}),
|
||||
"config": configMap,
|
||||
}),
|
||||
}
|
||||
default:
|
||||
return providers.ListResourceResponse{
|
||||
Result: cty.ObjectVal(map[string]cty.Value{
|
||||
"data": cty.ListVal([]cty.Value{}),
|
||||
"config": configMap,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "test" {}
|
||||
|
||||
resource "test_instance" "example" {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
list "test_instance" "example" {
|
||||
provider = test
|
||||
|
||||
config {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "test" {}
|
||||
|
||||
resource "test_instance" "example" {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
list "test_instance" "example" {
|
||||
provider = test
|
||||
|
||||
config {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// resource type not supported in query files
|
||||
resource "test_instance" "example" {
|
||||
provider = test
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "test" {}
|
||||
|
||||
resource "test_instance" "example" {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "test" {}
|
||||
|
||||
resource "test_instance" "example" {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
terraform {
|
||||
required_providers {
|
||||
test = {
|
||||
source = "hashicorp/test"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "test" {}
|
||||
|
||||
resource "test_instance" "example" {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
locals {
|
||||
ami = "ami-12345"
|
||||
}
|
||||
|
||||
list "test_instance" "example" {
|
||||
provider = test
|
||||
|
||||
config {
|
||||
ami = local.ami
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue