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

256 lines
7.1 KiB
Go

package languages
import (
"strings"
"testing"
"github.com/zzet/gortex/internal/graph"
)
func TestGoStrings_StatsdMetricsExtracted(t *testing.T) {
src := `package foo
type StatsdClient struct{}
func (c *StatsdClient) Increment(name string, tags []string, rate float64) error { return nil }
func (c *StatsdClient) Gauge(name string, value float64, tags []string, rate float64) error { return nil }
func Run(c *StatsdClient) {
c.Increment("orders.checkout.success", nil, 1)
c.Gauge("server.memory.bytes", 12345, nil, 1)
}
`
fix := runGoExtract(t, src)
strs := fix.nodesByKind[graph.KindString]
if len(strs) != 2 {
t.Fatalf("expected 2 KindString, got %d: %+v", len(strs), strs)
}
gotByValue := map[string]*graph.Node{}
for _, n := range strs {
gotByValue[n.Name] = n
if ctx, _ := n.Meta["context"].(string); ctx != "metric" {
t.Errorf("node %q: context = %q, want metric", n.Name, ctx)
}
want := "string::metric::" + n.Name
if n.ID != want {
t.Errorf("id = %q, want %q", n.ID, want)
}
}
if gotByValue["orders.checkout.success"] == nil || gotByValue["server.memory.bytes"] == nil {
t.Errorf("missing expected metric names: %v", gotByValue)
}
emits := fix.edgesByKind[graph.EdgeEmits]
if len(emits) != 2 {
t.Errorf("expected 2 EdgeEmits, got %d", len(emits))
}
for _, e := range emits {
if e.From != "pkg/foo.go::Run" {
t.Errorf("emit from = %q, want pkg/foo.go::Run", e.From)
}
if ctx, _ := e.Meta["context"].(string); ctx != "metric" {
t.Errorf("emit context = %q", ctx)
}
}
}
func TestGoStrings_GenericMethodNamesIgnored(t *testing.T) {
// Set / Inc / Add are too generic — would create false positives
// against map and counter types — and are deliberately excluded.
src := `package foo
type M struct{}
func (m M) Set(k, v string) {}
func (m M) Inc(k string) {}
func (m M) Add(k string, n int) {}
func Run(m M) {
m.Set("magic", "value")
m.Inc("counter")
m.Add("tally", 1)
}
`
fix := runGoExtract(t, src)
if got := fix.nodesByKind[graph.KindString]; len(got) != 0 {
t.Errorf("expected no metric strings, got %d: %+v", len(got), got)
}
}
func TestGoStrings_ErrorsNewAndFmtErrorf(t *testing.T) {
src := `package foo
import (
"errors"
"fmt"
)
func A() error { return errors.New("user not found") }
func B(id int) error { return fmt.Errorf("invalid id %d", id) }
`
fix := runGoExtract(t, src)
strs := fix.nodesByKind[graph.KindString]
if len(strs) != 2 {
t.Fatalf("expected 2 KindString, got %d: %+v", len(strs), strs)
}
gotByValue := map[string]*graph.Node{}
for _, n := range strs {
gotByValue[n.Name] = n
if ctx, _ := n.Meta["context"].(string); ctx != "error_msg" {
t.Errorf("node %q: context = %q, want error_msg", n.Name, ctx)
}
}
if gotByValue["user not found"] == nil {
t.Errorf("missing 'user not found': %v", gotByValue)
}
if gotByValue["invalid id %d"] == nil {
t.Errorf("missing 'invalid id %%d': %v", gotByValue)
}
}
func TestGoStrings_HTTPRoutesExtracted(t *testing.T) {
src := `package foo
type Mux struct{}
func (m *Mux) HandleFunc(pattern string, h func()) {}
func (m *Mux) Get(pattern string, h func()) {}
func Wire(m *Mux) {
m.HandleFunc("/api/v1/users", nil)
m.Get("/health", nil)
m.HandleFunc("GET /api/v1/orders", nil)
}
`
fix := runGoExtract(t, src)
strs := fix.nodesByKind[graph.KindString]
if len(strs) != 3 {
t.Fatalf("expected 3 KindString routes, got %d: %+v", len(strs), strs)
}
got := map[string]bool{}
for _, n := range strs {
got[n.Name] = true
if ctx, _ := n.Meta["context"].(string); ctx != "route" {
t.Errorf("node %q: context = %q, want route", n.Name, ctx)
}
}
for _, want := range []string{"/api/v1/users", "/health", "GET /api/v1/orders"} {
if !got[want] {
t.Errorf("missing route %q", want)
}
}
}
func TestGoStrings_NonRouteStringSkipped(t *testing.T) {
// Get/Post on map-like types — the looksLikeRoute filter should
// keep these out of the route bucket.
src := `package foo
type Cache struct{}
func (c *Cache) Get(key string) string { return "" }
func Run(c *Cache) {
_ = c.Get("user:42:profile")
}
`
fix := runGoExtract(t, src)
if got := fix.nodesByKind[graph.KindString]; len(got) != 0 {
t.Errorf("expected no routes for cache.Get, got: %+v", got)
}
}
func TestGoStrings_SameMetricAcrossCallsDeduplicates(t *testing.T) {
src := `package foo
type Client struct{}
func (c *Client) Increment(name string) {}
func A(c *Client) { c.Increment("orders.success") }
func B(c *Client) { c.Increment("orders.success") }
`
fix := runGoExtract(t, src)
strs := fix.nodesByKind[graph.KindString]
if len(strs) != 1 {
t.Fatalf("expected 1 KindString (deduplicated), got %d", len(strs))
}
emits := fix.edgesByKind[graph.EdgeEmits]
if len(emits) != 2 {
t.Errorf("expected 2 EdgeEmits (one per caller), got %d", len(emits))
}
}
func TestGoStrings_NodeIDForLongValueIsHashed(t *testing.T) {
// 250 chars > 200-char threshold — should hash to keep IDs sane.
long := strings.Repeat("a", 250)
got := goStringNodeID(stringCtxErrorMsg, long)
want := "string::error_msg::sha1:"
if got[:len(want)] != want {
t.Errorf("long-value id = %q, want prefix %q", got, want)
}
short := goStringNodeID(stringCtxMetric, "ok.short")
if short != "string::metric::ok.short" {
t.Errorf("short-value id = %q", short)
}
}
func TestLooksLikeRoute(t *testing.T) {
cases := map[string]bool{
"/users": true,
"/api/v1/orders": true,
"GET /foo": true,
"POST /bar": true,
"": false,
"plain": false,
"user:42:profile": false,
"orders.checkout.event": false,
}
for in, want := range cases {
if got := looksLikeRoute(in, ""); got != want {
t.Errorf("looksLikeRoute(%q) = %v, want %v", in, got, want)
}
}
}
func TestLooksLikeRoute_FilesystemPathRejected(t *testing.T) {
// A rooted literal that is a filesystem/config path must not survive the
// route guard, even though the old shape heuristic accepted any "/..."
// literal. A real route alongside it still passes.
if looksLikeRoute("/etc/app.conf", "HandleFunc") {
t.Errorf("looksLikeRoute(/etc/app.conf) accepted a config path as a route")
}
if looksLikeRoute("/Users/zzet/x", "Get") {
t.Errorf("looksLikeRoute(/Users/zzet/x) accepted a home path as a route")
}
if !looksLikeRoute("/api/users", "Get") {
t.Errorf("looksLikeRoute(/api/users) wrongly rejected a real route")
}
if !looksLikeRoute("GET /api/orders", "HandleFunc") {
t.Errorf("looksLikeRoute(GET /api/orders) wrongly rejected a net/http route")
}
}
func TestGoStrings_FilesystemPathNotRoute(t *testing.T) {
// A route-registration call whose literal is a filesystem/config path no
// longer mints a route node, while a real route in the same file still does.
src := `package foo
type Mux struct{}
func (m *Mux) HandleFunc(pattern string, h func()) {}
func Wire(m *Mux) {
m.HandleFunc("/etc/app.conf", nil)
m.HandleFunc("/api/v1/users", nil)
}
`
fix := runGoExtract(t, src)
strs := fix.nodesByKind[graph.KindString]
if len(strs) != 1 {
t.Fatalf("expected exactly 1 route (the config path filtered), got %d: %+v", len(strs), strs)
}
if strs[0].Name != "/api/v1/users" {
t.Errorf("expected surviving route /api/v1/users, got %q", strs[0].Name)
}
}