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

112 lines
3.2 KiB
Go

package resolver
import (
"testing"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/parser/languages"
)
// TestGinMiddlewareDispatcherToHandlerEdges drives the Gin middleware-chain
// synthesizer end-to-end: the Go extractor's source parse stamps the dispatcher
// (the method indexing `c.handlers[idx](c)`) and the registered handler names,
// and the resolver bridges the dispatcher to each handler with a tier-tagged
// traversable call edge. That edge is the reachability a same-file scanner
// drops at the indexed-slice indirection — get_call_chain now reaches the
// handlers from the dispatcher. String path args and inline closures are not
// handlers, and with no dispatcher in the graph nothing is synthesized.
func TestGinMiddlewareDispatcherToHandlerEdges(t *testing.T) {
const router = `package web
type Context struct {
index int
handlers []HandlerFunc
}
type HandlerFunc func(*Context)
func (c *Context) Next() {
for c.index < len(c.handlers) {
c.handlers[c.index](c)
c.index++
}
}
func Logger() HandlerFunc { return nil }
func listUsers(c *Context) {}
func createUser(c *Context) {}
func setup(r *Engine) {
r.Use(Logger())
r.GET("/users", listUsers)
r.POST("/users", createUser, func(c *Context) {})
}
`
build := func(src string) *graph.Graph {
res, err := languages.NewGoExtractor().Extract("web/web.go", []byte(src))
if err != nil {
t.Fatalf("extract: %v", err)
}
g := graph.New()
for _, n := range res.Nodes {
g.AddNode(n)
}
for _, e := range res.Edges {
g.AddEdge(e)
}
return g
}
g := build(router)
n := ResolveGinMiddlewareCalls(g)
if n != 3 {
t.Fatalf("synthesized %d dispatcher→handler edges, want 3", n)
}
// Index the synthesized edges by target.
const disp = "web/web.go::Context.Next"
targets := map[string]*graph.Edge{}
for _, e := range g.AllEdges() {
if e.Meta != nil && e.Meta["via"] == ginMiddlewareVia && e.From == disp {
targets[e.To] = e
}
}
for _, want := range []string{"web/web.go::Logger", "web/web.go::listUsers", "web/web.go::createUser"} {
e, ok := targets[want]
if !ok {
t.Errorf("missing dispatcher→handler edge to %q", want)
continue
}
// Tier-tagged + provenance-stamped so the edge is min_tier filterable
// and attributable — not an opaque heuristic.
if e.Origin != graph.OriginSpeculative {
t.Errorf("edge to %q origin=%v, want OriginSpeculative", want, e.Origin)
}
if e.Meta[MetaSynthesizedBy] != SynthGinMiddleware {
t.Errorf("edge to %q synthesized_by=%v, want %q", want, e.Meta[MetaSynthesizedBy], SynthGinMiddleware)
}
if e.Kind != graph.EdgeCalls {
t.Errorf("edge to %q kind=%v, want EdgeCalls (traversable in get_call_chain)", want, e.Kind)
}
}
// The inline closure passed to POST is not a named handler.
for to := range targets {
if to == "" {
t.Errorf("synthesized an edge to an empty target (a closure leaked in)")
}
}
// Gate: no dispatcher in the graph → nothing synthesized.
const noDispatcher = `package web
type HandlerFunc func()
func listUsers() {}
func setup(r *Engine) {
r.GET("/users", listUsers)
}
`
g2 := build(noDispatcher)
if got := ResolveGinMiddlewareCalls(g2); got != 0 {
t.Errorf("with no dispatcher present, synthesized %d edges, want 0", got)
}
}