Files
openyield/web/store/import_test.go
T
cloudinit-bot 5a12ab0e76 feat(P1): mock store + fixtures + import-invariant test (REQ-040, G-025, G-027)
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---
2026-08-18 13:45:05 +00:00

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")
}