Skip to content

Commit 203e6cc

Browse files
authored
query: refactor query test step logic into testStepNewQuery() (#568)
* Add test for list validation diagnostics * Move query error handling to `RunQueryChecks()` * Remove query error filtering from `wd.Query()` * Refactor query check logic into `testStepNewQuery()` * Add copyright headers
1 parent d1435da commit 203e6cc

File tree

8 files changed

+203
-32
lines changed

8 files changed

+203
-32
lines changed

helper/resource/query/examplecloud_list_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package query_test
66
import (
77
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
88
"github.com/hashicorp/terraform-plugin-go/tftypes"
9+
910
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider"
1011
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/list"
1112
"github.com/hashicorp/terraform-plugin-testing/internal/teststep"

helper/resource/query/query_test.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
package query_test
55

66
import (
7+
"regexp"
78
"testing"
89

910
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
11+
"github.com/hashicorp/terraform-plugin-go/tftypes"
12+
1013
r "github.com/hashicorp/terraform-plugin-testing/helper/resource"
1114
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider"
15+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/list"
1216
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/providerserver"
1317
"github.com/hashicorp/terraform-plugin-testing/knownvalue"
1418
"github.com/hashicorp/terraform-plugin-testing/querycheck"
@@ -100,3 +104,88 @@ func TestQuery(t *testing.T) {
100104
},
101105
})
102106
}
107+
108+
func TestQuery_ExpectError_ValidationError(t *testing.T) {
109+
t.Parallel()
110+
111+
r.UnitTest(t, r.TestCase{
112+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
113+
tfversion.SkipBelow(tfversion.Version1_14_0),
114+
},
115+
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
116+
"examplecloud": providerserver.NewProviderServer(testprovider.Provider{
117+
ListResources: map[string]testprovider.ListResource{
118+
"examplecloud_containerette": {
119+
IncludeResource: true,
120+
SchemaResponse: &list.SchemaResponse{
121+
Schema: &tfprotov6.Schema{
122+
Block: &tfprotov6.SchemaBlock{
123+
Attributes: []*tfprotov6.SchemaAttribute{
124+
{
125+
Name: "resource_group_name",
126+
Type: tftypes.String,
127+
Required: true,
128+
},
129+
},
130+
},
131+
},
132+
},
133+
ValidateListConfigResponse: &list.ValidateListConfigResponse{
134+
Diagnostics: []*tfprotov6.Diagnostic{
135+
{
136+
Severity: tfprotov6.DiagnosticSeverityError,
137+
Summary: "Diagnostic summary",
138+
Detail: "Diagnostic details",
139+
},
140+
},
141+
},
142+
},
143+
},
144+
Resources: map[string]testprovider.Resource{
145+
"examplecloud_containerette": examplecloudResource(),
146+
},
147+
}),
148+
},
149+
Steps: []r.TestStep{
150+
{ // config mode step 1 needs tf file with terraform providers block
151+
// this step should provision all the resources that the query is support to list
152+
// for simplicity we're only "provisioning" one here
153+
Config: `
154+
resource "examplecloud_containerette" "primary" {
155+
name = "banana"
156+
resource_group_name = "foo"
157+
location = "westeurope"
158+
159+
instances = 5
160+
}`,
161+
},
162+
{ // Query mode step 2, operates on .tfquery.hcl files (needs tf file with terraform providers block)
163+
// ```provider "examplecloud" {}``` has a slightly different syntax for a .tfquery.hcl file
164+
// provider bock simulates a real providers workflow
165+
// "config" in this case means configuration of the list resource/filters
166+
167+
Query: true,
168+
Config: `
169+
provider "examplecloud" {}
170+
171+
list "examplecloud_containerette" "test" {
172+
provider = examplecloud
173+
174+
config {
175+
resource_group_name = "foo"
176+
}
177+
}
178+
179+
list "examplecloud_containerette" "test2" {
180+
provider = examplecloud
181+
182+
config {
183+
resource_group_name = "bar"
184+
}
185+
}
186+
`,
187+
ExpectError: regexp.MustCompile(`Diagnostic summary`),
188+
},
189+
},
190+
})
191+
}

helper/resource/testing_new.go

Lines changed: 1 addition & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
"github.com/google/go-cmp/cmp"
1616
"github.com/hashicorp/go-version"
1717
tfjson "github.com/hashicorp/terraform-json"
18-
"github.com/hashicorp/terraform-plugin-testing/helper/resource/query"
1918
"github.com/mitchellh/go-testing-interface"
2019

2120
"github.com/hashicorp/terraform-plugin-testing/config"
@@ -362,33 +361,7 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
362361
if step.Query {
363362
logging.HelperResourceTrace(ctx, "TestStep is Query mode")
364363

365-
queryConfigRequest := teststep.ConfigurationRequest{
366-
Raw: &step.Config,
367-
}
368-
err := wd.SetQuery(ctx, teststep.Configuration(queryConfigRequest), step.ConfigVariables)
369-
if err != nil {
370-
t.Fatalf("Step %d/%d error setting query: %s", stepNumber, len(c.Steps), err)
371-
}
372-
373-
err = runProviderCommand(ctx, t, wd, providers, func() error {
374-
return wd.Init(ctx)
375-
})
376-
if err != nil {
377-
t.Fatalf("Step %d/%d error running init: %s", stepNumber, len(c.Steps), err)
378-
}
379-
380-
var queryOut []tfjson.LogMsg
381-
err = runProviderCommand(ctx, t, wd, providers, func() error {
382-
var err error
383-
queryOut, err = wd.Query(ctx)
384-
return err
385-
})
386-
if err != nil {
387-
fmt.Printf("Step %d/%d Query Output:\n%s\n", stepNumber, len(c.Steps), queryOut)
388-
t.Fatalf("Step %d/%d error running query: %s", stepNumber, len(c.Steps), err)
389-
}
390-
391-
err = query.RunQueryChecks(ctx, t, queryOut, step.QueryResultChecks)
364+
err := testStepNewQuery(ctx, t, wd, step, providers)
392365

393366
if step.ExpectError != nil {
394367
logging.HelperResourceDebug(ctx, "Checking TestStep ExpectError")
@@ -414,7 +387,6 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
414387
)
415388
t.Fatalf("Step %d/%d error running query checks: %s", stepNumber, len(c.Steps), err)
416389
}
417-
fmt.Printf("Step %d/%d Query Output:\n%s\n", stepNumber, len(c.Steps), queryOut)
418390
}
419391

420392
logging.HelperResourceDebug(ctx, "Finished TestStep")
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package resource
5+
6+
import (
7+
"context"
8+
"fmt"
9+
10+
tfjson "github.com/hashicorp/terraform-json"
11+
"github.com/mitchellh/go-testing-interface"
12+
13+
"github.com/hashicorp/terraform-plugin-testing/helper/resource/query"
14+
"github.com/hashicorp/terraform-plugin-testing/internal/plugintest"
15+
"github.com/hashicorp/terraform-plugin-testing/internal/teststep"
16+
)
17+
18+
func testStepNewQuery(ctx context.Context, t testing.T, wd *plugintest.WorkingDir, step TestStep, providers *providerFactories) error {
19+
t.Helper()
20+
21+
queryConfigRequest := teststep.ConfigurationRequest{
22+
Raw: &step.Config,
23+
}
24+
err := wd.SetQuery(ctx, teststep.Configuration(queryConfigRequest), step.ConfigVariables)
25+
if err != nil {
26+
return fmt.Errorf("Error setting query config: %w", err)
27+
}
28+
29+
err = runProviderCommand(ctx, t, wd, providers, func() error {
30+
return wd.Init(ctx)
31+
})
32+
if err != nil {
33+
t.Fatalf("Error getting init: %s", err)
34+
}
35+
36+
var queryOut []tfjson.LogMsg
37+
err = runProviderCommand(ctx, t, wd, providers, func() error {
38+
var err error
39+
queryOut, err = wd.Query(ctx)
40+
return err
41+
})
42+
if err != nil {
43+
return err
44+
}
45+
46+
return query.RunQueryChecks(ctx, t, queryOut, step.QueryResultChecks)
47+
}

internal/plugintest/working_dir.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ package plugintest
66
import (
77
"context"
88
"fmt"
9-
"github.com/hashicorp/terraform-exec/tfexec"
10-
tfjson "github.com/hashicorp/terraform-json"
119
"io"
1210
"os"
1311
"path/filepath"
1412

13+
"github.com/hashicorp/terraform-exec/tfexec"
14+
tfjson "github.com/hashicorp/terraform-json"
15+
1516
"github.com/hashicorp/terraform-plugin-testing/config"
1617
"github.com/hashicorp/terraform-plugin-testing/internal/logging"
1718
"github.com/hashicorp/terraform-plugin-testing/internal/teststep"

internal/testing/testprovider/list_resource.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ type ListResource struct {
1818
ValidateListConfigResponse *list.ValidateListConfigResponse
1919
}
2020

21+
func (r ListResource) ValidateListConfig(ctx context.Context, req list.ValidateListConfigRequest, resp *list.ValidateListConfigResponse) {
22+
if r.ValidateListConfigResponse != nil {
23+
resp.Diagnostics = r.ValidateListConfigResponse.Diagnostics
24+
}
25+
}
26+
2127
func (r ListResource) Schema(ctx context.Context, req list.SchemaRequest, resp *list.SchemaResponse) {
2228
if r.SchemaResponse != nil {
2329
resp.Diagnostics = r.SchemaResponse.Diagnostics

internal/testing/testsdk/list/list_resource.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
type ListResource interface {
1515
Schema(context.Context, SchemaRequest, *SchemaResponse)
1616
List(context.Context, ListRequest, *ListResultsStream)
17+
ValidateListConfig(context.Context, ValidateListConfigRequest, *ValidateListConfigResponse)
1718
}
1819

1920
type ListRequest struct {
@@ -45,7 +46,12 @@ type ListResult struct {
4546
Diagnostics []*tfprotov6.Diagnostic
4647
}
4748

49+
type ValidateListConfigRequest struct {
50+
Config tftypes.Value
51+
}
52+
4853
type ValidateListConfigResponse struct {
54+
Diagnostics []*tfprotov6.Diagnostic
4955
}
5056

5157
type SchemaRequest struct{}

internal/testing/testsdk/providerserver/providerserver.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1104,7 +1104,56 @@ func (s ProviderServer) ListResource(ctx context.Context, req *tfprotov6.ListRes
11041104
}
11051105

11061106
func (s ProviderServer) ValidateListResourceConfig(ctx context.Context, req *tfprotov6.ValidateListResourceConfigRequest) (*tfprotov6.ValidateListResourceConfigResponse, error) {
1107-
return &tfprotov6.ValidateListResourceConfigResponse{}, nil
1107+
// Copy over identity if it's supported
1108+
identitySchemaReq := resource.IdentitySchemaRequest{}
1109+
identitySchemaResp := &resource.IdentitySchemaResponse{}
1110+
1111+
r, err := ProviderResource(s.Provider, req.TypeName)
1112+
if err != nil {
1113+
return nil, fmt.Errorf("failed to retrieve resource: %v", err)
1114+
}
1115+
r.IdentitySchema(ctx, identitySchemaReq, identitySchemaResp)
1116+
if len(identitySchemaResp.Diagnostics) > 0 {
1117+
return nil, fmt.Errorf("failed to retrieve resource schema: %v", identitySchemaResp.Diagnostics)
1118+
}
1119+
1120+
listresource, diag := ProviderListResource(s.Provider, req.TypeName)
1121+
if diag != nil {
1122+
return nil, fmt.Errorf("failed to retrieve resource identity schema: %v", err)
1123+
}
1124+
1125+
configSchemaReq := list.SchemaRequest{}
1126+
configSchemaResp := &list.SchemaResponse{}
1127+
1128+
listresource.Schema(ctx, configSchemaReq, configSchemaResp)
1129+
if len(configSchemaResp.Diagnostics) > 0 {
1130+
return nil, fmt.Errorf("failed to retrieve resource schema: %v", configSchemaResp.Diagnostics)
1131+
}
1132+
1133+
resourceSchemaResp := &resource.SchemaResponse{}
1134+
r.Schema(ctx, resource.SchemaRequest{}, resourceSchemaResp)
1135+
if resourceSchemaResp.Schema == nil {
1136+
return nil, fmt.Errorf("failed to retrieve resource schema: %v", resourceSchemaResp.Schema)
1137+
}
1138+
1139+
var config tftypes.Value
1140+
config, diag = DynamicValueToValue(configSchemaResp.Schema, req.Config)
1141+
if diag != nil {
1142+
return nil, fmt.Errorf("failed to convert config to value: %v", err)
1143+
}
1144+
1145+
validateReq := list.ValidateListConfigRequest{
1146+
Config: config,
1147+
}
1148+
validateResp := &list.ValidateListConfigResponse{}
1149+
1150+
listresource.ValidateListConfig(ctx, validateReq, validateResp)
1151+
1152+
resp := &tfprotov6.ValidateListResourceConfigResponse{
1153+
Diagnostics: validateResp.Diagnostics,
1154+
}
1155+
1156+
return resp, nil
11081157
}
11091158

11101159
func processListResults(req list.ListRequest, stream iter.Seq[list.ListResult]) iter.Seq[tfprotov6.ListResourceResult] {

0 commit comments

Comments
 (0)