From f0b520e1a6b3c117c9f43e5ba9a69e8acb80d597 Mon Sep 17 00:00:00 2001 From: momer Date: Tue, 14 May 2024 09:26:50 -0500 Subject: [PATCH] Add additional configuration to allow mock tests - responses from the API provider can be mocked, while we isolate and test terraform handling of expected responses --- .task/checksum/docs | 2 +- go.mod | 3 +- internal/provider/provider.go | 50 ++++++++++++++++++---- internal/space/data_source_list_test.go | 3 +- internal/space/data_source_test.go | 3 +- internal/space/space_test.go | 8 ++-- internal/test/resource.go | 55 ++++++++++++++++++++++--- main.go | 4 +- 8 files changed, 104 insertions(+), 24 deletions(-) diff --git a/.task/checksum/docs b/.task/checksum/docs index 2c2c351..147458f 100644 --- a/.task/checksum/docs +++ b/.task/checksum/docs @@ -1 +1 @@ -aca40009b238058ed9f39142c729980 +f7f04e4db1b11ccfe67e14765ab91cb3 diff --git a/go.mod b/go.mod index 64dd2fc..fd8f3ee 100644 --- a/go.mod +++ b/go.mod @@ -5,8 +5,10 @@ go 1.22 toolchain go1.22.3 require ( + github.com/go-chi/chi/v5 v5.0.12 github.com/hashicorp/terraform-plugin-docs v0.19.2 github.com/hashicorp/terraform-plugin-framework v1.8.0 + github.com/hashicorp/terraform-plugin-go v0.22.2 github.com/hashicorp/terraform-plugin-testing v1.7.0 github.com/omc/bonsai-api-go/v2 v2.1.0 github.com/stretchr/testify v1.9.0 @@ -46,7 +48,6 @@ require ( github.com/hashicorp/logutils v1.0.0 // indirect github.com/hashicorp/terraform-exec v0.20.0 // indirect github.com/hashicorp/terraform-json v0.21.0 // indirect - github.com/hashicorp/terraform-plugin-go v0.22.2 // indirect github.com/hashicorp/terraform-plugin-log v0.9.0 // indirect github.com/hashicorp/terraform-plugin-sdk/v2 v2.33.0 // indirect github.com/hashicorp/terraform-registry-address v0.2.3 // indirect diff --git a/internal/provider/provider.go b/internal/provider/provider.go index 62644cf..c72b2a1 100644 --- a/internal/provider/provider.go +++ b/internal/provider/provider.go @@ -7,9 +7,6 @@ import ( "context" "os" - "github.com/omc/bonsai-api-go/v2/bonsai" - "github.com/omc/terraform-provider-bonsai/internal/space" - "github.com/hashicorp/terraform-plugin-framework/datasource" "github.com/hashicorp/terraform-plugin-framework/function" "github.com/hashicorp/terraform-plugin-framework/path" @@ -17,6 +14,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/provider/schema" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/omc/bonsai-api-go/v2/bonsai" + "github.com/omc/terraform-provider-bonsai/internal/space" ) // Ensure bonsaiProvider satisfies various provider interfaces. @@ -29,6 +28,10 @@ type bonsaiProvider struct { // provider is built and ran locally, and "test" when running acceptance // testing. version string + // bonsaiAPIClient is an optional override of the API Client used by + // Terraform to perform requests. Defaults to nil, and will use a + // default provided API Client. + bonsaiAPIClient *bonsai.Client } // bonsaiProviderModel maps provider schema data to a Go type. @@ -88,11 +91,33 @@ func (p *bonsaiProvider) Functions(ctx context.Context) []func() function.Functi return []func() function.Function{} } -func New(version string) func() provider.Provider { +// BonsaiProviderOption is a functional option, used to configure Client. +type BonsaiProviderOption func(*bonsaiProvider) + +// WithAPIClient configures the Bonsai API Client used to perform +// terraform action HTTP requests. +func WithAPIClient(c *bonsai.Client) BonsaiProviderOption { + return func(p *bonsaiProvider) { + p.bonsaiAPIClient = c + } +} + +func WithVersion(version string) BonsaiProviderOption { + return func(p *bonsaiProvider) { + p.version = version + } +} + +func New(options ...BonsaiProviderOption) func() provider.Provider { return func() provider.Provider { - return &bonsaiProvider{ - version: version, + p := &bonsaiProvider{} + + // apply options + for _, option := range options { + option(p) } + + return p } } @@ -100,6 +125,15 @@ func (p *bonsaiProvider) Configure(ctx context.Context, req provider.ConfigureRe // Retrieve provider data from configuration var config bonsaiProviderModel + // Bonsai API Client has already been configured; skip all client configuration + if p.bonsaiAPIClient != nil { + // Make the Bonsai client available during DataSource and Resource + // type Configure methods. + resp.DataSourceData = p.bonsaiAPIClient + resp.ResourceData = p.bonsaiAPIClient + return + } + diags := req.Config.Get(ctx, &config) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { @@ -194,7 +228,7 @@ func (p *bonsaiProvider) Configure(ctx context.Context, req provider.ConfigureRe return } - // Create a new HashiCups client using the configuration values + // Create a new Bonsai client using the configuration values client := bonsai.NewClient( bonsai.WithCredentialPair( bonsai.CredentialPair{ @@ -204,7 +238,7 @@ func (p *bonsaiProvider) Configure(ctx context.Context, req provider.ConfigureRe ), ) - // Make the HashiCups client available during DataSource and Resource + // Make the Bonsai client available during DataSource and Resource // type Configure methods. resp.DataSourceData = client resp.ResourceData = client diff --git a/internal/space/data_source_list_test.go b/internal/space/data_source_list_test.go index 36598f8..71ed1b3 100644 --- a/internal/space/data_source_list_test.go +++ b/internal/space/data_source_list_test.go @@ -2,12 +2,11 @@ package space_test import ( "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/omc/terraform-provider-bonsai/internal/test" ) func (s *SpaceTestSuite) TestSpace_ListDataSource() { resource.Test(s.T(), resource.TestCase{ - ProtoV6ProviderFactories: test.ProtoV6ProviderFactories, + ProtoV6ProviderFactories: s.ProtoV6ProviderFactories, Steps: []resource.TestStep{ { Config: ` diff --git a/internal/space/data_source_test.go b/internal/space/data_source_test.go index c5d75e1..75d1ea0 100644 --- a/internal/space/data_source_test.go +++ b/internal/space/data_source_test.go @@ -4,13 +4,12 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/omc/terraform-provider-bonsai/internal/test" ) func (s *SpaceTestSuite) TestSpace_DataSource() { const spacePath = "omc/bonsai/eu-west-1/common" resource.Test(s.T(), resource.TestCase{ - ProtoV6ProviderFactories: test.ProtoV6ProviderFactories, + ProtoV6ProviderFactories: s.ProtoV6ProviderFactories, Steps: []resource.TestStep{ { Config: fmt.Sprintf(` diff --git a/internal/space/space_test.go b/internal/space/space_test.go index edaac45..463417c 100644 --- a/internal/space/space_test.go +++ b/internal/space/space_test.go @@ -8,13 +8,13 @@ import ( ) type SpaceTestSuite struct { - test.ProviderTestSuite + *test.ProviderTestSuite } func TestSpaceTestSuite(t *testing.T) { - suite.Run(t, &SpaceTestSuite{}) + suite.Run(t, &SpaceTestSuite{ProviderTestSuite: &test.ProviderTestSuite{}}) } -func (s *SpaceTestSuite) SetupTest() { - suite.SetupAllSuite(s).SetupSuite() +func (s *SpaceTestSuite) SetupSuite() { + suite.SetupAllSuite(s.ProviderTestSuite).SetupSuite() } diff --git a/internal/test/resource.go b/internal/test/resource.go index c62404d..e76226c 100644 --- a/internal/test/resource.go +++ b/internal/test/resource.go @@ -1,6 +1,9 @@ package test import ( + "net/http/httptest" + + "github.com/go-chi/chi/v5" "github.com/hashicorp/terraform-plugin-framework/providerserver" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/omc/bonsai-api-go/v2/bonsai" @@ -9,10 +12,6 @@ import ( "github.com/stretchr/testify/suite" ) -var ProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ - "bonsai": providerserver.NewProtocol6WithError(provider.New("test")()), -} - type ClientTestSuite struct { // Assertions embedded here allows all tests to reach through the suite to access assertion methods *require.Assertions @@ -21,6 +20,8 @@ type ClientTestSuite struct { // client allows each test to have a reachable *bonsai.Client for testing client *bonsai.Client + + ProtoV6ProviderFactories map[string]func() (tfprotov6.ProviderServer, error) } // ProviderTestSuite is used for all provider acceptance tests. @@ -29,11 +30,42 @@ type ProviderTestSuite struct { } func (s *ProviderTestSuite) SetupSuite() { + version := "0.1.0-test" + + // configure terraform provider factory + s.ProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "bonsai": providerserver.NewProtocol6WithError( + provider.New( + provider.WithVersion(version), + )(), + ), + } + + // configure testify + s.Assertions = require.New(s.T()) +} + +// ProviderMockRequestTestSuite is used for tests where the target endpoints +// are mocked; allowing for isolating the terraform provider functionality, +// without requiring live responses from the production Bonsai API. +type ProviderMockRequestTestSuite struct { + ClientTestSuite + serveMux *chi.Mux + server *httptest.Server +} + +func (s *ProviderMockRequestTestSuite) SetupSuite() { + version := "0.1.0-test" + + // Configure http client and other miscellany + s.serveMux = chi.NewRouter() + s.server = httptest.NewServer(s.serveMux) s.client = bonsai.NewClient( + bonsai.WithEndpoint(s.server.URL), bonsai.WithApplication( bonsai.Application{ Name: "terraform-provider-bonsai", - Version: "0.1.0-dev", + Version: version, }, ), bonsai.WithCredentialPair( @@ -43,6 +75,19 @@ func (s *ProviderTestSuite) SetupSuite() { }, ), ) + + // configure terraform provider factory + s.ProtoV6ProviderFactories = map[string]func() (tfprotov6.ProviderServer, error){ + "bonsai": providerserver.NewProtocol6WithError( + provider.New( + provider.WithAPIClient( + s.client, + ), + provider.WithVersion(version), + )(), + ), + } + // configure testify s.Assertions = require.New(s.T()) } diff --git a/main.go b/main.go index 951d649..e101a7c 100644 --- a/main.go +++ b/main.go @@ -46,7 +46,9 @@ func main() { Debug: debug, } - err := providerserver.Serve(context.Background(), provider.New(version), opts) + err := providerserver.Serve(context.Background(), provider.New( + provider.WithVersion(version), + ), opts) if err != nil { log.Fatal(err.Error())