@@ -2,19 +2,27 @@ package acctest_test
2
2
3
3
import (
4
4
"encoding/json"
5
+ "errors"
5
6
"fmt"
7
+ "go/ast"
8
+ "go/parser"
9
+ "go/token"
6
10
"io/fs"
7
11
"net/http"
8
12
"path/filepath"
13
+ "sort"
9
14
"strings"
10
15
"testing"
11
16
17
+ "github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
12
18
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/mnq"
13
19
"github.com/stretchr/testify/assert"
14
20
"github.com/stretchr/testify/require"
15
21
"gopkg.in/dnaeon/go-vcr.v3/cassette"
16
22
)
17
23
24
+ const servicesDir = "../services"
25
+
18
26
func exceptionsCassettesCases () map [string ]struct {} {
19
27
return map [string ]struct {}{
20
28
"../services/mnq/testdata/sns-topic-basic.cassette.yaml" : {},
@@ -35,16 +43,16 @@ func exceptionsCassettesCases() map[string]struct{} {
35
43
}
36
44
37
45
// getTestFiles returns a map of cassettes files
38
- func getTestFiles () (map [string ]struct {}, error ) {
46
+ func getTestFiles (includeExceptions bool ) (map [string ]struct {}, error ) {
39
47
filesMap := make (map [string ]struct {})
40
48
exceptions := exceptionsCassettesCases ()
41
49
42
- err := filepath .WalkDir ("../services" , func (path string , _ fs.DirEntry , _ error ) error {
43
- isCassette := strings .Contains (path , "cassette" )
44
- _ , isException := exceptions [path ]
45
-
46
- if isCassette && ! isException {
47
- filesMap [ fileNameWithoutExtSuffix ( path )] = struct {}{ }
50
+ err := filepath .WalkDir (servicesDir , func (path string , _ fs.DirEntry , _ error ) error {
51
+ if isCassette := strings .Contains (path , "cassette" ); isCassette {
52
+ _ , isException := exceptions [path ]
53
+ if ! isException || includeExceptions {
54
+ filesMap [ fileNameWithoutExtSuffix ( path )] = struct {}{}
55
+ }
48
56
}
49
57
50
58
return nil
@@ -57,7 +65,7 @@ func getTestFiles() (map[string]struct{}, error) {
57
65
}
58
66
59
67
func TestAccCassettes_Validator (t * testing.T ) {
60
- paths , err := getTestFiles ()
68
+ paths , err := getTestFiles (false )
61
69
require .NoError (t , err )
62
70
63
71
for path := range paths {
@@ -129,3 +137,76 @@ func isTransientStateError(i *cassette.Interaction) bool {
129
137
130
138
return scwError .Type == "transient_state"
131
139
}
140
+
141
+ func listAccTestFunctions () (map [string ]string , error ) {
142
+ fset := token .NewFileSet ()
143
+ testFuncs := map [string ]string {}
144
+
145
+ err := filepath .WalkDir (servicesDir , func (path string , _ fs.DirEntry , _ error ) error {
146
+ if strings .HasSuffix (path , "_test.go" ) {
147
+ pkgFolder := filepath .Base (filepath .Dir (path ))
148
+
149
+ node , err := parser .ParseFile (fset , path , nil , 0 )
150
+ if err != nil {
151
+ return err
152
+ }
153
+
154
+ for _ , decl := range node .Decls {
155
+ if fn , ok := decl .(* ast.FuncDecl ); ok {
156
+ if strings .HasPrefix (fn .Name .Name , "Test" ) && fn .Name .Name != "TestMain" && fn .Recv == nil {
157
+ expectedCassettePath := fmt .Sprintf ("%s/%s" , servicesDir , acctest .BuildCassetteName (fn .Name .Name , pkgFolder , ".cassette" ))
158
+ testFuncs [expectedCassettePath ] = fmt .Sprintf ("%s/%s" , pkgFolder , fn .Name .Name )
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ return nil
165
+ })
166
+
167
+ return testFuncs , err
168
+ }
169
+
170
+ func TestAccCassettes_CheckOrphans (t * testing.T ) {
171
+ // List actual cassettes
172
+ actualCassettesPaths , err := getTestFiles (true )
173
+ if err != nil {
174
+ t .Fatalf ("Failed to list cassettes: %v" , err )
175
+ }
176
+
177
+ // List actual acceptance tests functions and their expected cassettes' paths
178
+ expectedCassettesPaths , err := listAccTestFunctions ()
179
+ if err != nil {
180
+ t .Fatalf ("Failed to list acceptance tests: %v" , err )
181
+ }
182
+
183
+ testWithNoCassetteErrs := []string (nil )
184
+ cassetteWithNoTestErrs := []error (nil )
185
+
186
+ // Look for tests with no matching cassette
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
+
193
+ // Look for cassettes with no matching test
194
+ for actualCassettePath := range actualCassettesPaths {
195
+ if _ , ok := expectedCassettesPaths [actualCassettePath ]; ! ok {
196
+ cassetteWithNoTestErrs = append (cassetteWithNoTestErrs , fmt .Errorf ("+ cassette [%s] has no matching test" , actualCassettePath ))
197
+ }
198
+ }
199
+
200
+ // Print results:
201
+ // If a cassette has no test, it should result in an error, but if a test has no cassette, it should only result in
202
+ // a warning (e.g. for tests that are currently skipped and which cassette had to be removed because of a 500, or else)
203
+ sort .Strings (testWithNoCassetteErrs )
204
+ t .Log ("WARNING:\n " , strings .Join (testWithNoCassetteErrs , "\n " ))
205
+
206
+ if len (cassetteWithNoTestErrs ) > 0 {
207
+ sort .Slice (cassetteWithNoTestErrs , func (i , j int ) bool {
208
+ return cassetteWithNoTestErrs [i ].Error () < cassetteWithNoTestErrs [j ].Error ()
209
+ })
210
+ t .Error (errors .Join (cassetteWithNoTestErrs ... ))
211
+ }
212
+ }
0 commit comments