Skip to content

Commit 7ed2ee2

Browse files
authored
query: add support for ExpectError (#561)
* add support for expect error in query mode * add header
1 parent f652157 commit 7ed2ee2

File tree

3 files changed

+288
-3
lines changed

3 files changed

+288
-3
lines changed

helper/resource/testing_new.go

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,11 +389,36 @@ func runNewTest(ctx context.Context, t testing.T, c TestCase, helper *plugintest
389389
}
390390

391391
err = query.RunQueryChecks(ctx, t, queryOut, step.QueryResultChecks)
392-
if err != nil {
393-
t.Fatalf("Step %d/%d error running query checks: %s", stepNumber, len(c.Steps), err)
392+
393+
if step.ExpectError != nil {
394+
logging.HelperResourceDebug(ctx, "Checking TestStep ExpectError")
395+
if err == nil {
396+
logging.HelperResourceError(ctx, "Error running query: expected an error but got none")
397+
t.Fatalf("Step %d/%d error running query: expected an error but got none", stepNumber, len(c.Steps))
398+
}
399+
if !step.ExpectError.MatchString(err.Error()) {
400+
logging.HelperResourceError(ctx, fmt.Sprintf("Error running query: expected an error with pattern (%s)", step.ExpectError.String()),
401+
map[string]interface{}{logging.KeyError: err},
402+
)
403+
t.Fatalf("Step %d/%d error running query, expected an error with pattern (%s), no match on: %s", stepNumber, len(c.Steps), step.ExpectError.String(), err)
404+
}
405+
} else {
406+
if err != nil && c.ErrorCheck != nil {
407+
logging.HelperResourceDebug(ctx, "Calling TestCase ErrorCheck")
408+
err = c.ErrorCheck(err)
409+
logging.HelperResourceDebug(ctx, "Called TestCase ErrorCheck")
410+
}
411+
if err != nil {
412+
logging.HelperResourceError(ctx, "Error running query",
413+
map[string]interface{}{logging.KeyError: err},
414+
)
415+
t.Fatalf("Step %d/%d error running query checks: %s", stepNumber, len(c.Steps), err)
416+
}
417+
fmt.Printf("Step %d/%d Query Output:\n%s\n", stepNumber, len(c.Steps), queryOut)
394418
}
395419

396-
fmt.Printf("Step %d/%d Query Output:\n%s\n", stepNumber, len(c.Steps), queryOut)
420+
logging.HelperResourceDebug(ctx, "Finished TestStep")
421+
397422
continue
398423
}
399424

File renamed without changes.

querycheck/contains_name_test.go

Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
// SPDX-License-Identifier: MPL-2.0
2+
3+
package querycheck_test
4+
5+
import (
6+
"regexp"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
10+
"github.com/hashicorp/terraform-plugin-go/tftypes"
11+
r "github.com/hashicorp/terraform-plugin-testing/helper/resource"
12+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider"
13+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/list"
14+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/providerserver"
15+
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource"
16+
"github.com/hashicorp/terraform-plugin-testing/internal/teststep"
17+
"github.com/hashicorp/terraform-plugin-testing/querycheck"
18+
"github.com/hashicorp/terraform-plugin-testing/tfversion"
19+
)
20+
21+
func dessertsThatStartWithPResource() testprovider.Resource {
22+
return testprovider.Resource{
23+
CreateResponse: &resource.CreateResponse{
24+
NewState: tftypes.NewValue(
25+
tftypes.Object{
26+
AttributeTypes: map[string]tftypes.Type{
27+
"name": tftypes.String,
28+
},
29+
},
30+
map[string]tftypes.Value{
31+
"name": tftypes.NewValue(tftypes.String, "pie"),
32+
},
33+
),
34+
NewIdentity: teststep.Pointer(tftypes.NewValue(
35+
tftypes.Object{
36+
AttributeTypes: map[string]tftypes.Type{
37+
"name": tftypes.String,
38+
},
39+
},
40+
map[string]tftypes.Value{
41+
"name": tftypes.NewValue(tftypes.String, "pie"),
42+
},
43+
)),
44+
},
45+
ReadResponse: &resource.ReadResponse{
46+
NewState: tftypes.NewValue(
47+
tftypes.Object{
48+
AttributeTypes: map[string]tftypes.Type{
49+
"name": tftypes.String,
50+
},
51+
},
52+
map[string]tftypes.Value{
53+
"name": tftypes.NewValue(tftypes.String, "pie"),
54+
},
55+
),
56+
NewIdentity: teststep.Pointer(tftypes.NewValue(
57+
tftypes.Object{
58+
AttributeTypes: map[string]tftypes.Type{
59+
"name": tftypes.String,
60+
},
61+
},
62+
map[string]tftypes.Value{
63+
"name": tftypes.NewValue(tftypes.String, "pie"),
64+
},
65+
)),
66+
},
67+
ImportStateResponse: &resource.ImportStateResponse{
68+
State: tftypes.NewValue(
69+
tftypes.Object{
70+
AttributeTypes: map[string]tftypes.Type{
71+
"name": tftypes.String,
72+
},
73+
},
74+
map[string]tftypes.Value{
75+
"name": tftypes.NewValue(tftypes.String, "pie"),
76+
},
77+
),
78+
Identity: teststep.Pointer(tftypes.NewValue(
79+
tftypes.Object{
80+
AttributeTypes: map[string]tftypes.Type{
81+
"name": tftypes.String,
82+
},
83+
},
84+
map[string]tftypes.Value{
85+
"name": tftypes.NewValue(tftypes.String, "pie"),
86+
},
87+
)),
88+
},
89+
SchemaResponse: &resource.SchemaResponse{
90+
Schema: &tfprotov6.Schema{
91+
Block: &tfprotov6.SchemaBlock{
92+
Attributes: []*tfprotov6.SchemaAttribute{
93+
{
94+
Name: "name",
95+
Type: tftypes.String,
96+
Required: true,
97+
},
98+
},
99+
},
100+
},
101+
},
102+
IdentitySchemaResponse: &resource.IdentitySchemaResponse{
103+
Schema: &tfprotov6.ResourceIdentitySchema{
104+
Version: 1,
105+
IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{
106+
{
107+
Name: "name",
108+
Type: tftypes.String,
109+
RequiredForImport: true,
110+
},
111+
},
112+
},
113+
},
114+
}
115+
}
116+
117+
func dessertsThatStartWithPListResource() testprovider.ListResource {
118+
return testprovider.ListResource{
119+
IncludeResource: true,
120+
SchemaResponse: &list.SchemaResponse{
121+
Schema: &tfprotov6.Schema{
122+
Block: &tfprotov6.SchemaBlock{
123+
Attributes: []*tfprotov6.SchemaAttribute{
124+
{
125+
Name: "group",
126+
Type: tftypes.String,
127+
Computed: true,
128+
},
129+
},
130+
},
131+
},
132+
},
133+
ListResultsStream: &list.ListResultsStream{
134+
Results: func(push func(list.ListResult) bool) {
135+
push(list.ListResult{
136+
Identity: teststep.Pointer(tftypes.NewValue(
137+
tftypes.Object{
138+
AttributeTypes: map[string]tftypes.Type{
139+
"name": tftypes.String,
140+
},
141+
},
142+
map[string]tftypes.Value{
143+
"name": tftypes.NewValue(tftypes.String, "pie"),
144+
},
145+
)),
146+
DisplayName: "pie",
147+
})
148+
push(list.ListResult{
149+
Identity: teststep.Pointer(tftypes.NewValue(
150+
tftypes.Object{
151+
AttributeTypes: map[string]tftypes.Type{
152+
"name": tftypes.String,
153+
},
154+
},
155+
map[string]tftypes.Value{
156+
"name": tftypes.NewValue(tftypes.String, "pudding"),
157+
},
158+
)),
159+
DisplayName: "pudding",
160+
})
161+
},
162+
},
163+
}
164+
}
165+
166+
func TestContainsResourceWithName(t *testing.T) {
167+
t.Parallel()
168+
169+
r.UnitTest(t, r.TestCase{
170+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
171+
tfversion.SkipBelow(tfversion.Version1_14_0),
172+
},
173+
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
174+
"dessertcloud": providerserver.NewProviderServer(testprovider.Provider{
175+
ListResources: map[string]testprovider.ListResource{
176+
"dessert_letter_p": dessertsThatStartWithPListResource(),
177+
},
178+
Resources: map[string]testprovider.Resource{
179+
"dessert_letter_p": dessertsThatStartWithPResource(),
180+
},
181+
}),
182+
},
183+
Steps: []r.TestStep{
184+
{
185+
Query: true,
186+
Config: `
187+
provider "dessertcloud" {}
188+
189+
list "dessert_letter_p" "test" {
190+
provider = dessertcloud
191+
192+
config {
193+
group = "foo"
194+
}
195+
}
196+
197+
list "dessert_letter_p" "test2" {
198+
provider = dessertcloud
199+
200+
config {
201+
group = "bar"
202+
}
203+
}
204+
`,
205+
QueryResultChecks: []querycheck.QueryResultCheck{
206+
querycheck.ContainsResourceWithName("dessert_letter_p.test", "pie"),
207+
querycheck.ContainsResourceWithName("dessert_letter_p.test", "pudding"),
208+
},
209+
},
210+
},
211+
})
212+
}
213+
214+
func TestContainsResourceWithName_NotFound(t *testing.T) {
215+
t.Parallel()
216+
217+
r.UnitTest(t, r.TestCase{
218+
TerraformVersionChecks: []tfversion.TerraformVersionCheck{
219+
tfversion.SkipBelow(tfversion.Version1_14_0),
220+
},
221+
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
222+
"dessertcloud": providerserver.NewProviderServer(testprovider.Provider{
223+
ListResources: map[string]testprovider.ListResource{
224+
"dessert_letter_p": dessertsThatStartWithPListResource(),
225+
},
226+
Resources: map[string]testprovider.Resource{
227+
"dessert_letter_p": dessertsThatStartWithPResource(),
228+
},
229+
}),
230+
},
231+
Steps: []r.TestStep{
232+
{
233+
Query: true,
234+
Config: `
235+
provider "dessertcloud" {}
236+
237+
list "dessert_letter_p" "test" {
238+
provider = dessertcloud
239+
240+
config {
241+
group = "foo"
242+
}
243+
}
244+
245+
list "dessert_letter_p" "test2" {
246+
provider = dessertcloud
247+
248+
config {
249+
group = "bar"
250+
}
251+
}
252+
`,
253+
QueryResultChecks: []querycheck.QueryResultCheck{
254+
querycheck.ContainsResourceWithName("dessert_letter_p.test", "pavlova"),
255+
},
256+
ExpectError: regexp.MustCompile("expected to find resource with display name \"pavlova\" in results but resource was not found"),
257+
},
258+
},
259+
})
260+
}

0 commit comments

Comments
 (0)