diff --git a/helper/resource/query/examplecloud_list_test.go b/helper/resource/query/examplecloud_list_test.go index 7b18943e..ca3972f2 100644 --- a/helper/resource/query/examplecloud_list_test.go +++ b/helper/resource/query/examplecloud_list_test.go @@ -19,9 +19,9 @@ func examplecloudListResource() testprovider.ListResource { Block: &tfprotov6.SchemaBlock{ Attributes: []*tfprotov6.SchemaAttribute{ { - Name: "id", + Name: "resource_group_name", Type: tftypes.String, - Computed: true, + Required: true, }, }, }, @@ -33,57 +33,199 @@ func examplecloudListResource() testprovider.ListResource { Resource: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, - "name": tftypes.String, + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "westeurope"), - "name": tftypes.NewValue(tftypes.String, "somevalue"), + "id": tftypes.NewValue(tftypes.String, "foo/banane"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banane"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 5), }, )), Identity: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, + "resource_group_name": tftypes.String, + "name": tftypes.String, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue1"), - "location": tftypes.NewValue(tftypes.String, "westeurope"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banane"), }, )), + DisplayName: "banane", }) push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/ananas"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "ananas"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 9000), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "ananas"), + }, + )), + DisplayName: "ananas", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/kiwi"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "kiwi"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 88), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "kiwi"), + }, + )), + DisplayName: "kiwi", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "bar/papaya"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banane"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 3), + }, + )), Identity: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, + "resource_group_name": tftypes.String, + "name": tftypes.String, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue2"), - "location": tftypes.NewValue(tftypes.String, "westeurope2"), + "resource_group_name": tftypes.NewValue(tftypes.String, "bar"), + "name": tftypes.NewValue(tftypes.String, "papaya"), }, )), + DisplayName: "papaya", }) push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "bar/birne"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "birne"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 8564), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "bar"), + "name": tftypes.NewValue(tftypes.String, "birne"), + }, + )), + DisplayName: "birne", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "bar/kirsche"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "kirsche"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 500), + }, + )), Identity: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, + "resource_group_name": tftypes.String, + "name": tftypes.String, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue3"), - "location": tftypes.NewValue(tftypes.String, "westeurope3"), + "resource_group_name": tftypes.NewValue(tftypes.String, "bar"), + "name": tftypes.NewValue(tftypes.String, "kirsche"), }, )), + DisplayName: "kirsche", }) }, }, diff --git a/helper/resource/query/examplecloud_test.go b/helper/resource/query/examplecloud_test.go index 3ad86c16..0376455a 100644 --- a/helper/resource/query/examplecloud_test.go +++ b/helper/resource/query/examplecloud_test.go @@ -17,27 +17,31 @@ func examplecloudResource() testprovider.Resource { NewState: tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, - "name": tftypes.String, + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "westeurope"), - "name": tftypes.NewValue(tftypes.String, "somevalue"), + "id": tftypes.NewValue(tftypes.String, "foo/banana"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banana"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, int64(5)), }, ), NewIdentity: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, + "resource_group_name": tftypes.String, + "name": tftypes.String, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "somelocation"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banana"), }, )), }, @@ -45,27 +49,31 @@ func examplecloudResource() testprovider.Resource { NewState: tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, - "name": tftypes.String, + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "westeurope"), - "name": tftypes.NewValue(tftypes.String, "somevalue"), + "id": tftypes.NewValue(tftypes.String, "foo/banana"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banana"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, int64(5)), }, ), NewIdentity: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, + "resource_group_name": tftypes.String, + "name": tftypes.String, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "westeurope"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banana"), }, )), }, @@ -73,27 +81,31 @@ func examplecloudResource() testprovider.Resource { State: tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, - "name": tftypes.String, + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "westeurope"), - "name": tftypes.NewValue(tftypes.String, "somevalue"), + "id": tftypes.NewValue(tftypes.String, "foo/banana"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banana"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, int64(5)), }, ), Identity: teststep.Pointer(tftypes.NewValue( tftypes.Object{ AttributeTypes: map[string]tftypes.Type{ - "id": tftypes.String, - "location": tftypes.String, + "resource_group_name": tftypes.String, + "name": tftypes.String, }, }, map[string]tftypes.Value{ - "id": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), - "location": tftypes.NewValue(tftypes.String, "westeurope/somevalue"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banana"), }, )), }, @@ -104,6 +116,8 @@ func examplecloudResource() testprovider.Resource { ComputedStringAttribute("id"), RequiredStringAttribute("location"), RequiredStringAttribute("name"), + RequiredStringAttribute("resource_group_name"), + OptionalNumberAttribute("instances"), }, }, }, @@ -113,12 +127,12 @@ func examplecloudResource() testprovider.Resource { Version: 1, IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ { - Name: "id", + Name: "resource_group_name", Type: tftypes.String, RequiredForImport: true, }, { - Name: "location", + Name: "name", Type: tftypes.String, RequiredForImport: true, }, diff --git a/helper/resource/query/query_test.go b/helper/resource/query/query_test.go index 1fa76381..26ac069f 100644 --- a/helper/resource/query/query_test.go +++ b/helper/resource/query/query_test.go @@ -34,11 +34,15 @@ func TestQuery(t *testing.T) { }, 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" { - id = "westeurope/somevalue" - location = "westeurope" - name = "somevalue" + name = "banana" + resource_group_name = "foo" + location = "westeurope" + + instances = 5 }`, }, { // Query mode step 2, operates on .tfquery.hcl files (needs tf file with terraform providers block) @@ -49,96 +53,50 @@ func TestQuery(t *testing.T) { Query: true, Config: ` provider "examplecloud" {} + list "examplecloud_containerette" "test" { provider = examplecloud config { - id = "westeurope/somevalue" + resource_group_name = "foo" } } + list "examplecloud_containerette" "test2" { provider = examplecloud config { - id = "foo" + resource_group_name = "bar" } } `, QueryResultChecks: []querycheck.QueryResultCheck{ querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ - "id": knownvalue.StringExact("westeurope/somevalue1"), - "location": knownvalue.StringExact("westeurope"), + "name": knownvalue.StringExact("banane"), + "resource_group_name": knownvalue.StringExact("foo"), }), querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ - "id": knownvalue.StringExact("westeurope/somevalue2"), - "location": knownvalue.StringExact("westeurope2"), + "name": knownvalue.StringExact("ananas"), + "resource_group_name": knownvalue.StringExact("foo"), }), querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ - "id": knownvalue.StringExact("westeurope/somevalue3"), - "location": knownvalue.StringExact("westeurope3"), + "name": knownvalue.StringExact("kiwi"), + "resource_group_name": knownvalue.StringExact("foo"), }), querycheck.ExpectIdentity("examplecloud_containerette.test2", map[string]knownvalue.Check{ - "id": knownvalue.StringExact("westeurope/somevalue1"), - "location": knownvalue.StringExact("westeurope"), + "name": knownvalue.StringExact("papaya"), + "resource_group_name": knownvalue.StringExact("bar"), }), querycheck.ExpectIdentity("examplecloud_containerette.test2", map[string]knownvalue.Check{ - "id": knownvalue.StringExact("westeurope/somevalue2"), - "location": knownvalue.StringExact("westeurope2"), + "name": knownvalue.StringExact("birne"), + "resource_group_name": knownvalue.StringExact("bar"), }), querycheck.ExpectIdentity("examplecloud_containerette.test2", map[string]knownvalue.Check{ - "id": knownvalue.StringExact("westeurope/somevalue3"), - "location": knownvalue.StringExact("westeurope3"), + "name": knownvalue.StringExact("kirsche"), + "resource_group_name": knownvalue.StringExact("bar"), }), }, }, - { - Query: true, - Config: ` - provider "examplecloud" {} - list "examplecloud_containerette" "test" { - provider = examplecloud - - config { - id = "westeurope/somevalue" - } - } - list "examplecloud_containerette" "test2" { - provider = examplecloud - - config { - id = "foo" - } - } - `, - QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLength("examplecloud_containerette.test", 3), - querycheck.ExpectLength("examplecloud_containerette.test2", 3), - }, - }, - { - Query: true, - Config: ` - provider "examplecloud" {} - list "examplecloud_containerette" "test" { - provider = examplecloud - - config { - id = "westeurope/somevalue" - } - } - list "examplecloud_containerette" "test2" { - provider = examplecloud - - config { - id = "foo" - } - } - `, - QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 2), - querycheck.ExpectLengthAtLeast("examplecloud_containerette.test2", 1), - }, - }, }, }) } diff --git a/helper/resource/query/types_test.go b/helper/resource/query/types_test.go index 7620d4d7..a4612db3 100644 --- a/helper/resource/query/types_test.go +++ b/helper/resource/query/types_test.go @@ -41,6 +41,14 @@ func RequiredNumberAttribute(name string) *tfprotov6.SchemaAttribute { } } +func OptionalNumberAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.Number, + Optional: true, + } +} + func ComputedStringAttribute(name string) *tfprotov6.SchemaAttribute { return &tfprotov6.SchemaAttribute{ Name: name, diff --git a/internal/testing/testsdk/providerserver/providerserver.go b/internal/testing/testsdk/providerserver/providerserver.go index 0f14d0da..72be6cbe 100644 --- a/internal/testing/testsdk/providerserver/providerserver.go +++ b/internal/testing/testsdk/providerserver/providerserver.go @@ -1062,22 +1062,28 @@ func (s ProviderServer) ListResource(ctx context.Context, req *tfprotov6.ListRes return nil, fmt.Errorf("failed to retrieve resource identity schema: %v", err) } - schemaReq := list.SchemaRequest{} - schemaResp := &list.SchemaResponse{} + configSchemaReq := list.SchemaRequest{} + configSchemaResp := &list.SchemaResponse{} - listresource.Schema(ctx, schemaReq, schemaResp) - if len(schemaResp.Diagnostics) > 0 { - return nil, fmt.Errorf("failed to retrieve resource schema: %v", schemaResp.Diagnostics) + listresource.Schema(ctx, configSchemaReq, configSchemaResp) + if len(configSchemaResp.Diagnostics) > 0 { + return nil, fmt.Errorf("failed to retrieve resource schema: %v", configSchemaResp.Diagnostics) + } + + resourceSchemaResp := &resource.SchemaResponse{} + r.Schema(ctx, resource.SchemaRequest{}, resourceSchemaResp) + if resourceSchemaResp.Schema == nil { + return nil, fmt.Errorf("failed to retrieve resource schema: %v", resourceSchemaResp.Schema) } listReq := list.ListRequest{ TypeName: req.TypeName, IncludeResource: req.IncludeResource, Limit: req.Limit, - ResourceSchema: schemaResp.Schema, + ResourceSchema: resourceSchemaResp.Schema, } - listReq.Config, diag = DynamicValueToValue(schemaResp.Schema, req.Config) + listReq.Config, diag = DynamicValueToValue(configSchemaResp.Schema, req.Config) if diag != nil { return nil, fmt.Errorf("failed to convert config to value: %v", err) } @@ -1157,8 +1163,9 @@ func processListResult(req list.ListRequest, result list.ListResult) tfprotov6.L } listResourceResult.Resource, diag = ValuetoDynamicValue(req.ResourceSchema, *result.Resource) - listResourceResult.Diagnostics = append(listResourceResult.Diagnostics, diag) - return listResourceResult + if diag != nil { + listResourceResult.Diagnostics = append(listResourceResult.Diagnostics, diag) + } } listResourceResult.Identity = &tfprotov6.ResourceIdentityData{} @@ -1167,7 +1174,6 @@ func processListResult(req list.ListRequest, result list.ListResult) tfprotov6.L listResourceResult.Identity.IdentityData, diag = IdentityValuetoDynamicValue(req.ResourceIdentitySchema, *result.Identity) if diag != nil { listResourceResult.Diagnostics = append(listResourceResult.Diagnostics, diag) - return listResourceResult } } diff --git a/querycheck/contains_name_test.go b/querycheck/contains_name_test.go index ba8e2f2d..8ae703b1 100644 --- a/querycheck/contains_name_test.go +++ b/querycheck/contains_name_test.go @@ -7,162 +7,13 @@ import ( "testing" "github.com/hashicorp/terraform-plugin-go/tfprotov6" - "github.com/hashicorp/terraform-plugin-go/tftypes" 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/list" "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/providerserver" - "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource" - "github.com/hashicorp/terraform-plugin-testing/internal/teststep" "github.com/hashicorp/terraform-plugin-testing/querycheck" "github.com/hashicorp/terraform-plugin-testing/tfversion" ) -func dessertsThatStartWithPResource() testprovider.Resource { - return testprovider.Resource{ - CreateResponse: &resource.CreateResponse{ - NewState: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - ), - NewIdentity: teststep.Pointer(tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - )), - }, - ReadResponse: &resource.ReadResponse{ - NewState: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - ), - NewIdentity: teststep.Pointer(tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - )), - }, - ImportStateResponse: &resource.ImportStateResponse{ - State: tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - ), - Identity: teststep.Pointer(tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - )), - }, - SchemaResponse: &resource.SchemaResponse{ - Schema: &tfprotov6.Schema{ - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "name", - Type: tftypes.String, - Required: true, - }, - }, - }, - }, - }, - IdentitySchemaResponse: &resource.IdentitySchemaResponse{ - Schema: &tfprotov6.ResourceIdentitySchema{ - Version: 1, - IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ - { - Name: "name", - Type: tftypes.String, - RequiredForImport: true, - }, - }, - }, - }, - } -} - -func dessertsThatStartWithPListResource() testprovider.ListResource { - return testprovider.ListResource{ - IncludeResource: true, - SchemaResponse: &list.SchemaResponse{ - Schema: &tfprotov6.Schema{ - Block: &tfprotov6.SchemaBlock{ - Attributes: []*tfprotov6.SchemaAttribute{ - { - Name: "group", - Type: tftypes.String, - Computed: true, - }, - }, - }, - }, - }, - ListResultsStream: &list.ListResultsStream{ - Results: func(push func(list.ListResult) bool) { - push(list.ListResult{ - Identity: teststep.Pointer(tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pie"), - }, - )), - DisplayName: "pie", - }) - push(list.ListResult{ - Identity: teststep.Pointer(tftypes.NewValue( - tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "name": tftypes.String, - }, - }, - map[string]tftypes.Value{ - "name": tftypes.NewValue(tftypes.String, "pudding"), - }, - )), - DisplayName: "pudding", - }) - }, - }, - } -} - func TestContainsResourceWithName(t *testing.T) { t.Parallel() @@ -171,40 +22,56 @@ func TestContainsResourceWithName(t *testing.T) { tfversion.SkipBelow(tfversion.Version1_14_0), }, ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ - "dessertcloud": providerserver.NewProviderServer(testprovider.Provider{ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ ListResources: map[string]testprovider.ListResource{ - "dessert_letter_p": dessertsThatStartWithPListResource(), + "examplecloud_containerette": examplecloudListResource(), }, Resources: map[string]testprovider.Resource{ - "dessert_letter_p": dessertsThatStartWithPResource(), + "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 "dessertcloud" {} + provider "examplecloud" {} - list "dessert_letter_p" "test" { - provider = dessertcloud + list "examplecloud_containerette" "test" { + provider = examplecloud config { - group = "foo" + resource_group_name = "foo" } } - list "dessert_letter_p" "test2" { - provider = dessertcloud + list "examplecloud_containerette" "test2" { + provider = examplecloud config { - group = "bar" + resource_group_name = "bar" } } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ContainsResourceWithName("dessert_letter_p.test", "pie"), - querycheck.ContainsResourceWithName("dessert_letter_p.test", "pudding"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test", "banane"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test", "ananas"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test", "kiwi"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test2", "papaya"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test2", "birne"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test2", "kirsche"), }, }, }, @@ -219,41 +86,53 @@ func TestContainsResourceWithName_NotFound(t *testing.T) { tfversion.SkipBelow(tfversion.Version1_14_0), }, ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){ - "dessertcloud": providerserver.NewProviderServer(testprovider.Provider{ + "examplecloud": providerserver.NewProviderServer(testprovider.Provider{ ListResources: map[string]testprovider.ListResource{ - "dessert_letter_p": dessertsThatStartWithPListResource(), + "examplecloud_containerette": examplecloudListResource(), }, Resources: map[string]testprovider.Resource{ - "dessert_letter_p": dessertsThatStartWithPResource(), + "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 "dessertcloud" {} + provider "examplecloud" {} - list "dessert_letter_p" "test" { - provider = dessertcloud + list "examplecloud_containerette" "test" { + provider = examplecloud config { - group = "foo" - } + resource_group_name = "foo" + } } - list "dessert_letter_p" "test2" { - provider = dessertcloud + list "examplecloud_containerette" "test2" { + provider = examplecloud config { - group = "bar" + resource_group_name = "bar" } } `, QueryResultChecks: []querycheck.QueryResultCheck{ - querycheck.ContainsResourceWithName("dessert_letter_p.test", "pavlova"), + querycheck.ContainsResourceWithName("examplecloud_containerette.test", "pflaume"), }, - ExpectError: regexp.MustCompile("expected to find resource with display name \"pavlova\" in results but resource was not found"), + ExpectError: regexp.MustCompile("expected to find resource with display name \"pflaume\" in results but resource was not found"), }, }, }) diff --git a/querycheck/examplecloud_list_test.go b/querycheck/examplecloud_list_test.go new file mode 100644 index 00000000..af7cce88 --- /dev/null +++ b/querycheck/examplecloud_list_test.go @@ -0,0 +1,233 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package querycheck_test + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/list" + "github.com/hashicorp/terraform-plugin-testing/internal/teststep" +) + +func examplecloudListResource() testprovider.ListResource { + return testprovider.ListResource{ + IncludeResource: true, + SchemaResponse: &list.SchemaResponse{ + Schema: &tfprotov6.Schema{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + { + Name: "resource_group_name", + Type: tftypes.String, + Required: true, + }, + }, + }, + }, + }, + ListResultsStream: &list.ListResultsStream{ + Results: func(push func(list.ListResult) bool) { + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/banane"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banane"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 5), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banane"), + }, + )), + DisplayName: "banane", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/ananas"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "ananas"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 9000), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "ananas"), + }, + )), + DisplayName: "ananas", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/kiwi"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "kiwi"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 88), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "kiwi"), + }, + )), + DisplayName: "kiwi", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "bar/papaya"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banane"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 3), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "bar"), + "name": tftypes.NewValue(tftypes.String, "papaya"), + }, + )), + DisplayName: "papaya", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "bar/birne"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "birne"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 8564), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "bar"), + "name": tftypes.NewValue(tftypes.String, "birne"), + }, + )), + DisplayName: "birne", + }) + push(list.ListResult{ + Resource: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "bar/kirsche"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "kirsche"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, 500), + }, + )), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "bar"), + "name": tftypes.NewValue(tftypes.String, "kirsche"), + }, + )), + DisplayName: "kirsche", + }) + }, + }, + } +} diff --git a/querycheck/examplecloud_test.go b/querycheck/examplecloud_test.go new file mode 100644 index 00000000..9984f84d --- /dev/null +++ b/querycheck/examplecloud_test.go @@ -0,0 +1,143 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package querycheck_test + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" + "github.com/hashicorp/terraform-plugin-testing/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource" + "github.com/hashicorp/terraform-plugin-testing/internal/teststep" +) + +func examplecloudResource() testprovider.Resource { + return testprovider.Resource{ + CreateResponse: &resource.CreateResponse{ + NewState: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/banana"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banana"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, int64(5)), + }, + ), + NewIdentity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banana"), + }, + )), + }, + ReadResponse: &resource.ReadResponse{ + NewState: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/banana"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banana"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, int64(5)), + }, + ), + NewIdentity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banana"), + }, + )), + }, + ImportStateResponse: &resource.ImportStateResponse{ + State: tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "id": tftypes.String, + "location": tftypes.String, + "name": tftypes.String, + "resource_group_name": tftypes.String, + "instances": tftypes.Number, + }, + }, + map[string]tftypes.Value{ + "id": tftypes.NewValue(tftypes.String, "foo/banana"), + "location": tftypes.NewValue(tftypes.String, "westeurope"), + "name": tftypes.NewValue(tftypes.String, "banana"), + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "instances": tftypes.NewValue(tftypes.Number, int64(5)), + }, + ), + Identity: teststep.Pointer(tftypes.NewValue( + tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "resource_group_name": tftypes.String, + "name": tftypes.String, + }, + }, + map[string]tftypes.Value{ + "resource_group_name": tftypes.NewValue(tftypes.String, "foo"), + "name": tftypes.NewValue(tftypes.String, "banana"), + }, + )), + }, + SchemaResponse: &resource.SchemaResponse{ + Schema: &tfprotov6.Schema{ + Block: &tfprotov6.SchemaBlock{ + Attributes: []*tfprotov6.SchemaAttribute{ + ComputedStringAttribute("id"), + RequiredStringAttribute("location"), + RequiredStringAttribute("name"), + RequiredStringAttribute("resource_group_name"), + OptionalNumberAttribute("instances"), + }, + }, + }, + }, + IdentitySchemaResponse: &resource.IdentitySchemaResponse{ + Schema: &tfprotov6.ResourceIdentitySchema{ + Version: 1, + IdentityAttributes: []*tfprotov6.ResourceIdentitySchemaAttribute{ + { + Name: "resource_group_name", + Type: tftypes.String, + RequiredForImport: true, + }, + { + Name: "name", + Type: tftypes.String, + RequiredForImport: true, + }, + }, + }, + }, + } +} diff --git a/querycheck/expect_identity_test.go b/querycheck/expect_identity_test.go new file mode 100644 index 00000000..64a3d7d6 --- /dev/null +++ b/querycheck/expect_identity_test.go @@ -0,0 +1,170 @@ +// Copyright (c) HashiCorp, Inc. +// 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/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/querycheck" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestExpectIdentity(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 mode step 2, operates on .tfquery.hcl files (needs tf file with terraform providers block) + // ```provider "examplecloud" {}``` has a slightly different syntax for a .tfquery.hcl file + // provider bock simulates a real providers workflow + // "config" in this case means configuration of the list resource/filters + + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("banane"), + "resource_group_name": knownvalue.StringExact("foo"), + }), + querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("ananas"), + "resource_group_name": knownvalue.StringExact("foo"), + }), + querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("kiwi"), + "resource_group_name": knownvalue.StringExact("foo"), + }), + querycheck.ExpectIdentity("examplecloud_containerette.test2", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("papaya"), + "resource_group_name": knownvalue.StringExact("bar"), + }), + querycheck.ExpectIdentity("examplecloud_containerette.test2", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("birne"), + "resource_group_name": knownvalue.StringExact("bar"), + }), + querycheck.ExpectIdentity("examplecloud_containerette.test2", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("kirsche"), + "resource_group_name": knownvalue.StringExact("bar"), + }), + }, + }, + }, + }) +} + +func TestExpectIdentity_NotFound(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 mode step 2, operates on .tfquery.hcl files (needs tf file with terraform providers block) + // ```provider "examplecloud" {}``` has a slightly different syntax for a .tfquery.hcl file + // provider bock simulates a real providers workflow + // "config" in this case means configuration of the list resource/filters + + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + + config { + resource_group_name = "foo" + } + } + + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectIdentity("examplecloud_containerette.test", map[string]knownvalue.Check{ + "name": knownvalue.StringExact("owo"), + "resource_group_name": knownvalue.StringExact("uwu"), + }), + }, + ExpectError: regexp.MustCompile("an identity with the following attributes was not found\nattribute \"name\": owo\nattribute \"resource_group_name\": uwu\naddress: examplecloud_containerette.test"), + }, + }, + }) +} diff --git a/querycheck/expect_known_value_test.go b/querycheck/expect_known_value_test.go new file mode 100644 index 00000000..a12ab688 --- /dev/null +++ b/querycheck/expect_known_value_test.go @@ -0,0 +1,142 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package querycheck_test + +import ( + "math/big" + "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/knownvalue" + "github.com/hashicorp/terraform-plugin-testing/querycheck" + "github.com/hashicorp/terraform-plugin-testing/tfjsonpath" + "github.com/hashicorp/terraform-plugin-testing/tfversion" +) + +func TestExpectKnownValue(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 mode step 2, operates on .tfquery.hcl files (needs tf file with terraform providers block) + // ```provider "examplecloud" {}``` has a slightly different syntax for a .tfquery.hcl file + // provider bock simulates a real providers workflow + // "config" in this case means configuration of the list resource/filters + + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + include_resource = true + + config { + resource_group_name = "foo" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectKnownValue( + "examplecloud_containerette.test", + "banane", + tfjsonpath.New("instances"), + knownvalue.NumberExact(big.NewFloat(5)), + ), + }, + }, + }, + }) +} + +func TestExpectKnownValue_ValueIncorrect(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 mode step 2, operates on .tfquery.hcl files (needs tf file with terraform providers block) + // ```provider "examplecloud" {}``` has a slightly different syntax for a .tfquery.hcl file + // provider bock simulates a real providers workflow + // "config" in this case means configuration of the list resource/filters + + Query: true, + Config: ` + provider "examplecloud" {} + + list "examplecloud_containerette" "test" { + provider = examplecloud + include_resource = true + + config { + resource_group_name = "foo" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectKnownValue( + "examplecloud_containerette.test", + "banane", + tfjsonpath.New("instances"), + knownvalue.NumberExact(big.NewFloat(4)), + ), + }, + ExpectError: regexp.MustCompile("the following errors were found while checking values: error checking value for attribute at path: instances for resource banane, err: expected value 4 for NumberExact check, got: 5;"), + }, + }, + }) +} diff --git a/querycheck/expect_result_length_atleast_test.go b/querycheck/expect_result_length_atleast_test.go new file mode 100644 index 00000000..4ddb5267 --- /dev/null +++ b/querycheck/expect_result_length_atleast_test.go @@ -0,0 +1,132 @@ +// Copyright (c) HashiCorp, Inc. +// 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 TestResultLengthAtLeast(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 = "foo" + } + } + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 2), + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test2", 1), + }, + }, + }, + }) +} + +func TestResultLengthAtLeast_TooFewResults(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 = "foo" + } + } + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLengthAtLeast("examplecloud_containerette.test", 8), + }, + ExpectError: regexp.MustCompile("Query result of at least length 8 - expected but got 6."), + }, + }, + }) +} diff --git a/querycheck/expect_result_length_exact_test.go b/querycheck/expect_result_length_exact_test.go new file mode 100644 index 00000000..a69b959d --- /dev/null +++ b/querycheck/expect_result_length_exact_test.go @@ -0,0 +1,132 @@ +// Copyright (c) HashiCorp, Inc. +// 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 TestResultLengthExact(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 = "foo" + } + } + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("examplecloud_containerette.test", 6), + querycheck.ExpectLength("examplecloud_containerette.test2", 6), + }, + }, + }, + }) +} + +func TestResultLengthExact_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(), + }, + 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 = "foo" + } + } + list "examplecloud_containerette" "test2" { + provider = examplecloud + + config { + resource_group_name = "bar" + } + } + `, + QueryResultChecks: []querycheck.QueryResultCheck{ + querycheck.ExpectLength("examplecloud_containerette.test", 2), + }, + ExpectError: regexp.MustCompile("number of found resources 2 - expected but got 6."), + }, + }, + }) +} diff --git a/querycheck/types_test.go b/querycheck/types_test.go new file mode 100644 index 00000000..18c8ff50 --- /dev/null +++ b/querycheck/types_test.go @@ -0,0 +1,74 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package querycheck_test + +import ( + "github.com/hashicorp/terraform-plugin-go/tfprotov6" + "github.com/hashicorp/terraform-plugin-go/tftypes" +) + +func RequiredBoolAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.Bool, + Required: true, + } +} + +func OptionalComputedListAttribute(name string, elementType tftypes.Type) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.List{ElementType: elementType}, + Optional: true, + Computed: true, + } +} + +func RequiredListAttribute(name string, elementType tftypes.Type) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.List{ElementType: elementType}, + Required: true, + } +} + +func RequiredNumberAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.Number, + Required: true, + } +} + +func OptionalNumberAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.Number, + Optional: true, + } +} + +func ComputedStringAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.String, + Computed: true, + } +} + +func OptionalStringAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.String, + Optional: true, + } +} + +func RequiredStringAttribute(name string) *tfprotov6.SchemaAttribute { + return &tfprotov6.SchemaAttribute{ + Name: name, + Type: tftypes.String, + Required: true, + } +}