Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable e2e testing against custom Terraform executable #305

Merged
merged 4 commits into from Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Expand Up @@ -38,6 +38,15 @@ Command options are implemented using the functional variadic options pattern. F

We aim for full test coverage of all Terraform CLI commands implemented in `tfexec`, with as many combinations of command-line options as possible. New command implementations will not be merged without both unit and end-to-end tests.

### Environment variables

The following environment variables can be set during testing:

- `TFEXEC_E2ETEST_VERSIONS`: When set to a comma-separated list of version strings, this overrides the default list of Terraform versions for end-to-end tests, and runs those tests against only the versions supplied.
- `TFEXEC_E2ETEST_TERRAFORM_PATH`: When set to the path of a valid local Terraform executable, only tests appropriate to that executable's version are run. No other versions are downloaded or run. Note that this means that tests using `runTestVersions()` will only run if the test version matches the local executable exactly.

If both of these environment variables are set, `TFEXEC_E2ETEST_TERRAFORM_PATH` takes precedence, and any other versions specified in `TFEXEC_E2ETEST_VERSIONS` are ignored.

### Unit tests

Unit tests live alongside command implementations in `tfexec/`. A unit test asserts that the *string* version of the `exec.Cmd` returned by the `*Cmd` function (e.g. `refreshCmd`) is as expected. Minimally, commands must be tested with no options passed ("defaults"), and with all options set to non-default values. The `assertCmd()` helper can be used for this purpose. Please see `tfexec/init_test.go` for a reasonable starting point.
Expand Down
11 changes: 11 additions & 0 deletions README.md
Expand Up @@ -71,6 +71,17 @@ func main() {
}
```

## Testing Terraform binaries

The terraform-exec test suite contains end-to-end tests which run realistic workflows against a real Terraform binary using `tfexec.Terraform{}`.

To run these tests with a local Terraform binary, set the environment variable `TFEXEC_E2ETEST_TERRAFORM_PATH` to its path and run:
```sh
go test -timeout=20m ./tfexec/internal/e2etest
```

For more information on terraform-exec's test suite, please see Contributing below.

## Contributing

Please see [CONTRIBUTING.md](./CONTRIBUTING.md).
52 changes: 48 additions & 4 deletions tfexec/internal/e2etest/util_test.go
Expand Up @@ -41,6 +41,37 @@ func runTest(t *testing.T, fixtureName string, cb func(t *testing.T, tfVersion *
versions = strings.Split(override, ",")
}

// If the env var TFEXEC_E2ETEST_TERRAFORM_PATH is set to the path of a
// valid Terraform executable, only tests appropriate to that
// executable's version will be run.
if localBinPath := os.Getenv("TFEXEC_E2ETEST_TERRAFORM_PATH"); localBinPath != "" {
// By convention, every new Terraform struct is given a clean
// temp dir, even if we are only invoking tf.Version(). This
// prevents any possible confusion that could result from
// reusing an os.TempDir() (for example) that already contained
// Terraform files.
td, err := ioutil.TempDir("", "tf")
if err != nil {
t.Fatalf("error creating temporary test directory: %s", err)
}
t.Cleanup(func() {
os.RemoveAll(td)
})
kmoe marked this conversation as resolved.
Show resolved Hide resolved
ltf, err := tfexec.NewTerraform(td, localBinPath)
if err != nil {
t.Fatal(err)
}

ltf.SetAppendUserAgent("tfexec-e2etest")

lVersion, _, err := ltf.Version(context.Background(), false)
if err != nil {
t.Fatalf("unable to determine version of Terraform binary at %s: %s", localBinPath, err)
}

versions = []string{lVersion.String()}
}

runTestVersions(t, versions, fixtureName, cb)
}

Expand All @@ -63,12 +94,12 @@ func runTestVersions(t *testing.T, versions []string, fixtureName string, cb fun
os.RemoveAll(td)
})

// TODO: do this in a cleaner way than string comparison?
var execPath string
switch {
case strings.HasPrefix(tfv, "refs/"):
if localBinPath := os.Getenv("TFEXEC_E2ETEST_TERRAFORM_PATH"); localBinPath != "" {
execPath = localBinPath
} else if strings.HasPrefix(tfv, "refs/") {
kmoe marked this conversation as resolved.
Show resolved Hide resolved
execPath = tfcache.GitRef(t, tfv)
default:
} else {
execPath = tfcache.Version(t, tfv)
}

Expand All @@ -84,6 +115,19 @@ func runTestVersions(t *testing.T, versions []string, fixtureName string, cb fun
t.Fatalf("unable to determine running version (expected %q): %s", tfv, err)
}

// Check that the runningVersion matches the expected
// test version. This ensures non-matching tests are
// skipped when using a local Terraform executable.
if !strings.HasPrefix(tfv, "refs/") {
testVersion, err := version.NewVersion(tfv)
if err != nil {
t.Fatalf("unable to parse version %s: %s", testVersion, err)
}
if !testVersion.Equal(runningVersion) {
t.Skipf("test applies to version %s, but local executable is version %s", tfv, runningVersion)
}
}

if fixtureName != "" {
err = copyFiles(filepath.Join(testFixtureDir, fixtureName), td)
if err != nil {
Expand Down