5a12ab0e76
web/store instantiates real x/identity/types.Reach + x/stash/types.Stash (app-layer consumption per D-070, NOT a cross-x/ import). CreateReach atomically creates Reach (IsNomad=true) + Stash (D-071). G-027 validates HolderID/PublicKey (non-empty, <=128, no path separators, no template syntax). import_test.go enforces G-025: web/ imports only x/*/types, never x/*/keeper or x/<module> (module.go). Coverage 100%. ---ci--- project: oy phase: 1 milestone: v0.6 status: execute ---/ci---
109 lines
3.3 KiB
Go
109 lines
3.3 KiB
Go
// 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/<module>/types packages (the app-layer
|
|
// consumption direction), but MUST NOT import github.com/oy/openyield/
|
|
// x/<module>/keeper OR github.com/oy/openyield/x/<module> (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> (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/<module>/keeper or a bare
|
|
// x/<module> (module.go) import — both forbidden from web/ (G-025). The
|
|
// x/<module>/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> (module.go package) — forbidden (G-025).
|
|
return true
|
|
case 2:
|
|
// x/<module>/types -> allowed (D-070). x/<module>/keeper -> forbidden.
|
|
if parts[1] == "types" {
|
|
return false
|
|
}
|
|
return true
|
|
default:
|
|
// x/<module>/<sub>/... — forbid anything other than types (e.g.
|
|
// x/<module>/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")
|
|
}
|