Skip to content

Commit

Permalink
internal/testing: Initial testsdk and testprovider packages (#166)
Browse files Browse the repository at this point in the history
Reference: #151

Creates an internal terraform-plugin-go based SDK and declarative provider for unit testing for within this Go module. The new `helper/resource` tests should be equivalent to the existing terraform-plugin-sdk based `TestTest_TestStep_ExternalProviders_To_ProviderFactories` and `TestTest_TestStep_ProviderFactories_To_ExternalProviders` tests.
  • Loading branch information
bflad committed Aug 25, 2023
1 parent 12db397 commit ba04167
Show file tree
Hide file tree
Showing 19 changed files with 1,471 additions and 0 deletions.
111 changes: 111 additions & 0 deletions helper/resource/teststep_providers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,16 @@ import (
"github.com/hashicorp/go-cty/cty"
"github.com/hashicorp/terraform-plugin-go/tfprotov5"
"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-go/tftypes"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"github.com/hashicorp/terraform-plugin-testing/internal/plugintest"
"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/internal/testing/testsdk/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

Expand Down Expand Up @@ -1142,6 +1146,53 @@ func TestTest_TestStep_ExternalProvidersAndProviderFactories_NonHashiCorpNamespa
})
}

func TestTest_TestStep_ExternalProviders_To_ProtoV6ProviderFactories(t *testing.T) {
t.Parallel()

Test(t, TestCase{
Steps: []TestStep{
{
Config: `resource "null_resource" "test" {}`,
ExternalProviders: map[string]ExternalProvider{
"null": {
Source: "registry.terraform.io/hashicorp/null",
VersionConstraint: "3.1.1",
},
},
},
{
Config: `resource "null_resource" "test" {}`,
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"null": providerserver.NewProviderServer(testprovider.Provider{
Resources: map[string]testprovider.Resource{
"null_resource": {
SchemaResponse: &resource.SchemaResponse{
Schema: &tfprotov6.Schema{
Block: &tfprotov6.SchemaBlock{
Attributes: []*tfprotov6.SchemaAttribute{
{
Name: "id",
Type: tftypes.String,
Computed: true,
},
{
Name: "triggers",
Type: tftypes.Map{ElementType: tftypes.String},
Optional: true,
},
},
},
},
},
},
},
}),
},
},
},
})
}

func TestTest_TestStep_ExternalProviders_To_ProviderFactories(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -1406,6 +1457,66 @@ func TestTest_TestStep_ProtoV6ProviderFactories_Error(t *testing.T) {
})
}

func TestTest_TestStep_ProtoV6ProviderFactories_To_ExternalProviders(t *testing.T) {
t.Parallel()

Test(t, TestCase{
Steps: []TestStep{
{
Config: `resource "null_resource" "test" {}`,
ProtoV6ProviderFactories: map[string]func() (tfprotov6.ProviderServer, error){
"null": providerserver.NewProviderServer(testprovider.Provider{
Resources: map[string]testprovider.Resource{
"null_resource": {
CreateResponse: &resource.CreateResponse{
NewState: tftypes.NewValue(
tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"id": tftypes.String,
"triggers": tftypes.Map{ElementType: tftypes.String},
},
},
map[string]tftypes.Value{
"id": tftypes.NewValue(tftypes.String, "test"),
"triggers": tftypes.NewValue(tftypes.Map{ElementType: tftypes.String}, nil),
},
),
},
SchemaResponse: &resource.SchemaResponse{
Schema: &tfprotov6.Schema{
Block: &tfprotov6.SchemaBlock{
Attributes: []*tfprotov6.SchemaAttribute{
{
Name: "id",
Type: tftypes.String,
Computed: true,
},
{
Name: "triggers",
Type: tftypes.Map{ElementType: tftypes.String},
Optional: true,
},
},
},
},
},
},
},
}),
},
},
{
Config: `resource "null_resource" "test" {}`,
ExternalProviders: map[string]ExternalProvider{
"null": {
Source: "registry.terraform.io/hashicorp/null",
},
},
},
},
})
}

func TestTest_TestStep_ProviderFactories(t *testing.T) {
t.Parallel()

Expand Down
6 changes: 6 additions & 0 deletions internal/testing/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// Package testing contains functionality and helpers for unit testing within
// this Go module.
package testing
38 changes: 38 additions & 0 deletions internal/testing/testprovider/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package testprovider

import (
"context"

"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource"
)

var _ datasource.DataSource = DataSource{}

type DataSource struct {
ReadResponse *datasource.ReadResponse
SchemaResponse *datasource.SchemaResponse
ValidateConfigResponse *datasource.ValidateConfigResponse
}

func (d DataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
if d.ReadResponse != nil {
resp.Diagnostics = d.ReadResponse.Diagnostics
resp.State = d.ReadResponse.State
}
}

func (d DataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
if d.SchemaResponse != nil {
resp.Diagnostics = d.SchemaResponse.Diagnostics
resp.Schema = d.SchemaResponse.Schema
}
}

func (d DataSource) ValidateConfig(ctx context.Context, req datasource.ValidateConfigRequest, resp *datasource.ValidateConfigResponse) {
if d.ValidateConfigResponse != nil {
resp.Diagnostics = d.ValidateConfigResponse.Diagnostics
}
}
6 changes: 6 additions & 0 deletions internal/testing/testprovider/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

// Package testprovider is a declarative provider for implementing unit testing
// within this Go module.
package testprovider
75 changes: 75 additions & 0 deletions internal/testing/testprovider/provider.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package testprovider

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/datasource"
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/provider"
"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource"
)

var _ provider.Provider = Provider{}

// Provider is a declarative provider implementation for unit testing in this
// Go module.
type Provider struct {
ConfigureResponse *provider.ConfigureResponse
DataSources map[string]DataSource
Resources map[string]Resource
SchemaResponse *provider.SchemaResponse
StopResponse *provider.StopResponse
ValidateConfigResponse *provider.ValidateConfigResponse
}

func (p Provider) Configure(ctx context.Context, req provider.ConfigureRequest, resp *provider.ConfigureResponse) {
if p.ConfigureResponse != nil {
resp.Diagnostics = p.ConfigureResponse.Diagnostics
}
}

func (p Provider) DataSourcesMap() map[string]datasource.DataSource {
datasources := make(map[string]datasource.DataSource, len(p.DataSources))

for typeName, d := range p.DataSources {
datasources[typeName] = d
}

return datasources
}

func (p Provider) ResourcesMap() map[string]resource.Resource {
resources := make(map[string]resource.Resource, len(p.Resources))

for typeName, d := range p.Resources {
resources[typeName] = d
}

return resources
}

func (p Provider) Stop(ctx context.Context, req provider.StopRequest, resp *provider.StopResponse) {
if p.StopResponse != nil {
resp.Error = p.StopResponse.Error
}
}

func (p Provider) Schema(ctx context.Context, req provider.SchemaRequest, resp *provider.SchemaResponse) {
if p.SchemaResponse != nil {
resp.Diagnostics = p.SchemaResponse.Diagnostics
resp.Schema = p.SchemaResponse.Schema
}

resp.Schema = &tfprotov6.Schema{
Block: &tfprotov6.SchemaBlock{},
}
}

func (p Provider) ValidateConfig(ctx context.Context, req provider.ValidateConfigRequest, resp *provider.ValidateConfigResponse) {
if p.ValidateConfigResponse != nil {
resp.Diagnostics = p.ValidateConfigResponse.Diagnostics
}
}
88 changes: 88 additions & 0 deletions internal/testing/testprovider/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package testprovider

import (
"context"

"github.com/hashicorp/terraform-plugin-testing/internal/testing/testsdk/resource"
)

var _ resource.Resource = Resource{}

type Resource struct {
CreateResponse *resource.CreateResponse
DeleteResponse *resource.DeleteResponse
ImportStateResponse *resource.ImportStateResponse

// Planning happens multiple ways during a single TestStep, so statically
// defining only the response is very problematic.
PlanChangeFunc func(context.Context, resource.PlanChangeRequest, *resource.PlanChangeResponse)

ReadResponse *resource.ReadResponse
SchemaResponse *resource.SchemaResponse
UpdateResponse *resource.UpdateResponse
UpgradeStateResponse *resource.UpgradeStateResponse
ValidateConfigResponse *resource.ValidateConfigResponse
}

func (r Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
if r.CreateResponse != nil {
resp.Diagnostics = r.CreateResponse.Diagnostics
resp.NewState = r.CreateResponse.NewState
}
}

func (r Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
if r.DeleteResponse != nil {
resp.Diagnostics = r.DeleteResponse.Diagnostics
}
}

func (r Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
if r.ImportStateResponse != nil {
resp.Diagnostics = r.ImportStateResponse.Diagnostics
resp.State = r.ImportStateResponse.State
}
}

func (r Resource) PlanChange(ctx context.Context, req resource.PlanChangeRequest, resp *resource.PlanChangeResponse) {
if r.PlanChangeFunc != nil {
r.PlanChangeFunc(ctx, req, resp)
}
}

func (r Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
if r.ReadResponse != nil {
resp.Diagnostics = r.ReadResponse.Diagnostics
resp.NewState = r.ReadResponse.NewState
}
}

func (r Resource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
if r.SchemaResponse != nil {
resp.Diagnostics = r.SchemaResponse.Diagnostics
resp.Schema = r.SchemaResponse.Schema
}
}

func (r Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
if r.UpdateResponse != nil {
resp.Diagnostics = r.UpdateResponse.Diagnostics
resp.NewState = r.UpdateResponse.NewState
}
}

func (r Resource) UpgradeState(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) {
if r.UpgradeStateResponse != nil {
resp.Diagnostics = r.UpgradeStateResponse.Diagnostics
resp.UpgradedState = r.UpgradeStateResponse.UpgradedState
}
}

func (r Resource) ValidateConfig(ctx context.Context, req resource.ValidateConfigRequest, resp *resource.ValidateConfigResponse) {
if r.ValidateConfigResponse != nil {
resp.Diagnostics = r.ValidateConfigResponse.Diagnostics
}
}
41 changes: 41 additions & 0 deletions internal/testing/testsdk/datasource/datasource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package datasource

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tfprotov6"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

type DataSource interface {
Read(context.Context, ReadRequest, *ReadResponse)
Schema(context.Context, SchemaRequest, *SchemaResponse)
ValidateConfig(context.Context, ValidateConfigRequest, *ValidateConfigResponse)
}

type ReadRequest struct {
Config tftypes.Value
}

type ReadResponse struct {
Diagnostics []*tfprotov6.Diagnostic
State tftypes.Value
}

type SchemaRequest struct{}

type SchemaResponse struct {
Diagnostics []*tfprotov6.Diagnostic
Schema *tfprotov6.Schema
}

type ValidateConfigRequest struct {
Config tftypes.Value
}

type ValidateConfigResponse struct {
Diagnostics []*tfprotov6.Diagnostic
}

0 comments on commit ba04167

Please sign in to comment.