Skip to content

Commit 67467cd

Browse files
committed
add test that checks for orphan cassettes
1 parent 42afe41 commit 67467cd

File tree

2 files changed

+94
-12
lines changed

2 files changed

+94
-12
lines changed

internal/acctest/validate_cassettes_test.go

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,28 @@ package acctest_test
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
7+
"go/ast"
8+
"go/parser"
9+
"go/token"
610
"io/fs"
711
"net/http"
12+
"os"
813
"path/filepath"
14+
"sort"
915
"strings"
1016
"testing"
1117

18+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
1219
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/mnq"
1320
"github.com/stretchr/testify/assert"
1421
"github.com/stretchr/testify/require"
1522
"gopkg.in/dnaeon/go-vcr.v3/cassette"
1623
)
1724

25+
const servicesDir = "../services"
26+
1827
func exceptionsCassettesCases() map[string]struct{} {
1928
return map[string]struct{}{
2029
"../services/mnq/testdata/sns-topic-basic.cassette.yaml": {},
@@ -35,16 +44,17 @@ func exceptionsCassettesCases() map[string]struct{} {
3544
}
3645

3746
// getTestFiles returns a map of cassettes files
38-
func getTestFiles() (map[string]struct{}, error) {
47+
func getTestFiles(includeExceptions bool) (map[string]struct{}, error) {
3948
filesMap := make(map[string]struct{})
4049
exceptions := exceptionsCassettesCases()
4150

42-
err := filepath.WalkDir("../services", func(path string, _ fs.DirEntry, _ error) error {
51+
err := filepath.WalkDir(servicesDir, func(path string, _ fs.DirEntry, _ error) error {
4352
isCassette := strings.Contains(path, "cassette")
44-
_, isException := exceptions[path]
45-
46-
if isCassette && !isException {
47-
filesMap[fileNameWithoutExtSuffix(path)] = struct{}{}
53+
if isCassette {
54+
_, isException := exceptions[path]
55+
if !isException || includeExceptions {
56+
filesMap[fileNameWithoutExtSuffix(path)] = struct{}{}
57+
}
4858
}
4959

5060
return nil
@@ -57,7 +67,7 @@ func getTestFiles() (map[string]struct{}, error) {
5767
}
5868

5969
func TestAccCassettes_Validator(t *testing.T) {
60-
paths, err := getTestFiles()
70+
paths, err := getTestFiles(false)
6171
require.NoError(t, err)
6272

6373
for path := range paths {
@@ -129,3 +139,71 @@ func isTransientStateError(i *cassette.Interaction) bool {
129139

130140
return scwError.Type == "transient_state"
131141
}
142+
143+
func listAccTestFunctions() (map[string]string, error) {
144+
fset := token.NewFileSet()
145+
testFuncs := map[string]string{}
146+
147+
filepath.Walk(servicesDir, func(path string, info os.FileInfo, err error) error {
148+
if err != nil || info.IsDir() || !strings.HasSuffix(info.Name(), "_test.go") {
149+
return nil
150+
}
151+
152+
pkgFolder := filepath.Base(filepath.Dir(path))
153+
154+
node, err := parser.ParseFile(fset, path, nil, 0)
155+
if err != nil {
156+
return err
157+
}
158+
159+
for _, decl := range node.Decls {
160+
if fn, ok := decl.(*ast.FuncDecl); ok {
161+
if strings.HasPrefix(fn.Name.Name, "Test") && fn.Name.Name != "TestMain" && fn.Recv == nil {
162+
expectedCassettePath := fmt.Sprintf("%s/%s", servicesDir, acctest.BuildCassetteName(fn.Name.Name, pkgFolder, ".cassette"))
163+
testFuncs[expectedCassettePath] = fmt.Sprintf("%s/%s", pkgFolder, fn.Name.Name)
164+
}
165+
}
166+
}
167+
168+
return nil
169+
})
170+
171+
return testFuncs, nil
172+
}
173+
174+
func TestAccCassettes_CheckOrphans(t *testing.T) {
175+
// List actual cassettes
176+
actualCassettesPaths, err := getTestFiles(true)
177+
require.NoError(t, err)
178+
179+
// List actual acceptance tests functions and their expected cassettes' paths
180+
expectedCassettesPaths, err := listAccTestFunctions()
181+
if err != nil {
182+
t.Fatalf("Failed to list acceptance tests: %v", err)
183+
}
184+
185+
// Look for tests with no matching cassette
186+
testWithNoCassetteErrs := []string(nil)
187+
for expectedCassettePath, testName := range expectedCassettesPaths {
188+
if _, ok := actualCassettesPaths[expectedCassettePath]; !ok {
189+
testWithNoCassetteErrs = append(testWithNoCassetteErrs, fmt.Sprintf("- %s has no matching cassette", testName))
190+
}
191+
}
192+
sort.Strings(testWithNoCassetteErrs)
193+
t.Log("WARNING:\n", strings.Join(testWithNoCassetteErrs, "\n"))
194+
195+
// Look for cassettes with no matching test
196+
cassetteWithNoTestErrs := []error(nil)
197+
for actualCassettePath := range actualCassettesPaths {
198+
if _, ok := expectedCassettesPaths[actualCassettePath]; !ok {
199+
cassetteWithNoTestErrs = append(cassetteWithNoTestErrs, fmt.Errorf("+ cassette [%s] has no matching test", actualCassettePath))
200+
}
201+
}
202+
203+
if len(cassetteWithNoTestErrs) > 0 {
204+
sort.Slice(cassetteWithNoTestErrs, func(i, j int) bool {
205+
return cassetteWithNoTestErrs[i].Error() < cassetteWithNoTestErrs[j].Error()
206+
})
207+
t.Error(errors.Join(cassetteWithNoTestErrs...))
208+
}
209+
}

internal/acctest/vcr.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -62,14 +62,11 @@ func removeKeyRecursive(m map[string]any, key string) {
6262
}
6363
}
6464

65-
// getTestFilePath returns a valid filename path based on the go test name and suffix. (Take care of non fs friendly char)
66-
func getTestFilePath(t *testing.T, pkgFolder string, suffix string) string {
67-
t.Helper()
68-
65+
func BuildCassetteName(testName string, pkgFolder string, suffix string) string {
6966
specialChars := regexp.MustCompile(`[\\?%*:|"<>. ]`)
7067

7168
// Replace nested tests separators.
72-
fileName := strings.ReplaceAll(t.Name(), "/", "-")
69+
fileName := strings.ReplaceAll(testName, "/", "-")
7370

7471
fileName = strcase.ToBashArg(fileName)
7572

@@ -82,6 +79,13 @@ func getTestFilePath(t *testing.T, pkgFolder string, suffix string) string {
8279
return filepath.Join(pkgFolder, "testdata", fileName)
8380
}
8481

82+
// getTestFilePath returns a valid filename path based on the go test name and suffix. (Take care of non fs friendly char)
83+
func getTestFilePath(t *testing.T, pkgFolder string, suffix string) string {
84+
t.Helper()
85+
86+
return BuildCassetteName(t.Name(), pkgFolder, suffix)
87+
}
88+
8589
// cassetteMatcher is a custom matcher that will juste check equivalence of request bodies
8690
func cassetteBodyMatcher(request *http.Request, cassette cassette.Request) bool {
8791
if request.Body == nil || request.ContentLength == 0 {

0 commit comments

Comments
 (0)