diff --git a/content/terraform-plugin-testing/v1.14.x/data/plugin-testing-nav-data.json b/content/terraform-plugin-testing/v1.14.x/data/plugin-testing-nav-data.json index 0cb8e53de8..6768edb969 100644 --- a/content/terraform-plugin-testing/v1.14.x/data/plugin-testing-nav-data.json +++ b/content/terraform-plugin-testing/v1.14.x/data/plugin-testing-nav-data.json @@ -25,7 +25,10 @@ }, { "title": "Import mode", "path": "acceptance-tests/import-mode" - }] + }, { + "title": "Query mode", + "path": "acceptance-tests/query-mode" + }] }, { "title": "Terraform Version Checks", @@ -73,6 +76,23 @@ } ] }, + { + "title": "Query Checks", + "routes": [ + { + "title": "Overview", + "path": "acceptance-tests/query-checks" + }, + { + "title": "Query Result Checks", + "path": "acceptance-tests/query-checks/query-result" + }, + { + "title": "Custom Query Checks", + "path": "acceptance-tests/query-checks/custom" + } + ] + }, { "title": "Known Value Checks", "routes": [ diff --git a/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/custom.mdx b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/custom.mdx new file mode 100644 index 0000000000..8db67d3cdb --- /dev/null +++ b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/custom.mdx @@ -0,0 +1,95 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Query Checks' +description: >- + Query Checks are test assertions that can inspect the query results during a TestStep. Custom Query Checks can be implemented. +--- + +# Custom Query Checks + +The package [`querycheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck) also provides the [`QueryResultCheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#QueryResultCheck) interface, which can be implemented for a custom query check. + +The [`querycheck.CheckQueryRequest`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#CheckQueryResultRequest) contains the current query results, parsed by the [terraform-json package](https://pkg.go.dev/github.com/hashicorp/terraform-json#ListResourceFoundData). + +Here is an example implementation of a query check that asserts that a specific query result resource object attribute has a known type and value: + +```go +package example_test + +import ( + "context" + "fmt" + + tfjson "github.com/hashicorp/terraform-json" + + "github.com/hashicorp/terraform-plugin-testing/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/querycheck" + "github.com/hashicorp/terraform-plugin-testing/querycheck/queryfilter" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" +) + +var _ querycheck.QueryResultCheck = expectKnownValue{} +var _ querycheck.QueryResultCheckWithFilters = expectKnownValue{} + +type expectKnownValue struct { + listResourceAddress string + filter queryfilter.QueryFilter + attributePath tfjsonpath.Path + knownValueCheck knownvalue.Check +} + +func (e expectResourceKnownValues) QueryFilters(_ context.Context) []queryfilter.QueryFilter { + if e.filter == nil { + return []queryfilter.QueryFilter{} + } + + return []queryfilter.QueryFilter{ + e.filter, + } +} + +func (e expectKnownValue) CheckQuery(_ context.Context, req querycheck.CheckQueryRequest, resp *querycheck.CheckQueryResponse) { + listRes := make([]tfjson.ListResourceFoundData, 0) + for _, res := range req.Query { + if e.listResourceAddress == strings.TrimPrefix(res.Address, "list.") { + listRes = append(listRes, res) + } + } + + if len(listRes) == 0 { + resp.Error = fmt.Errorf("%s - no query results found after filtering", e.listResourceAddress) + return + } + + if len(listRes) > 1 { + resp.Error = fmt.Errorf("%s - more than 1 query result found after filtering", e.listResourceAddress) + return + } + + res := listRes[0] + + if res.ResourceObject == nil { + resp.Error = fmt.Errorf("%s - no resource object was returned, ensure `include_resource` has been set to `true` in the list resource config`", e.listResourceAddress) + return + } + + attribute, err := tfjsonpath.Traverse(res.ResourceObject, e.attributePath) + if err != nil { + resp.Error = err + return + } + + if err := e.KnownValue.CheckValue(attribute); err != nil { + resp.Error = fmt.Errorf("error checking value for attribute at path: %s for resource with identity %s, err: %s", e.attributePath, e.filter, err) + return + } +} + +func ExpectKnownValues(listResourceAddress string, filter queryfilter.QueryFilter, attributePath tfjsonpath.Path, knownValueCheck knownvalue.Check) QueryResultCheck { + return expectKnownValues{ + listResourceAddress: listResourceAddress, + filter: filter, + attributePath: attributePath, + knownValueCheck: knownValueCheck, + } +} +``` \ No newline at end of file diff --git a/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/index.mdx b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/index.mdx new file mode 100644 index 0000000000..01a1658fe5 --- /dev/null +++ b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/index.mdx @@ -0,0 +1,18 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Query Checks' +description: >- + Query Checks are test assertions that can inspect the query results during a Query TestStep. The testing module + provides built-in Query Checks for common use-cases, and custom Query Checks can also be implemented. +--- + +# Query Checks +During the **Query** [mode](/terraform/plugin/testing/acceptance-tests/teststep#test-modes) of a `TestStep`, the configuration supplied is used as the content of a `.tfquery.hcl` query file and the testing framework will run `terraform query`. + +The execution of `terraform query` results in a set of query results. + +A **query check** is a test assertion that inspects the query results. Multiple query checks can be run, all assertion errors returned are aggregated, reported as a test failure, and all test cleanup logic is executed. + +Refer to: + +- [Query Result Query Checks](/terraform/plugin/testing/acceptance-tests/query-checks/query-results) for built-in query checks. +- [Custom Query Checks](/terraform/plugin/testing/acceptance-tests/query-checks/custom) for defining bespoke query checks. \ No newline at end of file diff --git a/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/query-result.mdx b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/query-result.mdx new file mode 100644 index 0000000000..cd7672a677 --- /dev/null +++ b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-checks/query-result.mdx @@ -0,0 +1,338 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Query Result Checks' +description: >- + Query Checks are test assertions that can inspect the query results during a TestStep. The testing module + provides built-in Query Checks for common use-cases. +--- + +# Query Result Checks + +The `terraform-plugin-testing` module provides a package [`querycheck`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck) with built-in query checks for common use-cases: + +Check | Description | +|---------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| [`ExpectIdentity`](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#expectidentity-query-check) | Asserts that a specified resource identity object is present in the query result. | +| [`ExpectLengthAtLeast`](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#expectlengthatleast-query-check) | Asserts that the number of query results returned is at least a given value. | +| [`ExpectLengthExact`](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#expectlengthexact-query-check) | Asserts that the number of query results returned is equal to the given value. | +| [`ExpectNoIdentity`](/terraform/plugin/testing/acceptance-tests/state-checks/query-checks/query-result#expectnoidentity-query-check) | Asserts that a specified resource identity object is absent from the query results. | +| [`ExpectResourceDisplayName`](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#expectresourcedisplayname-query-check) | Asserts that a particular query result has the specified display name. | +| [`ExpectResourceKnownValues`](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#expectresourceknownvalues-query-check) | Asserts that a specific query result's resource attribute has a specified value. | + +# Query Filter + +Several built-in query checks can only be applied to a specific query result which is identified by a query filter. The `terraform-plugin-testing` module provides a package [`queryfilter`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck/queryfilter) with built-in query filters for common use-cases: + +- Filtering by Display Name: [`queryfilter.ByDisplayName(displayName)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck/queryfilter#ByDisplayName) is used to filter query results by matching the display name of the resource. +- Filtering by Resource Identity: [`queryfilter.ByResourceIdentity(identity)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck/queryfilter#ByResourceIdentity) is used to filter query results by matching a resource identity object. + +## `ExpectIdentity` Query Check + +The [`querycheck.ExpectIdentity(resourceAddress, identity)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#ExpectIdentity) query check is used to assert that the query results returned by a list resource contain a resource with the specified identity. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/querycheck" +) + +func TestExpectIdentity_QueryCheck(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example config creating three resources for querying + Config: ` + resource "test_resource" "one" {} + resource "test_resource" "two" {} + resource "test_resource" "three" {} + `, + }, + { + // Config for the .tfquery.hcl query file + Config: ` + list "test_resource" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectIdentity( + "test_resource.test", + map[string]knownvalue.Check{"attr_1": "foo", "attr_1": "bar"}, + ), + }, + }, + }, + }) +} +``` + +## `ExpectLengthAtLeast` Query Check + +The [`querycheck.ExpectLengthAtLeast(resourceAddress, length)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#ExpectLengthAtLeast) query check is used to assert that a query returns a minimum number of results. + +This is intended to be used when the exact number of results cannot be guaranteed due to the testing environment. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/querycheck" +) + +func TestExpectIdentity_QueryCheck(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example config creating three resources for querying + Config: ` + resource "test_resource" "one" {} + resource "test_resource" "two" {} + resource "test_resource" "three" {} + `, + }, + { + // Config for the .tfquery.hcl query file + Config: ` + list "test_resource" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthAtLeast("test_resource.test", 3), + }, + }, + }, + }) +} +``` + +## `ExpectLengthExact` Query Check + +The [`querycheck.ExpectLengthExact(resourceAddress, length)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#ExpectLength) query check is used to assert that a query returns a specific number of results. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/querycheck" +) + +func TestExpectIdentity_QueryCheck(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example config creating three resources for querying + Config: ` + resource "test_resource" "one" {} + resource "test_resource" "two" {} + resource "test_resource" "three" {} + `, + }, + { + // Config for the .tfquery.hcl query file + Config: ` + list "test_resource" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("test_resource.test", 3), + }, + }, + }, + }) +} +``` + +## `ExpectNoIdentity` Query Check + +The [`querycheck.ExpectNoIdentity(resourceAddress, identity)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#ExpectNoIdentity) query check is used to assert that the query results returned by a list resource do not contain a resource with the specified identity. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/querycheck" +) + +func TestExpectIdentity_QueryCheck(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example config creating three resources for querying + Config: ` + resource "test_resource" "one" {} + resource "test_resource" "two" {} + resource "test_resource" "three" {} + `, + }, + { + // Config for the .tfquery.hcl query file + Config: ` + list "test_resource" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectNoIdentity( + "test_resource.test", + map[string]knownvalue.Check{"attr_1": "foo", "attr_1": "bar"} + ), + }, + }, + }, + }) +} +``` + +## `ExpectResourceDisplayName` Query Check + +The [`querycheck.ExpectResourceDisplayName(resourceAddress, filter, displayName)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#ExpectResourceDisplayName) query check is used to assert that a particular query result which is returned by a query filter has a given display name. + +This check requires a [query filter](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#query-filter) to identify the query result on which the assertion should be performed. If the query filter returns zero or multiple results, the check will fail. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/querycheck" +) + +func TestExpectIdentity_QueryCheck(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example config creating three resources for querying + Config: ` + resource "test_resource" "one" {} + resource "test_resource" "two" {} + resource "test_resource" "three" {} + `, + }, + { + // Config for the .tfquery.hcl query file + Config: ` + list "test_resource" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectResourceDisplayName( + "test_resource.test", + queryfilter.ByResourceIdentity(map[string]knownvalue.Check{"attr_1": knownvalue.StringExact("foo"), "attr_2": knownvalue.StringExact("bar")}), + knownvalue.StringExact("foobar"), + ), + }, + }, + }, + }) +} +``` + +## `ExpectResourceKnownValues` Query Check + +The [`querycheck.ExpectResourceKnownValues(resourceAddress, filter, knownValueChecks)`](https://pkg.go.dev/github.com/hashicorp/terraform-plugin-testing/querycheck#ExpectResourceKnownValues) query check is used to assert that a particular query result's resource object has the given values. + +This check requires a [query filter](/terraform/plugin/testing/acceptance-tests/query-checks/query-result#query-filter) to identify the query result on which the assertion should be performed. If the query filter returns zero or multiple results, the check will fail. + +```go +package example_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-testing/helper/resource" + "github.com/hashicorp/terraform-plugin-testing/querycheck" +) + +func TestExpectIdentity_QueryCheck(t *testing.T) { + t.Parallel() + + resource.Test(t, resource.TestCase{ + // Provider definition omitted. + Steps: []resource.TestStep{ + { + // Example config creating three resources for querying + Config: ` + resource "test_resource" "one" {} + resource "test_resource" "two" {} + resource "test_resource" "three" {} + `, + }, + { + // Config for the .tfquery.hcl query file + Config: ` + list "test_resource" "test" { + provider = examplecloud + include_resource = true + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectResourceKnownValues( + "test_resource.test", + queryfilter.ByResourceIdentity(map[string]knownvalue.Check{"attr_1": knownvalue.StringExact("foo"), "attr_2": knownvalue.StringExact("bar")}), + []querycheck.KnownValueCheck{ + { + tfjsonpath.New("attr_3"), + knownvalue.StringExact("foobar"), + }, + { + tfjsonpath.New("attr_4"), + knownvalue.StringExact("baz"), + }, + }, + ), + }, + }, + }, + }) +} +``` \ No newline at end of file diff --git a/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-mode.mdx b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-mode.mdx new file mode 100644 index 0000000000..4eebc5899c --- /dev/null +++ b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/query-mode.mdx @@ -0,0 +1,66 @@ +--- +page_title: 'Plugin Development - Acceptance Testing: Query mode' +description: |- + _Query_ mode is used for testing list resources using Terraform query. +--- + +# Acceptance Tests: Query mode + +Terraform _query_ is used to search for resources within a given scope and is part of +the workflow that enables practitioners to bulk import existing infrastructure into +Terraform. For reference information about the _query_ workflow: +[Terraform CLI](https://developer.hashicorp.com/terraform/cli/commands/query/import) +[Framework](https://developer.hashicorp.com/terraform/plugin/framework/list-resources) +[SDKv2](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/list) + +_Query_ mode is used for testing list resources by executing a real Terraform query, using the supplied +config as the contents of a `.tfquery.hcl` query file. + +Query checks can be performed on the results of the query to validate various things about the results such as the +number of results returned, the presence of a specific result, or the values of resource attributes of a result. + +## Examples + +### Testing a list resource with `terraform query` + +The following example shows an acceptance test that validates the presence of a specific query result by specifying the expected resource identity object. + +```go +func TestAccThing_query(t *testing.T) { + r.ParallelTest(t, r.TestCase{ + ProtoV5ProviderFactories: providerFactories, + Steps: []r.TestStep{ + { + Config: ` + resource "examplecloud_thing" "test1" {} + resource "examplecloud_thing" "test2" {} + resource "examplecloud_thing" "test3" {} + `, + }, + { + Config: ` + list "examplecloud_thing" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("examplecloud_thing.test", 1), + querycheck.ExpectResourceDisplayName("examplecloud_thing.test", + queryfilter.ByResourceIdentity(map[string]knownvalue.Check{ + "attr_1": knownvalue.StringExact("foo"), + "attr_2": knownvalue.StringExact("bar"), + }), knownvalue.StringExact("foobarthing")) + } + }, + }, + }) +} +``` + + + + + diff --git a/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/teststep.mdx b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/teststep.mdx index 192406852b..4e7c6893b7 100644 --- a/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/teststep.mdx +++ b/content/terraform-plugin-testing/v1.14.x/docs/plugin/testing/acceptance-tests/teststep.mdx @@ -14,8 +14,8 @@ under test. ## Test Modes -Terraform's test framework facilitates three distinct modes of acceptance tests, -_Lifecycle (config)_, _Import_ and _Refresh_. +Terraform's test framework facilitates four distinct modes of acceptance tests, +_Lifecycle (config)_, _Import_, _Refresh_ and _Query_. ### Lifecycle (config) mode @@ -84,6 +84,40 @@ steps := []TestStep{ } ``` +### Query mode + +_Query_ mode runs `terraform query` to test list resources. The configuration supplied +for a _Query_ mode test step are used as the contents of a `.tfquery.hcl` query file. + +-> **Note:** To define a _Query_ mode test step, set the `Query` field to `true`. + +It is recommended to use _Query_ mode after a _Lifecycle (config)_ mode test step that +creates instances of the resources to be listed. + +```go +steps := []TestStep{ + { + Config: ` + resource "random_string" "puzzle" { length = 12 } + resource "random_string" "jigsaw" { length = 1000 } + `, + }, + { + Config: ` + list "random_string" "test" { + provider = examplecloud + + config {} + } + `, + Query: true, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthExact("random_string.test", 2), + } + }, +} +``` + ## Steps `Steps` is a field within @@ -155,6 +189,17 @@ Refer to the [State Checks](/terraform/plugin/testing/acceptance-tests/state-che section for more information about the built-in state checks for resources, data sources, output values, and how to write custom state checks. +## Query Checks + +After the configuration for a _Query_ mode `TestStep` is run, Terraform's +testing framework provides developers an opportunity to check the query results +by providing one or more [query check +implementations](/terraform/plugin/testing/acceptance-tests/query-checks). + +Refer to the [Query Checks](terraform/plugin/testing/acceptance-tests/query-checks) +section for more information about the built-in query checks for list resources +and how to write custom query checks. + ### Legacy Check function