From 0e366d5b4662ce437a82e70b642cc9280b34a9eb Mon Sep 17 00:00:00 2001 From: Steph Date: Tue, 10 Feb 2026 15:16:23 +0100 Subject: [PATCH 1/6] add expect exact length with regex query check --- .../regex_expect_result_length_exact.go | 54 ++++++ .../regex_expect_result_length_exact_test.go | 171 ++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 querycheck/regex_expect_result_length_exact.go create mode 100644 querycheck/regex_expect_result_length_exact_test.go diff --git a/querycheck/regex_expect_result_length_exact.go b/querycheck/regex_expect_result_length_exact.go new file mode 100644 index 00000000..e2d9d276 --- /dev/null +++ b/querycheck/regex_expect_result_length_exact.go @@ -0,0 +1,54 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package querycheck + +import ( + "context" + "fmt" + "regexp" +) + +var _ QueryResultCheck = regexExpectLength{} + +type regexExpectLength struct { + regex *regexp.Regexp + check int +} + +// CheckQuery implements the query check logic. +func (e regexExpectLength) CheckQuery(_ context.Context, req CheckQueryRequest, resp *CheckQueryResponse) { + if req.QuerySummary == nil && req.QuerySummaries == nil { + resp.Error = fmt.Errorf("no query summary information available") + return + } + + total := 0 + for _, summary := range req.QuerySummaries { + matches, err := regexp.MatchString(e.regex.String(), summary.Address) + if err != nil { + resp.Error = fmt.Errorf("invalid regex pattern provided: %s, error: %s", e.regex.String(), err) + return + } + + if matches { + total += summary.Total + } + } + + if total == e.check { + return + } + + resp.Error = fmt.Errorf("number of found resources matching regex %s - expected %v but got %v.", e.regex.String(), e.check, total) +} + +// ExpectLength returns a query check that asserts that the length of the query result is exactly the given value. +// +// This query check can only be used with managed resources that support query. Query is only supported in Terraform v1.14+ +func RegexExpectLength(regex *regexp.Regexp, length int) QueryResultCheck { + return regexExpectLength{ + regex: regex, + check: length, + } +} diff --git a/querycheck/regex_expect_result_length_exact_test.go b/querycheck/regex_expect_result_length_exact_test.go new file mode 100644 index 00000000..452eb1f6 --- /dev/null +++ b/querycheck/regex_expect_result_length_exact_test.go @@ -0,0 +1,171 @@ +// Copyright IBM Corp. 2014, 2026 +// SPDX-License-Identifier: MPL-2.0 + +package querycheck_test + +import ( + "regexp" + "testing" + + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + r "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/providerserver" + "github.com/hashicorp/terraform-plugin-testing/querycheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestResultLengthExactRegex_Multiple(t *testing.T) { + t.Parallel() + + r.UnitTest(t, r.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ + ListResources: map[string]testprovider.ListResource{ + "examplecloud_containerette": examplecloudListResource(), + "examplecloud_bananette": examplecloudListResourceBananette(), + }, + Resources: map[string]testprovider.Resource{ + "examplecloud_containerette": examplecloudResource(), + "examplecloud_bananette": examplecloudResourceBananette(), + }, + }), + }, + Steps: []r.TestStep{ + { // config mode step 1 needs tf file with terraform providers block + // this step should provision all the resources that the query is support to list + // for simplicity we're only "provisioning" one here + Config: ` + resource "examplecloud_containerette" "primary" { + name = "banana" + resource_group_name = "foo" + location = "westeurope" + + instances = 5 + }`, + }, + { + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_bananette" "test" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_containerette" "test3" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.RegexExpectLength(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 12), + }, + }, + }, + }) +} + +func TestResultLengthExactRegex_WrongAmount(t *testing.T) { + t.Parallel() + + r.UnitTest(t, r.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ + ListResources: map[string]testprovider.ListResource{ + "examplecloud_containerette": examplecloudListResource(), + "examplecloud_bananette": examplecloudListResourceBananette(), + }, + Resources: map[string]testprovider.Resource{ + "examplecloud_containerette": examplecloudResource(), + "examplecloud_bananette": examplecloudResourceBananette(), + }, + }), + }, + Steps: []r.TestStep{ + { // config mode step 1 needs tf file with terraform providers block + // this step should provision all the resources that the query is support to list + // for simplicity we're only "provisioning" one here + Config: ` + resource "examplecloud_containerette" "primary" { + name = "banana" + resource_group_name = "foo" + location = "westeurope" + + instances = 5 + }`, + }, + { + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_bananette" "test" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_containerette" "test3" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.RegexExpectLength(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 10), + }, + ExpectError: regexp.MustCompile("number of found resources matching regex .* - expected 10 but got 12."), + }, + }, + }) +} From 699487c374c44e03dac688f6323d3bd8d43fa4a7 Mon Sep 17 00:00:00 2001 From: Steph Date: Tue, 10 Feb 2026 15:51:38 +0100 Subject: [PATCH 2/6] apply ExpectLength fix to ExpectLengthAtLeast query check --- querycheck/expect_result_length_atleast.go | 25 +++- .../expect_result_length_atleast_test.go | 110 ++++++++++++++++-- 2 files changed, 124 insertions(+), 11 deletions(-) diff --git a/querycheck/expect_result_length_atleast.go b/querycheck/expect_result_length_atleast.go index 9a0cd30c..9bcd217d 100644 --- a/querycheck/expect_result_length_atleast.go +++ b/querycheck/expect_result_length_atleast.go @@ -6,6 +6,7 @@ package querycheck import ( "context" "fmt" + "strings" ) var _ QueryResultCheck = expectLengthAtLeast{} @@ -17,15 +18,31 @@ type expectLengthAtLeast struct { // CheckQuery implements the query check logic. func (e expectLengthAtLeast) CheckQuery(_ context.Context, req CheckQueryRequest, resp *CheckQueryResponse) { - if req.QuerySummary == nil { + if req.QuerySummary == nil && len(req.QuerySummaries) == 0 { resp.Error = fmt.Errorf("no completed query information available") return } - if req.QuerySummary.Total < e.check { - resp.Error = fmt.Errorf("Query result of at least length %v - expected but got %v.", e.check, req.QuerySummary.Total) - return + for _, summary := range req.QuerySummaries { + address := summary.Address + + // this brings the behaviour of this check in-line with the other query checks where the resource + // address needs to be provided without the `list.` prefix, but maintains the previous behaviour + // to not break existing tests that may be using the `list.` prefix in the resource address + if !strings.HasPrefix(e.resourceAddress, "list.") { + address = strings.TrimPrefix(summary.Address, "list.") + } + + if strings.EqualFold(address, e.resourceAddress) { + if summary.Total < e.check { + resp.Error = fmt.Errorf("Query result of at least length %v - expected but got %v.", e.check, summary.Total) + return + } + return + } } + + resp.Error = fmt.Errorf("the list block %s was not found in the query results", e.resourceAddress) } // ExpectLengthAtLeast returns a query check that asserts that the length of the query result is at least the given value. diff --git a/querycheck/expect_result_length_atleast_test.go b/querycheck/expect_result_length_atleast_test.go index f77f8701..b957a6b9 100644 --- a/querycheck/expect_result_length_atleast_test.go +++ b/querycheck/expect_result_length_atleast_test.go @@ -56,7 +56,59 @@ func TestResultLengthAtLeast(t *testing.T) { resource_group_name = "foo" } } - list "examplecloud_containerette" "test2" { + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 2), + }, + }, + }, + }) +} + +func TestResultLengthAtLeast_Multiple(t *testing.T) { + t.Parallel() + + r.UnitTest(t, r.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ + ListResources: map[string]testprovider.ListResource{ + "examplecloud_containerette": examplecloudListResource(), + "examplecloud_bananette": examplecloudListResource(), + }, + Resources: map[string]testprovider.Resource{ + "examplecloud_containerette": examplecloudResource(), + "examplecloud_bananette": examplecloudResourceBananette(), + }, + }), + }, + Steps: []r.TestStep{ + { // config mode step 1 needs tf file with terraform providers block + // this step should provision all the resources that the query is support to list + // for simplicity we're only "provisioning" one here + Config: ` + resource "examplecloud_containerette" "primary" { + name = "banana" + resource_group_name = "foo" + location = "westeurope" + + instances = 5 + }`, + }, + { + Query: true, + Config: ` + provider "examplecloud" {} + list "examplecloud_containerette" "test" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + list "examplecloud_bananette" "test" { provider = examplecloud config { @@ -65,8 +117,8 @@ func TestResultLengthAtLeast(t *testing.T) { } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 2), - querycheck.ExpectLengthAtLeast("examplecloud_containerette.test2", 1), + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 6), + querycheck.ExpectLengthAtLeast("examplecloud_bananette.test", 2), }, }, }, @@ -114,18 +166,62 @@ func TestResultLengthAtLeast_TooFewResults(t *testing.T) { resource_group_name = "foo" } } - list "examplecloud_containerette" "test2" { + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 8), + }, + ExpectError: regexp.MustCompile("Query result of at least length 8 - expected but got 6."), + }, + }, + }) +} + +func TestResultLengthAtLeast_WrongResourceAddress(t *testing.T) { + t.Parallel() + + r.UnitTest(t, r.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ + ListResources: map[string]testprovider.ListResource{ + "examplecloud_containerette": examplecloudListResource(), + }, + Resources: map[string]testprovider.Resource{ + "examplecloud_containerette": examplecloudResource(), + }, + }), + }, + Steps: []r.TestStep{ + { // config mode step 1 needs tf file with terraform providers block + // this step should provision all the resources that the query is support to list + // for simplicity we're only "provisioning" one here + Config: ` + resource "examplecloud_containerette" "primary" { + name = "banana" + resource_group_name = "foo" + location = "westeurope" + + instances = 5 + }`, + }, + { + Query: true, + Config: ` + provider "examplecloud" {} + list "examplecloud_containerette" "test" { provider = examplecloud config { - resource_group_name = "bar" + resource_group_name = "foo" } } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 8), + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test2", 8), }, - ExpectError: regexp.MustCompile("Query result of at least length 8 - expected but got 6."), + ExpectError: regexp.MustCompile("the list block examplecloud_containerette.test2 was not found in the query results"), }, }, }) From 19faf70b7def253226d3a58ea35d4ba3bce70b7b Mon Sep 17 00:00:00 2001 From: Steph Date: Wed, 11 Feb 2026 10:17:32 +0100 Subject: [PATCH 3/6] rename query check to ExpectLengthForMultiple --- ...xpect_result_length_exact_for_multiple.go} | 22 +++-- ..._result_length_exact_for_multiple_test.go} | 88 +++++++++++++++++-- 2 files changed, 97 insertions(+), 13 deletions(-) rename querycheck/{regex_expect_result_length_exact.go => expect_result_length_exact_for_multiple.go} (51%) rename querycheck/{regex_expect_result_length_exact_test.go => expect_result_length_exact_for_multiple_test.go} (62%) diff --git a/querycheck/regex_expect_result_length_exact.go b/querycheck/expect_result_length_exact_for_multiple.go similarity index 51% rename from querycheck/regex_expect_result_length_exact.go rename to querycheck/expect_result_length_exact_for_multiple.go index e2d9d276..44785582 100644 --- a/querycheck/regex_expect_result_length_exact.go +++ b/querycheck/expect_result_length_exact_for_multiple.go @@ -9,21 +9,22 @@ import ( "regexp" ) -var _ QueryResultCheck = regexExpectLength{} +var _ QueryResultCheck = expectLengthForMultiple{} -type regexExpectLength struct { +type expectLengthForMultiple struct { regex *regexp.Regexp check int } // CheckQuery implements the query check logic. -func (e regexExpectLength) CheckQuery(_ context.Context, req CheckQueryRequest, resp *CheckQueryResponse) { +func (e expectLengthForMultiple) CheckQuery(_ context.Context, req CheckQueryRequest, resp *CheckQueryResponse) { if req.QuerySummary == nil && req.QuerySummaries == nil { resp.Error = fmt.Errorf("no query summary information available") return } total := 0 + matchFound := false for _, summary := range req.QuerySummaries { matches, err := regexp.MatchString(e.regex.String(), summary.Address) if err != nil { @@ -33,21 +34,26 @@ func (e regexExpectLength) CheckQuery(_ context.Context, req CheckQueryRequest, if matches { total += summary.Total + matchFound = true } } - if total == e.check { + if !matchFound { + resp.Error = fmt.Errorf("no list resources matching the provided regex pattern %s were found in the query results", e.regex.String()) return } - resp.Error = fmt.Errorf("number of found resources matching regex %s - expected %v but got %v.", e.regex.String(), e.check, total) + if total != e.check { + resp.Error = fmt.Errorf("number of found resources %v - expected but got %v.", e.check, total) + } } -// ExpectLength returns a query check that asserts that the length of the query result is exactly the given value. +// ExpectLengthForMultiple returns a query check that asserts that the sum of query result lengths +// produced by multiple list blocks is exactly the given value. // // This query check can only be used with managed resources that support query. Query is only supported in Terraform v1.14+ -func RegexExpectLength(regex *regexp.Regexp, length int) QueryResultCheck { - return regexExpectLength{ +func ExpectLengthForMultiple(regex *regexp.Regexp, length int) QueryResultCheck { + return expectLengthForMultiple{ regex: regex, check: length, } diff --git a/querycheck/regex_expect_result_length_exact_test.go b/querycheck/expect_result_length_exact_for_multiple_test.go similarity index 62% rename from querycheck/regex_expect_result_length_exact_test.go rename to querycheck/expect_result_length_exact_for_multiple_test.go index 452eb1f6..6ac2e250 100644 --- a/querycheck/regex_expect_result_length_exact_test.go +++ b/querycheck/expect_result_length_exact_for_multiple_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func TestResultLengthExactRegex_Multiple(t *testing.T) { +func TestResultLengthExactForMultiple(t *testing.T) { t.Parallel() r.UnitTest(t, r.TestCase{ @@ -85,14 +85,14 @@ func TestResultLengthExactRegex_Multiple(t *testing.T) { } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.RegexExpectLength(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 12), + querycheck.ExpectLengthForMultiple(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 12), }, }, }, }) } -func TestResultLengthExactRegex_WrongAmount(t *testing.T) { +func TestResultLengthExactForMultiple_WrongAmount(t *testing.T) { t.Parallel() r.UnitTest(t, r.TestCase{ @@ -162,9 +162,87 @@ func TestResultLengthExactRegex_WrongAmount(t *testing.T) { } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.RegexExpectLength(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 10), + querycheck.ExpectLengthForMultiple(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 10), }, - ExpectError: regexp.MustCompile("number of found resources matching regex .* - expected 10 but got 12."), + ExpectError: regexp.MustCompile("number of found resources 10 - expected but got 12."), + }, + }, + }) +} + +func TestResultLengthExactForMultiple_NoMatches(t *testing.T) { + t.Parallel() + + r.UnitTest(t, r.TestCase{ + TerraformVersionChecks: []tfversion.TerraformVersionCheck{ + tfversion.SkipBelow(tfversion.Version1_14_0), + }, + ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ + ListResources: map[string]testprovider.ListResource{ + "examplecloud_containerette": examplecloudListResource(), + "examplecloud_bananette": examplecloudListResourceBananette(), + }, + Resources: map[string]testprovider.Resource{ + "examplecloud_containerette": examplecloudResource(), + "examplecloud_bananette": examplecloudResourceBananette(), + }, + }), + }, + Steps: []r.TestStep{ + { // config mode step 1 needs tf file with terraform providers block + // this step should provision all the resources that the query is support to list + // for simplicity we're only "provisioning" one here + Config: ` + resource "examplecloud_containerette" "primary" { + name = "banana" + resource_group_name = "foo" + location = "westeurope" + + instances = 5 + }`, + }, + { + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_bananette" "test" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_containerette" "test3" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthForMultiple(regexp.MustCompile("examplecloud_(.*)ette.test[4-9]"), 10), + }, + ExpectError: regexp.MustCompile("no list resources matching the provided regex pattern .* were found in the query results"), }, }, }) From 17912badc8f977dfca01d31b938d5f4275359d5c Mon Sep 17 00:00:00 2001 From: Steph Date: Wed, 11 Feb 2026 12:30:28 +0100 Subject: [PATCH 4/6] add changelog --- .changes/unreleased/BUG FIXES-20260211-103028.yaml | 5 +++++ .changes/unreleased/BUG FIXES-20260211-122825.yaml | 5 +++++ .changes/unreleased/FEATURES-20260211-122845.yaml | 5 +++++ 3 files changed, 15 insertions(+) create mode 100644 .changes/unreleased/BUG FIXES-20260211-103028.yaml create mode 100644 .changes/unreleased/BUG FIXES-20260211-122825.yaml create mode 100644 .changes/unreleased/FEATURES-20260211-122845.yaml diff --git a/.changes/unreleased/BUG FIXES-20260211-103028.yaml b/.changes/unreleased/BUG FIXES-20260211-103028.yaml new file mode 100644 index 00000000..3e0a81cd --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20260211-103028.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'querycheck: Fix behaviour of `ExpectLength` querycheck to match on the provided resource address before comparing the count' +time: 2026-02-11T10:30:28.750109+01:00 +custom: + Issue: "604" diff --git a/.changes/unreleased/BUG FIXES-20260211-122825.yaml b/.changes/unreleased/BUG FIXES-20260211-122825.yaml new file mode 100644 index 00000000..267f17c6 --- /dev/null +++ b/.changes/unreleased/BUG FIXES-20260211-122825.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: 'querycheck: Fix behaviour of `ExpectLengthAtLeast` querycheck to match on the provided resource address before comparing the count' +time: 2026-02-11T12:28:25.372146+01:00 +custom: + Issue: "607" diff --git a/.changes/unreleased/FEATURES-20260211-122845.yaml b/.changes/unreleased/FEATURES-20260211-122845.yaml new file mode 100644 index 00000000..fc360c29 --- /dev/null +++ b/.changes/unreleased/FEATURES-20260211-122845.yaml @@ -0,0 +1,5 @@ +kind: FEATURES +body: 'querycheck: Add new `ExpectLengthForMultiple` querycheck for validating the number of found resources for multiple list blocks' +time: 2026-02-11T12:28:45.926523+01:00 +custom: + Issue: "607" From 273cddb9945f5f9c324788d333f1e530cf29ce99 Mon Sep 17 00:00:00 2001 From: Steph Date: Tue, 17 Feb 2026 08:19:42 +0100 Subject: [PATCH 5/6] address review comments --- ...expect_result_length_exact_for_multiple.go | 24 +++++++------------ ...t_result_length_exact_for_multiple_test.go | 6 ++--- 2 files changed, 12 insertions(+), 18 deletions(-) diff --git a/querycheck/expect_result_length_exact_for_multiple.go b/querycheck/expect_result_length_exact_for_multiple.go index 44785582..94af7637 100644 --- a/querycheck/expect_result_length_exact_for_multiple.go +++ b/querycheck/expect_result_length_exact_for_multiple.go @@ -9,16 +9,16 @@ import ( "regexp" ) -var _ QueryResultCheck = expectLengthForMultiple{} +var _ QueryResultCheck = expectTotalLengthForMatching{} -type expectLengthForMultiple struct { +type expectTotalLengthForMatching struct { regex *regexp.Regexp check int } // CheckQuery implements the query check logic. -func (e expectLengthForMultiple) CheckQuery(_ context.Context, req CheckQueryRequest, resp *CheckQueryResponse) { - if req.QuerySummary == nil && req.QuerySummaries == nil { +func (e expectTotalLengthForMatching) CheckQuery(_ context.Context, req CheckQueryRequest, resp *CheckQueryResponse) { + if req.QuerySummary == nil && len(req.QuerySummaries) == 0 { resp.Error = fmt.Errorf("no query summary information available") return } @@ -26,13 +26,7 @@ func (e expectLengthForMultiple) CheckQuery(_ context.Context, req CheckQueryReq total := 0 matchFound := false for _, summary := range req.QuerySummaries { - matches, err := regexp.MatchString(e.regex.String(), summary.Address) - if err != nil { - resp.Error = fmt.Errorf("invalid regex pattern provided: %s, error: %s", e.regex.String(), err) - return - } - - if matches { + if e.regex.MatchString(summary.Address) { total += summary.Total matchFound = true } @@ -44,16 +38,16 @@ func (e expectLengthForMultiple) CheckQuery(_ context.Context, req CheckQueryReq } if total != e.check { - resp.Error = fmt.Errorf("number of found resources %v - expected but got %v.", e.check, total) + resp.Error = fmt.Errorf("expected total of found resources to be %d, got %d", e.check, total) } } -// ExpectLengthForMultiple returns a query check that asserts that the sum of query result lengths +// ExpectTotalLengthForMatching returns a query check that asserts that the sum of query result lengths // produced by multiple list blocks is exactly the given value. // // This query check can only be used with managed resources that support query. Query is only supported in Terraform v1.14+ -func ExpectLengthForMultiple(regex *regexp.Regexp, length int) QueryResultCheck { - return expectLengthForMultiple{ +func ExpectTotalLengthForMatching(regex *regexp.Regexp, length int) QueryResultCheck { + return expectTotalLengthForMatching{ regex: regex, check: length, } diff --git a/querycheck/expect_result_length_exact_for_multiple_test.go b/querycheck/expect_result_length_exact_for_multiple_test.go index 6ac2e250..4abec9aa 100644 --- a/querycheck/expect_result_length_exact_for_multiple_test.go +++ b/querycheck/expect_result_length_exact_for_multiple_test.go @@ -85,7 +85,7 @@ func TestResultLengthExactForMultiple(t *testing.T) { } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLengthForMultiple(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 12), + querycheck.ExpectTotalLengthForMatching(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 12), }, }, }, @@ -162,7 +162,7 @@ func TestResultLengthExactForMultiple_WrongAmount(t *testing.T) { } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLengthForMultiple(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 10), + querycheck.ExpectTotalLengthForMatching(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 10), }, ExpectError: regexp.MustCompile("number of found resources 10 - expected but got 12."), }, @@ -240,7 +240,7 @@ func TestResultLengthExactForMultiple_NoMatches(t *testing.T) { } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLengthForMultiple(regexp.MustCompile("examplecloud_(.*)ette.test[4-9]"), 10), + querycheck.ExpectTotalLengthForMatching(regexp.MustCompile("examplecloud_(.*)ette.test[4-9]"), 10), }, ExpectError: regexp.MustCompile("no list resources matching the provided regex pattern .* were found in the query results"), }, From fad7e9028e27ec8215195c43296f44194322024b Mon Sep 17 00:00:00 2001 From: Steph Date: Tue, 17 Feb 2026 08:31:28 +0100 Subject: [PATCH 6/6] rename tests and files --- ...iple.go => expect_total_result_length_for_matching.go} | 0 ...go => expect_total_result_length_for_matching_test.go} | 8 ++++---- 2 files changed, 4 insertions(+), 4 deletions(-) rename querycheck/{expect_result_length_exact_for_multiple.go => expect_total_result_length_for_matching.go} (100%) rename querycheck/{expect_result_length_exact_for_multiple_test.go => expect_total_result_length_for_matching_test.go} (96%) diff --git a/querycheck/expect_result_length_exact_for_multiple.go b/querycheck/expect_total_result_length_for_matching.go similarity index 100% rename from querycheck/expect_result_length_exact_for_multiple.go rename to querycheck/expect_total_result_length_for_matching.go diff --git a/querycheck/expect_result_length_exact_for_multiple_test.go b/querycheck/expect_total_result_length_for_matching_test.go similarity index 96% rename from querycheck/expect_result_length_exact_for_multiple_test.go rename to querycheck/expect_total_result_length_for_matching_test.go index 4abec9aa..b959851c 100644 --- a/querycheck/expect_result_length_exact_for_multiple_test.go +++ b/querycheck/expect_total_result_length_for_matching_test.go @@ -15,7 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func TestResultLengthExactForMultiple(t *testing.T) { +func TestResultTotalLengthForMatching(t *testing.T) { t.Parallel() r.UnitTest(t, r.TestCase{ @@ -92,7 +92,7 @@ func TestResultLengthExactForMultiple(t *testing.T) { }) } -func TestResultLengthExactForMultiple_WrongAmount(t *testing.T) { +func TestResultTotalLengthForMatching_WrongAmount(t *testing.T) { t.Parallel() r.UnitTest(t, r.TestCase{ @@ -164,13 +164,13 @@ func TestResultLengthExactForMultiple_WrongAmount(t *testing.T) { QueryResultChecks: []querycheck.QueryResultCheck{ querycheck.ExpectTotalLengthForMatching(regexp.MustCompile("examplecloud_(.*)ette.test[1-9]"), 10), }, - ExpectError: regexp.MustCompile("number of found resources 10 - expected but got 12."), + ExpectError: regexp.MustCompile("expected total of found resources to be 10, got 12"), }, }, }) } -func TestResultLengthExactForMultiple_NoMatches(t *testing.T) { +func TestResultTotalLengthForMatching_NoMatches(t *testing.T) { t.Parallel() r.UnitTest(t, r.TestCase{