63ede5c237
- refactor: move HTTP status and DTO concerns out of model and service layers into API and handler code. - feat: add admin service boundary and route auth through services instead of direct repository access. - test: add architecture and error tests covering package boundaries, redaction, and service behavior. - docs: record the protocol-neutral boundary decision and update architecture and roadmap notes.
69 lines
1.6 KiB
Go
69 lines
1.6 KiB
Go
package internal_test
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestArchitecture_ModelAndServiceAreProtocolNeutral(t *testing.T) {
|
|
for _, dir := range []string{"model", "service"} {
|
|
imports := packageImports(t, dir)
|
|
for _, forbidden := range []string{
|
|
"net/http",
|
|
"github.com/gin-gonic/gin",
|
|
"github.com/dhao2001/mygo/internal/api",
|
|
} {
|
|
if imports[forbidden] {
|
|
t.Fatalf("internal/%s imports forbidden package %q", dir, forbidden)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestArchitecture_HTTPDoesNotImportRepositories(t *testing.T) {
|
|
for _, dir := range []string{"handler", "middleware"} {
|
|
imports := packageImports(t, dir)
|
|
if imports["github.com/dhao2001/mygo/internal/repository"] {
|
|
t.Fatalf("internal/%s imports internal/repository; use service boundaries instead", dir)
|
|
}
|
|
}
|
|
}
|
|
|
|
func packageImports(t *testing.T, dir string) map[string]bool {
|
|
t.Helper()
|
|
|
|
imports := map[string]bool{}
|
|
pkgs := parsePackage(t, dir)
|
|
for _, pkg := range pkgs {
|
|
for _, file := range pkg.Files {
|
|
for _, spec := range file.Imports {
|
|
path, err := strconv.Unquote(spec.Path.Value)
|
|
if err != nil {
|
|
t.Fatalf("unquote import path %s: %v", spec.Path.Value, err)
|
|
}
|
|
imports[path] = true
|
|
}
|
|
}
|
|
}
|
|
return imports
|
|
}
|
|
|
|
func parsePackage(t *testing.T, dir string) map[string]*ast.Package {
|
|
t.Helper()
|
|
|
|
fset := token.NewFileSet()
|
|
pkgs, err := parser.ParseDir(fset, dir, func(info os.FileInfo) bool {
|
|
name := info.Name()
|
|
return !strings.HasSuffix(name, "_test.go")
|
|
}, parser.ParseComments)
|
|
if err != nil {
|
|
t.Fatalf("parse internal/%s: %v", dir, err)
|
|
}
|
|
return pkgs
|
|
}
|