Skip to content

Commit b250c6d

Browse files
committed
chore: implement e2e test mvp
Foundation to consistently verify against `terraform` cli proper
1 parent 4dbb0e9 commit b250c6d

File tree

7 files changed

+166
-118
lines changed

7 files changed

+166
-118
lines changed

internal/verify/cmp.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,18 @@ import (
1313
)
1414

1515
func Compare(t *testing.T, pr *preview.Output, values *tfjson.StateModule) {
16+
passed := CompareParameters(t, pr, values)
17+
18+
// TODO: Compare workspace tags
19+
20+
if !passed {
21+
t.Fatalf("paramaters failed expectations")
22+
}
23+
}
24+
25+
func CompareParameters(t *testing.T, pr *preview.Output, values *tfjson.StateModule) bool {
26+
t.Helper()
27+
1628
// Assert expected parameters
1729
stateParams, err := extract.ParametersFromState(values)
1830
require.NoError(t, err, "extract parameters from state")
@@ -26,7 +38,5 @@ func Compare(t *testing.T, pr *preview.Output, values *tfjson.StateModule) {
2638
assert.Equal(t, param, pr.Parameters[i], "parameter %q %d", param.BlockName, i)
2739
}
2840

29-
if !passed {
30-
t.Fatalf("paramaters failed expectations")
31-
}
41+
return passed
3242
}

preview_test.go

Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -2,135 +2,20 @@ package preview_test
22

33
import (
44
"context"
5-
"encoding/json"
65
"fmt"
7-
"io/fs"
86
"os"
97
"path/filepath"
108
"testing"
11-
"time"
129

1310
"github.com/stretchr/testify/assert"
1411
"github.com/stretchr/testify/require"
1512
"github.com/zclconf/go-cty/cty"
1613
"github.com/zclconf/go-cty/cty/gocty"
17-
"golang.org/x/exp/slices"
1814

1915
"github.com/coder/preview"
20-
"github.com/coder/preview/internal/verify"
2116
"github.com/coder/preview/types"
2217
)
2318

24-
// Test_VerifyPreview will fully evaluate with `terraform apply`
25-
// and verify the output of `preview` against the tfstate. This
26-
// is the e2e test for the preview package.
27-
func Test_VerifyPreview(t *testing.T) {
28-
t.Parallel()
29-
30-
installCtx, cancel := context.WithCancel(context.Background())
31-
32-
versions := verify.TerraformTestVersions(installCtx)
33-
tfexecs := verify.InstallTerraforms(installCtx, t, versions...)
34-
cancel()
35-
36-
dirFs := os.DirFS("testdata")
37-
entries, err := fs.ReadDir(dirFs, ".")
38-
require.NoError(t, err)
39-
40-
for _, entry := range entries {
41-
entry := entry
42-
if !entry.IsDir() {
43-
t.Logf("skipping non directory file %q", entry.Name())
44-
continue
45-
}
46-
47-
entryFiles, err := fs.ReadDir(dirFs, filepath.Join(entry.Name()))
48-
require.NoError(t, err, "reading test data dir")
49-
if !slices.ContainsFunc(entryFiles, func(entry fs.DirEntry) bool {
50-
return filepath.Ext(entry.Name()) == ".tf"
51-
}) {
52-
t.Logf("skipping test data dir %q, no .tf files", entry.Name())
53-
continue
54-
}
55-
56-
if slices.ContainsFunc(entryFiles, func(entry fs.DirEntry) bool {
57-
return entry.Name() == "skip"
58-
}) {
59-
t.Logf("skipping test data dir %q, skip file found", entry.Name())
60-
continue
61-
}
62-
63-
name := entry.Name()
64-
t.Run(name, func(t *testing.T) {
65-
t.Parallel()
66-
67-
entryWrkPath := t.TempDir()
68-
69-
for _, tfexec := range tfexecs {
70-
tfexec := tfexec
71-
72-
t.Run(tfexec.Version, func(t *testing.T) {
73-
wp := filepath.Join(entryWrkPath, tfexec.Version)
74-
err := os.MkdirAll(wp, 0755)
75-
require.NoError(t, err, "creating working dir")
76-
77-
t.Logf("working dir %q", wp)
78-
79-
subFS, err := fs.Sub(dirFs, entry.Name())
80-
require.NoError(t, err, "creating sub fs")
81-
82-
err = verify.CopyTFFS(wp, subFS)
83-
require.NoError(t, err, "copying test data to working dir")
84-
85-
exe, err := tfexec.WorkingDir(wp)
86-
require.NoError(t, err, "creating working executable")
87-
88-
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*2)
89-
defer cancel()
90-
err = exe.Init(ctx)
91-
require.NoError(t, err, "terraform init")
92-
93-
planOutFile := "tfplan"
94-
planOutPath := filepath.Join(wp, planOutFile)
95-
_, err = exe.Plan(ctx, planOutPath)
96-
require.NoError(t, err, "terraform plan")
97-
98-
plan, err := exe.ShowPlan(ctx, planOutPath)
99-
require.NoError(t, err, "terraform show plan")
100-
101-
pd, err := json.Marshal(plan)
102-
require.NoError(t, err, "marshalling plan")
103-
104-
err = os.WriteFile(filepath.Join(wp, "plan.json"), pd, 0644)
105-
require.NoError(t, err, "writing plan.json")
106-
107-
_, err = exe.Apply(ctx)
108-
require.NoError(t, err, "terraform apply")
109-
110-
state, err := exe.Show(ctx)
111-
require.NoError(t, err, "terraform show")
112-
113-
output, diags := preview.Preview(context.Background(),
114-
preview.Input{
115-
PlanJSONPath: "plan.json",
116-
ParameterValues: map[string]types.ParameterValue{},
117-
},
118-
os.DirFS(wp))
119-
if diags.HasErrors() {
120-
t.Logf("diags: %s", diags)
121-
}
122-
require.False(t, diags.HasErrors(), "preview errors")
123-
124-
if state.Values == nil {
125-
t.Fatalf("state values are nil")
126-
}
127-
verify.Compare(t, output, state.Values.RootModule)
128-
})
129-
}
130-
})
131-
}
132-
}
133-
13419
func TestFoo(t *testing.T) {
13520
ty, err := gocty.ImpliedType([]any{1, 2, 3})
13621
require.NoError(t, err)

previewe2e_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
package preview_test
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"io/fs"
7+
"os"
8+
"path/filepath"
9+
"slices"
10+
"testing"
11+
"time"
12+
13+
"github.com/stretchr/testify/require"
14+
15+
"github.com/coder/preview"
16+
"github.com/coder/preview/internal/verify"
17+
"github.com/coder/preview/types"
18+
)
19+
20+
// Test_VerifyE2E will fully evaluate with `terraform apply`
21+
// and verify the output of `preview` against the tfstate. This
22+
// is the e2e test for the preview package.
23+
//
24+
// 1. Terraform versions listed from 'verify.TerraformTestVersions' are
25+
// installed into a temp directory. If multiple versions exist, a e2e test
26+
// for each tf version is run.
27+
// 2. For each test directory in `testdata`, the following steps are performed:
28+
// a. If the directory contains a file named `skipe2e`, skip the test.
29+
// Some tests are not meant to be run in e2e mode as they require external
30+
// credentials, or are invalid terraform configurations.
31+
// b. For each terraform version, the following steps are performed:
32+
// ---- Core Test ----
33+
// i. Create a working directory for the terraform version in a temp dir.
34+
// ii. Copy the test data into the working directory.
35+
// iii. Run `terraform init`.
36+
// iv. Run `terraform plan` and save the output to a file.
37+
// v. Run `terraform apply`.
38+
// vi. Run `terraform show` to get the state.
39+
// vii. Run `preview --plan=out.plan` to get the preview state.
40+
// viii. Compare the preview state with the terraform state.
41+
// ix. If the preview state is not equal to the terraform state, fail the test.
42+
// ---- ----
43+
//
44+
// The goal of the test is to compare `tfstate` with the output of `preview`.
45+
// If `preview`'s implementation of terraform is incorrect, the test will fail.
46+
// TODO: Adding varied parameter inputs would be a good idea.
47+
func Test_VerifyE2E(t *testing.T) {
48+
t.Parallel()
49+
50+
installCtx, cancel := context.WithCancel(context.Background())
51+
52+
versions := verify.TerraformTestVersions(installCtx)
53+
tfexecs := verify.InstallTerraforms(installCtx, t, versions...)
54+
cancel()
55+
56+
dirFs := os.DirFS("testdata")
57+
entries, err := fs.ReadDir(dirFs, ".")
58+
require.NoError(t, err)
59+
60+
for _, entry := range entries {
61+
entry := entry
62+
if !entry.IsDir() {
63+
t.Logf("skipping non directory file %q", entry.Name())
64+
continue
65+
}
66+
67+
entryFiles, err := fs.ReadDir(dirFs, filepath.Join(entry.Name()))
68+
require.NoError(t, err, "reading test data dir")
69+
if !slices.ContainsFunc(entryFiles, func(entry fs.DirEntry) bool {
70+
return filepath.Ext(entry.Name()) == ".tf"
71+
}) {
72+
t.Logf("skipping test data dir %q, no .tf files", entry.Name())
73+
continue
74+
}
75+
76+
if slices.ContainsFunc(entryFiles, func(entry fs.DirEntry) bool {
77+
return entry.Name() == "skipe2e"
78+
}) {
79+
t.Logf("skipping test data dir %q, skip file found", entry.Name())
80+
continue
81+
}
82+
83+
name := entry.Name()
84+
t.Run(name, func(t *testing.T) {
85+
t.Parallel()
86+
87+
entryWrkPath := t.TempDir()
88+
89+
for _, tfexec := range tfexecs {
90+
tfexec := tfexec
91+
92+
t.Run(tfexec.Version, func(t *testing.T) {
93+
wp := filepath.Join(entryWrkPath, tfexec.Version)
94+
err := os.MkdirAll(wp, 0755)
95+
require.NoError(t, err, "creating working dir")
96+
97+
t.Logf("working dir %q", wp)
98+
99+
subFS, err := fs.Sub(dirFs, entry.Name())
100+
require.NoError(t, err, "creating sub fs")
101+
102+
err = verify.CopyTFFS(wp, subFS)
103+
require.NoError(t, err, "copying test data to working dir")
104+
105+
exe, err := tfexec.WorkingDir(wp)
106+
require.NoError(t, err, "creating working executable")
107+
108+
ctx, cancel := context.WithTimeout(context.Background(), time.Minute*2)
109+
defer cancel()
110+
err = exe.Init(ctx)
111+
require.NoError(t, err, "terraform init")
112+
113+
planOutFile := "tfplan"
114+
planOutPath := filepath.Join(wp, planOutFile)
115+
_, err = exe.Plan(ctx, planOutPath)
116+
require.NoError(t, err, "terraform plan")
117+
118+
plan, err := exe.ShowPlan(ctx, planOutPath)
119+
require.NoError(t, err, "terraform show plan")
120+
121+
pd, err := json.Marshal(plan)
122+
require.NoError(t, err, "marshalling plan")
123+
124+
err = os.WriteFile(filepath.Join(wp, "plan.json"), pd, 0644)
125+
require.NoError(t, err, "writing plan.json")
126+
127+
_, err = exe.Apply(ctx)
128+
require.NoError(t, err, "terraform apply")
129+
130+
state, err := exe.Show(ctx)
131+
require.NoError(t, err, "terraform show")
132+
133+
output, diags := preview.Preview(context.Background(),
134+
preview.Input{
135+
PlanJSONPath: "plan.json",
136+
ParameterValues: map[string]types.ParameterValue{},
137+
},
138+
os.DirFS(wp))
139+
if diags.HasErrors() {
140+
t.Logf("diags: %s", diags)
141+
}
142+
require.False(t, diags.HasErrors(), "preview errors")
143+
144+
if state.Values == nil {
145+
t.Fatalf("state values are nil")
146+
}
147+
verify.Compare(t, output, state.Values.RootModule)
148+
})
149+
}
150+
})
151+
}
152+
}

testdata/README.md

Whitespace-only changes.
File renamed without changes.
File renamed without changes.

testdata/nulldefault/skipe2e

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
No terraform to run.

0 commit comments

Comments
 (0)