package architecture_test import ( "go/ast" "go/parser" "go/token" "os" "path/filepath" "strconv" "strings" "testing" ) func TestImportBoundaries(t *testing.T) { t.Parallel() repositoryRoot := filepath.Join("..", "..") rules := []struct { directory string forbidden []string }{ { directory: "internal/domain", forbidden: []string{"net/http", "database/sql", "github.com/jackc/pgx", "/internal/adapters/", "/internal/transport/"}, }, { directory: "internal/transport", forbidden: []string{"database/sql", "github.com/jackc/pgx", "os"}, }, } for _, rule := range rules { rule := rule t.Run(rule.directory, func(t *testing.T) { checkDirectory(t, repositoryRoot, rule.directory, rule.forbidden) }) } } func checkDirectory(t *testing.T, root, directory string, forbidden []string) { t.Helper() err := filepath.WalkDir(filepath.Join(root, directory), func(path string, entry os.DirEntry, walkErr error) error { if walkErr != nil { return walkErr } if entry.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { return nil } parsed, err := parser.ParseFile(token.NewFileSet(), path, nil, parser.ImportsOnly) if err != nil { return err } for _, spec := range parsed.Imports { assertImportAllowed(t, path, spec, forbidden) } return nil }) if err != nil { t.Fatalf("walk %s: %v", directory, err) } } func assertImportAllowed(t *testing.T, path string, spec *ast.ImportSpec, forbidden []string) { t.Helper() importPath, err := strconv.Unquote(spec.Path.Value) if err != nil { t.Fatalf("unquote import in %s: %v", path, err) } for _, pattern := range forbidden { if importPath == pattern || strings.Contains(importPath, pattern) { t.Errorf("%s imports forbidden package %q", path, importPath) } } }