@@ -2,19 +2,27 @@ package acctest_test
22
33import (
44 "encoding/json"
5+ "errors"
56 "fmt"
7+ "go/ast"
8+ "go/parser"
9+ "go/token"
610 "io/fs"
711 "net/http"
812 "path/filepath"
13+ "sort"
914 "strings"
1015 "testing"
1116
17+ "github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
1218 "github.com/scaleway/terraform-provider-scaleway/v2/internal/services/mnq"
1319 "github.com/stretchr/testify/assert"
1420 "github.com/stretchr/testify/require"
1521 "gopkg.in/dnaeon/go-vcr.v3/cassette"
1622)
1723
24+ const servicesDir = "../services"
25+
1826func exceptionsCassettesCases () map [string ]struct {} {
1927 return map [string ]struct {}{
2028 "../services/mnq/testdata/sns-topic-basic.cassette.yaml" : {},
@@ -35,16 +43,16 @@ func exceptionsCassettesCases() map[string]struct{} {
3543}
3644
3745// getTestFiles returns a map of cassettes files
38- func getTestFiles () (map [string ]struct {}, error ) {
46+ func getTestFiles (includeExceptions bool ) (map [string ]struct {}, error ) {
3947 filesMap := make (map [string ]struct {})
4048 exceptions := exceptionsCassettesCases ()
4149
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+ }
4856 }
4957
5058 return nil
@@ -57,7 +65,7 @@ func getTestFiles() (map[string]struct{}, error) {
5765}
5866
5967func TestAccCassettes_Validator (t * testing.T ) {
60- paths , err := getTestFiles ()
68+ paths , err := getTestFiles (false )
6169 require .NoError (t , err )
6270
6371 for path := range paths {
@@ -129,3 +137,76 @@ func isTransientStateError(i *cassette.Interaction) bool {
129137
130138 return scwError .Type == "transient_state"
131139}
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