Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

158 lines
4.3 KiB
Go

package contracts
import (
"testing"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser"
gosrc "github.com/zzet/gortex/internal/parser/tsitter/golang"
)
func TestGoRouteAST_BasicRoutes(t *testing.T) {
src := []byte(`package h
func register(mux *http.ServeMux) {
mux.HandleFunc("GET /users", listUsers)
mux.HandleFunc("/legacy", legacyHandler)
}
func chiRoutes(r chi.Router) {
r.Get("/api/users", h.List)
r.Post("/api/users", h.Create)
}
func fiberRoutes(app *fiber.App) {
app.GET("/v1/items", listItems)
app.DELETE("/v1/items/:id", deleteItem)
}
`)
tree, err := parser.ParseFile(src, gosrc.GetLanguage())
if err != nil {
t.Fatal(err)
}
defer tree.Close()
matches := detectGoRoutesAST(tree.RootNode(), src, "", "", nil)
if len(matches) != 6 {
t.Fatalf("matches: want 6, got %d (%+v)", len(matches), matches)
}
want := []struct{ method, path, framework string }{
{"GET", "/users", "net/http"},
{"ANY", "/legacy", "net/http"},
{"GET", "/api/users", "gin/echo/chi"}, // r.Get(...) → TitleCase → gin/echo/chi label
{"POST", "/api/users", "gin/echo/chi"},
{"GET", "/v1/items", "fiber"},
{"DELETE", "/v1/items/:id", "fiber"},
}
for i, w := range want {
if i >= len(matches) {
t.Errorf("missing match[%d]: %+v", i, w)
continue
}
got := matches[i]
if got.method != w.method || got.path != w.path || got.framework != w.framework {
t.Errorf("match[%d]: want %s %s (%s), got %s %s (%s)",
i, w.method, w.path, w.framework,
got.method, got.path, got.framework)
}
}
}
// THE Fiber bug fixture: `[]byte(".GET(")` must NOT produce a route.
// This is the original failure that motivated the entire spec — the
// regex at http.go:97 matched `.GET(` inside its own prefilter-marker
// source, emitting a contract whose path contained the substring
// "[]byte(". The AST detector structurally rejects it: a string
// literal inside a type-conversion expression is not a route
// registration's path argument.
func TestGoRouteAST_FiberSelfReflexiveBugStructurallyImpossible(t *testing.T) {
src := []byte(`package contracts
// httpPrefilterMarkers is the per-language substring prefilter.
var httpPrefilterMarkers = map[string][][]byte{
"go": {
[]byte("Handle"),
[]byte(".GET("), // fiber uppercase verbs
[]byte(".POST("),
[]byte(".PUT("),
[]byte(".DELETE("),
},
}
`)
tree, err := parser.ParseFile(src, gosrc.GetLanguage())
if err != nil {
t.Fatal(err)
}
defer tree.Close()
matches := detectGoRoutesAST(tree.RootNode(), src, "", "", nil)
if len(matches) != 0 {
t.Fatalf("AST detector matched non-routes: %+v", matches)
}
}
func TestGoRouteAST_ConsumerHttpGetNotMatched(t *testing.T) {
// http.Get(url) is a CONSUMER; the regex avoids it via a negative
// look-behind on `r/g/e/...`. The AST detector handles it via
// isHTTPStdlibConsumer.
src := []byte(`package h
func fetch() {
_, _ = http.Get("https://example.com")
}
`)
tree, err := parser.ParseFile(src, gosrc.GetLanguage())
if err != nil {
t.Fatal(err)
}
defer tree.Close()
matches := detectGoRoutesAST(tree.RootNode(), src, "", "", nil)
if len(matches) != 0 {
t.Fatalf("matched http.Get consumer: %+v", matches)
}
}
// HTTPExtractor end-to-end: when the tree is provided, the AST
// detector runs and produces the expected contracts; the bug fixture
// produces zero contracts.
func TestHTTPExtractor_TreeAware_BugFixtureProducesNoBogusRoutes(t *testing.T) {
src := []byte(`package contracts
var markers = [][]byte{
[]byte(".GET("),
[]byte(".POST("),
}
`)
tree, err := parser.ParseFile(src, gosrc.GetLanguage())
if err != nil {
t.Fatal(err)
}
pt := parser.NewParseTree(tree, src, "go")
defer pt.Release()
ext := &HTTPExtractor{}
out := ext.ExtractWithTree("contracts/markers.go", src, []*graph.Node{}, []*graph.Edge{}, pt)
for _, c := range out {
// Any route whose path contains marker text is the bug
// recurring.
if path, _ := c.Meta["path"].(string); path != "" {
if containsAny(path, []string{"[]byte(", "GET(", "POST("}) {
t.Errorf("bug-fixture produced bogus contract: id=%s path=%q", c.ID, path)
}
}
}
if t.Failed() {
t.Logf("%d contracts produced", len(out))
}
}
func containsAny(s string, substrs []string) bool {
for _, sub := range substrs {
if len(sub) > 0 && len(s) >= len(sub) {
for i := 0; i+len(sub) <= len(s); i++ {
if s[i:i+len(sub)] == sub {
return true
}
}
}
}
return false
}