// import_test.go enforces the G-003/G-025 boundary for web/: web/ is the // application layer that consumes protocol types (D-070), NOT a cross-x/ // production import. The invariant: every non-test .go file under web/ may // import github.com/oy/openyield/x//types packages (the app-layer // consumption direction), but MUST NOT import github.com/oy/openyield/ // x//keeper OR github.com/oy/openyield/x/ (the module.go // packages — G-025 extends the original keeper-only check to also forbid // module.go, since those packages carry Cosmos runtime machinery the mock UI // must not reach into). This test uses go/parser (stdlib only — G-006) and // mirrors the x/window/types/types_test.go G-003 pattern, but with the // inverted rule: x/*/types is ALLOWED (app-layer consumption), x/*/keeper // and x/ (module.go) are FORBIDDEN. package store import ( "go/parser" "go/token" "os" "path/filepath" "runtime" "strings" "testing" ) func TestG025WebImportsOnlyTypesNotKeeperOrModule(t *testing.T) { webRoot := webRoot(t) fset := token.NewFileSet() violations := []string{} err := filepath.Walk(webRoot, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { return nil } if !strings.HasSuffix(path, ".go") { return nil } // Skip test files (G-025 is about production code only). if strings.HasSuffix(path, "_test.go") { return nil } f, perr := parser.ParseFile(fset, path, nil, parser.ImportsOnly) if perr != nil { return perr } for _, imp := range f.Imports { ip := strings.Trim(imp.Path.Value, `"`) if isForbiddenXImport(ip) { rel, _ := filepath.Rel(webRoot, path) violations = append(violations, rel+" -> "+ip) } } return nil }) if err != nil { t.Fatalf("walk web/: %v", err) } if len(violations) > 0 { t.Errorf("G-025 violation: web/ production files importing forbidden x/ packages:\n %s", strings.Join(violations, "\n ")) } } // isForbiddenXImport reports whether ip is an x//keeper or a bare // x/ (module.go) import — both forbidden from web/ (G-025). The // x//types packages are ALLOWED (D-070 app-layer consumption). func isForbiddenXImport(ip string) bool { const prefix = "github.com/oy/openyield/x/" if !strings.HasPrefix(ip, prefix) { return false } rest := strings.TrimPrefix(ip, prefix) parts := strings.Split(rest, "/") switch len(parts) { case 1: // x/ (module.go package) — forbidden (G-025). return true case 2: // x//types -> allowed (D-070). x//keeper -> forbidden. if parts[1] == "types" { return false } return true default: // x///... — forbid anything other than types (e.g. // x//keeper/... sub-packages). if parts[1] == "types" { return false } return true } } // webRoot returns the absolute path to the web/ directory by walking up // from this test file (web/store/import_test.go -> repoRoot/web). func webRoot(t *testing.T) string { t.Helper() _, file, _, ok := runtime.Caller(0) if !ok { t.Fatal("runtime.Caller failed") } // file = .../oy/web/store/import_test.go // repoRoot = filepath.Dir(filepath.Dir(filepath.Dir(file))) // webRoot = repoRoot/web repoRoot := filepath.Dir(filepath.Dir(filepath.Dir(file))) return filepath.Join(repoRoot, "web") }