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

feat: Introduce simple arch tests #2210

Merged
merged 32 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
4927c2a
Add naive file getting
sfc-gh-asawicki Nov 22, 2023
434b206
Test naively (resources)
sfc-gh-asawicki Nov 22, 2023
3391f46
Split into files
sfc-gh-asawicki Nov 22, 2023
267de70
Add assertion for non-acceptance tests
sfc-gh-asawicki Nov 22, 2023
2d0ffb6
Reorganize
sfc-gh-asawicki Nov 22, 2023
7575186
Extract files
sfc-gh-asawicki Nov 22, 2023
fe584a0
Extract directory
sfc-gh-asawicki Nov 22, 2023
1219468
Extract method
sfc-gh-asawicki Nov 22, 2023
8e67814
Rename to archtest
sfc-gh-asawicki Nov 22, 2023
8d6d8c8
Split lib and our usage into 2 different packages
sfc-gh-asawicki Nov 22, 2023
8cf98a4
Introduce datasources tests
sfc-gh-asawicki Nov 22, 2023
ed3b744
Start testing archtest
sfc-gh-asawicki Nov 22, 2023
55f5d56
Parametrize directories test
sfc-gh-asawicki Nov 22, 2023
ca9a89b
Change API
sfc-gh-asawicki Nov 23, 2023
b3610cc
Rename to architest
sfc-gh-asawicki Nov 23, 2023
7c3ef6b
Add nicer assertions
sfc-gh-asawicki Nov 23, 2023
7c7b504
Add file to method
sfc-gh-asawicki Nov 23, 2023
5ef0cf0
Rename to exported methods
sfc-gh-asawicki Nov 23, 2023
94fc50b
Test file
sfc-gh-asawicki Nov 23, 2023
6204e8a
Test file filtering
sfc-gh-asawicki Nov 23, 2023
d738901
Add tests for package assertions
sfc-gh-asawicki Nov 23, 2023
b541e59
Test rest of the assertions
sfc-gh-asawicki Nov 23, 2023
265e73a
Test invocation on every file and method
sfc-gh-asawicki Nov 23, 2023
68531d8
Test creation of directory and file
sfc-gh-asawicki Nov 23, 2023
83d1875
Add tests for sdk integration tests
sfc-gh-asawicki Nov 23, 2023
bd33dd5
Add tests integration tests matchers
sfc-gh-asawicki Nov 23, 2023
dd5e885
Fix lint and stuff
sfc-gh-asawicki Nov 23, 2023
ff5f5b7
Add sample usage
sfc-gh-asawicki Nov 23, 2023
a68b35d
Fix test setup
sfc-gh-asawicki Nov 23, 2023
2a5bed9
Fix after review
sfc-gh-asawicki Nov 28, 2023
fd56b13
Introduce FilesProvider
sfc-gh-asawicki Nov 28, 2023
176a619
Add recipe
sfc-gh-asawicki Nov 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,18 @@ install: ## install the binary
go install -v ./...

lint: # Run static code analysis, check formatting. See https://golangci-lint.run/
golangci-lint run ./... -v
./bin/golangci-lint run ./... -v

lint-fix: ## Run static code analysis, check formatting and try to fix findings
golangci-lint run ./... -v --fix
./bin/golangci-lint run ./... -v --fix

mod: ## add missing and remove unused modules
go mod tidy -compat=1.20

mod-check: mod ## check if there are any missing/unused modules
git diff --exit-code -- go.mod go.sum

pre-push: fmt docs mod lint ## Run a few checks before pushing a change (docs, fmt, mod, etc.)
pre-push: fmt docs mod lint test-architecture ## Run a few checks before pushing a change (docs, fmt, mod, etc.)

pre-push-check: fmt-check docs-check lint-check mod-check ## Run a few checks before pushing a change (docs, fmt, mod, etc.)

Expand All @@ -68,6 +68,9 @@ test: ## run unit and integration tests
test-acceptance: ## run acceptance tests
TF_ACC=1 go test -run "^TestAcc_" -v -cover -timeout=30m ./...

test-architecture: ## check architecture constraints between packages
go test ./pkg/architests/... -v

build-local: ## build the binary locally
go build -o $(BASE_BINARY_NAME) .

Expand Down
357 changes: 357 additions & 0 deletions pkg/architest/architest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
package architest_test

import (
"fmt"
"regexp"
"testing"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/architest"
"github.com/stretchr/testify/assert"
)

func Test_Directory(t *testing.T) {
t.Run("fail to use non-existing directory", func(t *testing.T) {
assert.Panics(t, func() {
architest.Directory("testdata/non_existing")
})
})

t.Run("use directory", func(t *testing.T) {
assert.NotPanics(t, func() {
architest.Directory("testdata/dir1")
})
})

tests1 := []struct {
directory string
expectedFileNames []string
expectedPackageNames []string
}{
{directory: "testdata/dir1", expectedFileNames: []string{"testdata/dir1/sample1.go", "testdata/dir1/sample2.go", "testdata/dir1/different1.go"}, expectedPackageNames: []string{"dir1"}},
{directory: "testdata/dir2", expectedFileNames: []string{"testdata/dir2/sample1.go", "testdata/dir2/sample1_test.go"}, expectedPackageNames: []string{"dir2", "dir2_test"}},
{directory: "testdata/dir3", expectedFileNames: []string{"testdata/dir3/sample1.go", "testdata/dir3/sample1_acceptance_test.go"}, expectedPackageNames: []string{"dir3", "dir3_test"}},
{directory: "testdata/dir4", expectedFileNames: []string{"testdata/dir4/sample1.go", "testdata/dir4/sample1_integration_test.go"}, expectedPackageNames: []string{"dir4", "dir4_test"}},
}
for _, tt := range tests1 {
t.Run("list all files in the given directory", func(t *testing.T) {
dir := architest.Directory(tt.directory)

allFiles := dir.AllFiles()
assert.Len(t, allFiles, len(tt.expectedFileNames))

fileNames := make([]string, 0, len(allFiles))
for _, f := range allFiles {
fileNames = append(fileNames, f.Name())
}
assert.ElementsMatch(t, fileNames, tt.expectedFileNames)

packageNames := make(map[string]bool)
for _, f := range allFiles {
packageNames[f.PackageName()] = true
}
assert.Len(t, packageNames, len(tt.expectedPackageNames))
for _, name := range tt.expectedPackageNames {
assert.Contains(t, packageNames, name)
}
})
}

tests2 := []struct {
directory string
filter architest.FileFilter
expectedFileNames []string
}{
{directory: "testdata/dir1", filter: architest.FileNameFilterProvider("sample"), expectedFileNames: []string{"testdata/dir1/sample1.go", "testdata/dir1/sample2.go"}},
{directory: "testdata/dir1", filter: architest.FileNameRegexFilterProvider(regexp.MustCompile("sample")), expectedFileNames: []string{"testdata/dir1/sample1.go", "testdata/dir1/sample2.go"}},
{directory: "testdata/dir1", filter: architest.FileNameFilterWithExclusionsProvider(regexp.MustCompile("sample"), regexp.MustCompile("sample1")), expectedFileNames: []string{"testdata/dir1/sample2.go"}},
{directory: "testdata/dir2", filter: architest.PackageFilterProvider("dir2"), expectedFileNames: []string{"testdata/dir2/sample1.go"}},
{directory: "testdata/dir2", filter: architest.PackageFilterProvider("dir2_test"), expectedFileNames: []string{"testdata/dir2/sample1_test.go"}},
{directory: "testdata/dir2", filter: architest.FileNameRegexFilterProvider(architest.AcceptanceTestFileRegex), expectedFileNames: []string{}},
{directory: "testdata/dir3", filter: architest.FileNameRegexFilterProvider(architest.AcceptanceTestFileRegex), expectedFileNames: []string{"testdata/dir3/sample1_acceptance_test.go"}},
{directory: "testdata/dir4", filter: architest.FileNameRegexFilterProvider(architest.AcceptanceTestFileRegex), expectedFileNames: []string{}},
{directory: "testdata/dir2", filter: architest.FileNameRegexFilterProvider(architest.IntegrationTestFileRegex), expectedFileNames: []string{}},
{directory: "testdata/dir3", filter: architest.FileNameRegexFilterProvider(architest.IntegrationTestFileRegex), expectedFileNames: []string{}},
{directory: "testdata/dir4", filter: architest.FileNameRegexFilterProvider(architest.IntegrationTestFileRegex), expectedFileNames: []string{"testdata/dir4/sample1_integration_test.go"}},
{directory: "testdata/dir2", filter: architest.FileNameRegexFilterProvider(architest.TestFileRegex), expectedFileNames: []string{"testdata/dir2/sample1_test.go"}},
{directory: "testdata/dir3", filter: architest.FileNameRegexFilterProvider(architest.TestFileRegex), expectedFileNames: []string{"testdata/dir3/sample1_acceptance_test.go"}},
{directory: "testdata/dir4", filter: architest.FileNameRegexFilterProvider(architest.TestFileRegex), expectedFileNames: []string{"testdata/dir4/sample1_integration_test.go"}},
}
for _, tt := range tests2 {
t.Run("list only files matching filter in the given directory", func(t *testing.T) {
dir := architest.Directory(tt.directory)

filteredFiles := dir.Files(tt.filter)
assert.Len(t, filteredFiles, len(tt.expectedFileNames))

fileNames := make([]string, 0, len(filteredFiles))
for _, f := range filteredFiles {
fileNames = append(fileNames, f.Name())
}
assert.ElementsMatch(t, fileNames, tt.expectedFileNames)

// now exactly the same but indirectly
filteredFiles = dir.AllFiles().Filter(tt.filter)
assert.Len(t, filteredFiles, len(tt.expectedFileNames))

fileNames = make([]string, 0, len(filteredFiles))
for _, f := range filteredFiles {
fileNames = append(fileNames, f.Name())
}
assert.ElementsMatch(t, fileNames, tt.expectedFileNames)
})
}
}

func Test_Files(t *testing.T) {
t.Run("fail to use non-existing file", func(t *testing.T) {
assert.Panics(t, func() {
architest.NewFileFromPath("testdata/dir1/non_existing.go")
sfc-gh-jcieslak marked this conversation as resolved.
Show resolved Hide resolved
})
})

t.Run("use file", func(t *testing.T) {
assert.NotPanics(t, func() {
architest.NewFileFromPath("testdata/dir1/sample1.go")
})
})

tests1 := []struct {
filePath string
expectedMethodNames []string
}{
{filePath: "testdata/dir1/sample1.go", expectedMethodNames: []string{}},
{filePath: "testdata/dir1/sample2.go", expectedMethodNames: []string{"A"}},
}
for _, tt := range tests1 {
t.Run("list all methods in file", func(t *testing.T) {
file := architest.NewFileFromPath(tt.filePath)

exportedMethods := file.ExportedMethods()
assert.Len(t, exportedMethods, len(tt.expectedMethodNames))

methodNames := make([]string, 0, len(exportedMethods))
for _, m := range exportedMethods {
methodNames = append(methodNames, m.Name())
}
assert.ElementsMatch(t, methodNames, tt.expectedMethodNames)
})
}

tests2 := []struct {
fileNames []string
}{
{fileNames: []string{}},
{fileNames: []string{"a"}},
{fileNames: []string{"a", "A"}},
{fileNames: []string{"A", "a"}},
{fileNames: []string{"A", "B", "C"}},
}
for _, tt := range tests2 {
t.Run("receiver invoked for every file", func(t *testing.T) {
files := make(architest.Files, 0, len(tt.fileNames))
for _, f := range tt.fileNames {
files = append(files, *architest.NewFile("package", f, nil))
sfc-gh-jcieslak marked this conversation as resolved.
Show resolved Hide resolved
}
invokedFiles := make([]string, 0)
receiver := func(f *architest.File) {
invokedFiles = append(invokedFiles, f.Name())
}

files.All(receiver)

assert.ElementsMatch(t, tt.fileNames, invokedFiles)
})
}
}

func Test_Methods(t *testing.T) {
tests := []struct {
methodNames []string
}{
{methodNames: []string{}},
{methodNames: []string{"a"}},
{methodNames: []string{"a", "A"}},
{methodNames: []string{"A", "a"}},
{methodNames: []string{"A", "B", "C"}},
}
for _, tt := range tests {
t.Run("receiver invoked for every method", func(t *testing.T) {
methods := make(architest.Methods, 0, len(tt.methodNames))
for _, m := range tt.methodNames {
methods = append(methods, *architest.NewMethod(m, nil))
}
invokedMethods := make([]string, 0)
receiver := func(m *architest.Method) {
invokedMethods = append(invokedMethods, m.Name())
}

methods.All(receiver)

assert.ElementsMatch(t, tt.methodNames, invokedMethods)
})
}
}

func Test_Assertions(t *testing.T) {
tests1 := []struct {
filePath string
expectedPackage string
}{
{filePath: "testdata/dir1/sample1.go", expectedPackage: "dir1"},
{filePath: "testdata/dir2/sample1.go", expectedPackage: "dir2"},
{filePath: "testdata/dir2/sample1_test.go", expectedPackage: "dir2_test"},
}
for _, tt := range tests1 {
t.Run("file package assertions", func(t *testing.T) {
file := architest.NewFileFromPath(tt.filePath)
tut1 := &testing.T{}
tut2 := &testing.T{}

file.AssertHasPackage(tut1, tt.expectedPackage)
file.AssertHasPackage(tut2, "some_other_package")

assert.Equal(t, false, tut1.Failed())
assert.Equal(t, true, tut2.Failed())
})
}

tests2 := []struct {
methodName string
correct bool
}{
{methodName: "TestAcc_abc", correct: true},
{methodName: "TestAcc_TestAcc_Test", correct: true},
{methodName: "TestAcc_", correct: false},
{methodName: "ATestAcc_", correct: false},
{methodName: "TestAcc", correct: false},
{methodName: "Test_", correct: false},
{methodName: "Test", correct: false},
{methodName: "Test_asdf", correct: false},
{methodName: "TestAccc_", correct: false},
{methodName: "TestInt_Abc", correct: false},
}
for _, tt := range tests2 {
t.Run(fmt.Sprintf("acceptance test name assertions for method %s", tt.methodName), func(t *testing.T) {
file := architest.NewFileFromPath("testdata/dir1/sample1.go")
method := architest.NewMethod(tt.methodName, file)
tut := &testing.T{}

method.AssertAcceptanceTestNamedCorrectly(tut)

assert.Equal(t, !tt.correct, tut.Failed())
})
}

tests3 := []struct {
methodName string
regexRaw string
correct bool
}{
{methodName: "sample1", regexRaw: "sample", correct: true},
{methodName: "Sample1", regexRaw: "sample", correct: false},
{methodName: "Sample1", regexRaw: "Sample", correct: true},
}
for _, tt := range tests3 {
t.Run(fmt.Sprintf("matching and not matching method name assertions for %s", tt.methodName), func(t *testing.T) {
file := architest.NewFileFromPath("testdata/dir1/sample1.go")
method := architest.NewMethod(tt.methodName, file)
tut1 := &testing.T{}
tut2 := &testing.T{}

method.AssertNameMatches(tut1, regexp.MustCompile(tt.regexRaw))
method.AssertNameDoesNotMatch(tut2, regexp.MustCompile(tt.regexRaw))

assert.Equal(t, !tt.correct, tut1.Failed())
assert.Equal(t, tt.correct, tut2.Failed())
})
}

tests4 := []struct {
methodName string
correct bool
}{
{methodName: "Test", correct: true},
{methodName: "aTest", correct: false},
{methodName: "Test_", correct: true},
{methodName: "Test_adsfadf", correct: true},
}
for _, tt := range tests4 {
t.Run(fmt.Sprintf("test name assertions for method %s", tt.methodName), func(t *testing.T) {
file := architest.NewFileFromPath("testdata/dir1/sample1.go")
method := architest.NewMethod(tt.methodName, file)
tut := &testing.T{}

method.AssertTestNamedCorrectly(tut)

assert.Equal(t, !tt.correct, tut.Failed())
})
}

tests5 := []struct {
methodName string
correct bool
}{
{methodName: "TestInt_abc", correct: true},
{methodName: "TestInt_TestInt_Test", correct: true},
{methodName: "TestInt_", correct: false},
{methodName: "ATestInt_", correct: false},
{methodName: "TestInt", correct: false},
{methodName: "Test_", correct: false},
{methodName: "Test", correct: false},
{methodName: "Test_asdf", correct: false},
{methodName: "TestIntt_", correct: false},
{methodName: "TestAcc_Abc", correct: false},
}
for _, tt := range tests5 {
t.Run(fmt.Sprintf("intagration test name assertions for method %s", tt.methodName), func(t *testing.T) {
file := architest.NewFileFromPath("testdata/dir1/sample1.go")
method := architest.NewMethod(tt.methodName, file)
tut := &testing.T{}

method.AssertIntegrationTestNamedCorrectly(tut)

assert.Equal(t, !tt.correct, tut.Failed())
})
}
}

func Test_SampleArchiTestUsage(t *testing.T) {
t.Run("acceptance tests", func(t *testing.T) {
acceptanceTestFiles := architest.Directory("testdata/dir3/").
AllFiles().
Filter(architest.FileNameRegexFilterProvider(architest.AcceptanceTestFileRegex))

acceptanceTestFiles.All(func(file *architest.File) {
file.AssertHasPackage(t, "dir3_test")
file.ExportedMethods().All(func(method *architest.Method) {
method.AssertAcceptanceTestNamedCorrectly(t)
})
})
})

t.Run("integration tests", func(t *testing.T) {
integrationTestFiles := architest.Directory("testdata/dir4/").
AllFiles().
Filter(architest.FileNameRegexFilterProvider(architest.IntegrationTestFileRegex))

integrationTestFiles.All(func(file *architest.File) {
file.AssertHasPackage(t, "dir4_test")
file.ExportedMethods().All(func(method *architest.Method) {
method.AssertIntegrationTestNamedCorrectly(t)
})
})
})

t.Run("tests", func(t *testing.T) {
testFiles := architest.Directory("testdata/dir2/").
AllFiles().
Filter(architest.FileNameRegexFilterProvider(architest.TestNameRegex))

testFiles.All(func(file *architest.File) {
file.AssertHasPackage(t, "dir2_test")
file.ExportedMethods().All(func(method *architest.Method) {
method.AssertTestNamedCorrectly(t)
})
})
})
}
Loading
Loading