package adapter_test import ( "fmt" "go/parser" "go/token" "io/fs" "os" "path/filepath" "runtime" "strconv" "strings" "testing" ) const scriptoriumModulePath = "gitea.maximumdirect.net/eric/scriptorium" var forbiddenFrameworkPackageRoots = []string{ scriptoriumModulePath + "/internal/domain", scriptoriumModulePath + "/internal/usecase", scriptoriumModulePath + "/internal/promptdef", scriptoriumModulePath + "/internal/prompt", scriptoriumModulePath + "/internal/profile", scriptoriumModulePath + "/internal/validate", scriptoriumModulePath + "/internal/llm", scriptoriumModulePath + "/internal/artifact", } type forbiddenFrameworkImport struct { filePath string importPath string } func TestScriptoriumAdaptersUseOnlyPublicFrameworkBoundary(t *testing.T) { _, testFile, _, ok := runtime.Caller(0) if !ok { t.Fatal("locate dependency guard source") } adapterDir := filepath.Dir(testFile) violations, err := findForbiddenFrameworkImports([]string{ filepath.Join(adapterDir, "cli"), filepath.Join(adapterDir, "http"), filepath.Join(adapterDir, "..", "format"), }) if err != nil { t.Fatalf("scan framework imports: %v", err) } for _, violation := range violations { t.Errorf("%s imports forbidden framework package %s", violation.filePath, violation.importPath) } } func TestForbiddenFrameworkImportScannerDetectsNestedPackageFamilies(t *testing.T) { root := t.TempDir() nestedDir := filepath.Join(root, "nested", "adapter") if err := os.MkdirAll(nestedDir, 0o755); err != nil { t.Fatalf("create nested source directory: %v", err) } sourcePath := filepath.Join(nestedDir, "imports.go") source := `package nested import ( _ "gitea.maximumdirect.net/eric/scriptorium" _ "gitea.maximumdirect.net/eric/scriptorium/internal/profile/builtin" ) ` if err := os.WriteFile(sourcePath, []byte(source), 0o644); err != nil { t.Fatalf("write nested source fixture: %v", err) } violations, err := findForbiddenFrameworkImports([]string{root}) if err != nil { t.Fatalf("scan nested source fixture: %v", err) } if len(violations) != 1 { t.Fatalf("expected one forbidden import, got %#v", violations) } if violations[0].filePath != sourcePath { t.Fatalf("unexpected importing file: %q", violations[0].filePath) } wantImport := scriptoriumModulePath + "/internal/profile/builtin" if violations[0].importPath != wantImport { t.Fatalf("unexpected forbidden import: %q", violations[0].importPath) } } func findForbiddenFrameworkImports(roots []string) ([]forbiddenFrameworkImport, error) { var violations []forbiddenFrameworkImport for _, root := range roots { err := filepath.WalkDir(root, func(path string, entry fs.DirEntry, err error) error { if err != nil { return err } if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".go") || strings.HasSuffix(entry.Name(), "_test.go") { return nil } file, err := parser.ParseFile(token.NewFileSet(), path, nil, parser.ImportsOnly) if err != nil { return fmt.Errorf("parse imports in %s: %w", path, err) } for _, imported := range file.Imports { importPath, err := strconv.Unquote(imported.Path.Value) if err != nil { return fmt.Errorf("parse import path in %s: %w", path, err) } if isForbiddenFrameworkImport(importPath) { violations = append(violations, forbiddenFrameworkImport{ filePath: path, importPath: importPath, }) } } return nil }) if err != nil { return nil, fmt.Errorf("walk source root %s: %w", root, err) } } return violations, nil } func isForbiddenFrameworkImport(importPath string) bool { for _, root := range forbiddenFrameworkPackageRoots { if importPath == root || strings.HasPrefix(importPath, root+"/") { return true } } return false }