Skip to content

Commit

Permalink
Support testing of configurations in JSON syntax. (#722)
Browse files Browse the repository at this point in the history
Reference: #721
  • Loading branch information
rudo-thomas committed Mar 11, 2022
1 parent 907e4a8 commit aa11e4c
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 10 deletions.
3 changes: 3 additions & 0 deletions helper/resource/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,9 @@ type TestStep struct {
// Config a string of the configuration to give to Terraform. If this
// is set, then the TestCase will execute this step with the same logic
// as a `terraform apply`.
//
// JSON Configuration Syntax can be used and is assumed whenever Config
// contains valid JSON.
Config string

// Check is called after the Config is applied. Use this step to
Expand Down
36 changes: 26 additions & 10 deletions internal/plugintest/working_dir.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package plugintest
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
Expand All @@ -15,8 +16,9 @@ import (
)

const (
ConfigFileName = "terraform_plugin_test.tf"
PlanFileName = "tfplan"
ConfigFileName = "terraform_plugin_test.tf"
ConfigFileNameJSON = ConfigFileName + ".json"
PlanFileName = "tfplan"
)

// WorkingDir represents a distinct working directory that can be used for
Expand All @@ -29,6 +31,10 @@ type WorkingDir struct {
// baseDir is the root of the working directory tree
baseDir string

// configFilename is the full filename where the latest configuration
// was stored; empty until SetConfig is called.
configFilename string

// baseArgs is arguments that should be appended to all commands
baseArgs []string

Expand Down Expand Up @@ -85,11 +91,20 @@ func (wd *WorkingDir) GetHelper() *Helper {
// Destroy to establish the configuration. Any previously-set configuration is
// discarded and any saved plan is cleared.
func (wd *WorkingDir) SetConfig(ctx context.Context, cfg string) error {
configFilename := filepath.Join(wd.baseDir, ConfigFileName)
err := ioutil.WriteFile(configFilename, []byte(cfg), 0700)
outFilename := filepath.Join(wd.baseDir, ConfigFileName)
rmFilename := filepath.Join(wd.baseDir, ConfigFileNameJSON)
bCfg := []byte(cfg)
if json.Valid(bCfg) {
outFilename, rmFilename = rmFilename, outFilename
}
if err := os.Remove(rmFilename); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("unable to remove %q: %w", rmFilename, err)
}
err := ioutil.WriteFile(outFilename, bCfg, 0700)
if err != nil {
return err
}
wd.configFilename = outFilename

var mismatch *tfexec.ErrVersionMismatch
err = wd.tf.SetDisablePluginTLS(true)
Expand Down Expand Up @@ -158,11 +173,16 @@ func (wd *WorkingDir) ClearPlan(ctx context.Context) error {
return nil
}

var errWorkingDirSetConfigNotCalled = fmt.Errorf("must call SetConfig before Init")

// Init runs "terraform init" for the given working directory, forcing Terraform
// to use the current version of the plugin under test.
func (wd *WorkingDir) Init(ctx context.Context) error {
if _, err := os.Stat(wd.configFilename()); err != nil {
return fmt.Errorf("must call SetConfig before Init")
if wd.configFilename == "" {
return errWorkingDirSetConfigNotCalled
}
if _, err := os.Stat(wd.configFilename); err != nil {
return errWorkingDirSetConfigNotCalled
}

logging.HelperResourceTrace(ctx, "Calling Terraform CLI init command")
Expand All @@ -174,10 +194,6 @@ func (wd *WorkingDir) Init(ctx context.Context) error {
return err
}

func (wd *WorkingDir) configFilename() string {
return filepath.Join(wd.baseDir, ConfigFileName)
}

func (wd *WorkingDir) planFilename() string {
return filepath.Join(wd.baseDir, PlanFileName)
}
Expand Down
69 changes: 69 additions & 0 deletions internal/plugintest/working_dir_json_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package plugintest_test

import (
"context"
"testing"

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

// TestJSONConfig verifies that TestStep.Config can contain JSON.
// This test also proves that when changing the HCL and JSON formats back and
// forth, the framework deletes the previous configuration file.
func TestJSONConfig(t *testing.T) {
providerFactories := map[string]func() (*schema.Provider, error){
"tst": func() (*schema.Provider, error) { return tstProvider(), nil }, //nolint:unparam // required signature
}
resource.Test(t, resource.TestCase{
IsUnitTest: true,
ProviderFactories: providerFactories,
Steps: []resource.TestStep{{
Config: `{"resource":{"tst_t":{"r1":{"s":"x1"}}}}`,
Check: resource.TestCheckResourceAttr("tst_t.r1", "s", "x1"),
}, {
Config: `resource "tst_t" "r1" { s = "x2" }`,
Check: resource.TestCheckResourceAttr("tst_t.r1", "s", "x2"),
}, {
Config: `{"resource":{"tst_t":{"r1":{"s":"x3"}}}}`,
Check: resource.TestCheckResourceAttr("tst_t.r1", "s", "x3"),
}},
})
}

func tstProvider() *schema.Provider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"tst_t": {
CreateContext: resourceTstTCreate,
ReadContext: resourceTstTRead,
UpdateContext: resourceTstTCreate, // Update is the same as Create
DeleteContext: resourceTstTDelete,
Schema: map[string]*schema.Schema{
"s": {
Type: schema.TypeString,
Required: true,
},
},
},
},
}
}

func resourceTstTCreate(ctx context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
d.SetId(d.Get("s").(string))
return nil
}

func resourceTstTRead(ctx context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
if err := d.Set("s", d.Id()); err != nil {
return diag.FromErr(err)
}
return nil
}

func resourceTstTDelete(ctx context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
d.SetId("")
return nil
}

0 comments on commit aa11e4c

Please sign in to comment.