Files
zzet--gortex/internal/mcp/tools_analyze_new_kinds_test.go
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

181 lines
6.8 KiB
Go

package mcp
import (
"context"
"os"
"os/exec"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/graph"
)
// --- env_var_users ---------------------------------------------------------
func TestAnalyzeEnvVarUsers_FiltersToEnvKeys(t *testing.T) {
srv, _ := setupTestServer(t)
addConfigKeyNode(srv.graph, "cfg::env::DATABASE_URL", "DATABASE_URL", "env")
addConfigKeyNode(srv.graph, "viper::log.level", "log.level", "viper")
addReadConfigEdge(srv.graph, "f.go::A", "cfg::env::DATABASE_URL")
addReadConfigEdge(srv.graph, "f.go::B", "cfg::env::DATABASE_URL")
addReadConfigEdge(srv.graph, "f.go::C", "viper::log.level")
out := callAnalyze(t, srv, "env_var_users", map[string]any{})
rows, _ := out["env_vars"].([]any)
require.Len(t, rows, 1, "viper (non-env) config key must be excluded")
row := rows[0].(map[string]any)
require.Equal(t, "DATABASE_URL", row["name"])
require.Equal(t, float64(2), row["reads"])
}
func TestAnalyzeEnvVarUsers_NameFilter(t *testing.T) {
srv, _ := setupTestServer(t)
addConfigKeyNode(srv.graph, "env::PORT", "PORT", "env")
addConfigKeyNode(srv.graph, "env::HOST", "HOST", "env")
addReadConfigEdge(srv.graph, "f.go::A", "env::PORT")
addReadConfigEdge(srv.graph, "f.go::B", "env::HOST")
out := callAnalyze(t, srv, "env_var_users", map[string]any{"name": "port"})
require.Equal(t, float64(1), out["total"])
}
// --- sql_call_sites --------------------------------------------------------
func TestAnalyzeSQLCallSites_GroupsByCaller(t *testing.T) {
srv, _ := setupTestServer(t)
srv.graph.AddNode(&graph.Node{ID: "f.go::GetUser", Kind: graph.KindFunction, Name: "GetUser", FilePath: "f.go"})
srv.graph.AddNode(&graph.Node{ID: "tbl::users", Kind: graph.KindTable, Name: "users"})
srv.graph.AddNode(&graph.Node{ID: "tbl::orders", Kind: graph.KindTable, Name: "orders"})
srv.graph.AddEdge(&graph.Edge{From: "f.go::GetUser", To: "tbl::users", Kind: graph.EdgeQueries, Meta: map[string]any{"op": "read"}})
srv.graph.AddEdge(&graph.Edge{From: "f.go::GetUser", To: "tbl::orders", Kind: graph.EdgeQueries, Meta: map[string]any{"op": "write"}})
out := callAnalyze(t, srv, "sql_call_sites", map[string]any{})
rows, _ := out["call_sites"].([]any)
require.Len(t, rows, 1)
row := rows[0].(map[string]any)
require.Equal(t, "GetUser", row["name"])
require.Equal(t, float64(2), row["queries"])
require.Equal(t, float64(1), row["reads"])
require.Equal(t, float64(1), row["writes"])
}
// --- fixes_history ---------------------------------------------------------
func mustGit(t *testing.T, dir string, args ...string) {
t.Helper()
cmd := exec.Command("git", append([]string{"-C", dir}, args...)...)
cmd.Env = append(cmd.Environ(),
"GIT_AUTHOR_NAME=t", "GIT_AUTHOR_EMAIL=t@t.test",
"GIT_COMMITTER_NAME=t", "GIT_COMMITTER_EMAIL=t@t.test")
if out, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("git %v: %v\n%s", args, err, out)
}
}
func TestFixSubjectRe(t *testing.T) {
for _, s := range []string{"fix: x", "fixes #3", "fixed the bug", "bugfix in y", "hotfix", "FIX something"} {
require.True(t, fixSubjectRe.MatchString(s), "%q should be a fix subject", s)
}
for _, s := range []string{"add feature", "prefix handling", "refactor fixtures", "update docs"} {
require.False(t, fixSubjectRe.MatchString(s), "%q should NOT be a fix subject", s)
}
}
func TestMineFixCommits_DetectsFixSubjects(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git not available")
}
dir := t.TempDir()
mustGit(t, dir, "init", "-q")
commit := func(body, msg string) {
require.NoError(t, os.WriteFile(filepath.Join(dir, "a.go"), []byte(body), 0o644))
mustGit(t, dir, "add", "a.go")
mustGit(t, dir, "commit", "-q", "-m", msg)
}
commit("package a\n", "add feature a")
commit("package a\n// v2\n", "fix: nil deref in a")
commit("package a\n// v3\n", "fixes crash on startup")
commits := mineFixCommits(context.Background(), dir, 100)
require.Len(t, commits, 2, "two fix commits, the plain-feature commit excluded")
for _, c := range commits {
require.Contains(t, c.files, "a.go")
}
}
func TestAnalyzeFixesHistory_EndToEnd(t *testing.T) {
if _, err := exec.LookPath("git"); err != nil {
t.Skip("git not available")
}
srv, dir := setupTestServer(t)
mustGit(t, dir, "init", "-q")
mustGit(t, dir, "add", "main.go")
mustGit(t, dir, "commit", "-q", "-m", "initial commit")
require.NoError(t, os.WriteFile(filepath.Join(dir, "main.go"),
[]byte("package main\n\nfunc main() {}\n"), 0o644))
mustGit(t, dir, "add", "main.go")
mustGit(t, dir, "commit", "-q", "-m", "fix: correct main logic")
out := callAnalyze(t, srv, "fixes_history", map[string]any{})
if got, _ := out["total_fix_commits"].(float64); got < 1 {
t.Fatalf("expected >=1 fix commit, got %v", got)
}
files, _ := out["files"].([]any)
require.NotEmpty(t, files)
require.Equal(t, "main.go", files[0].(map[string]any)["file"])
}
func TestSymbolNamesInFile(t *testing.T) {
srv, _ := setupTestServer(t)
// setupTestServer indexes a main.go containing main + helper.
require.NotEmpty(t, srv.symbolNamesInFile("main.go"))
}
// --- edge_audit ------------------------------------------------------------
func TestAnalyzeEdgeAudit_Buckets(t *testing.T) {
srv, _ := setupTestServer(t)
g := srv.graph
// An interface with no implementor.
g.AddNode(&graph.Node{ID: "z.go::Orphan", Kind: graph.KindInterface, Name: "Orphan"})
// An interface with an implementor — must NOT be flagged.
g.AddNode(&graph.Node{ID: "z.go::Used", Kind: graph.KindInterface, Name: "Used"})
g.AddNode(&graph.Node{ID: "z.go::Impl", Kind: graph.KindType, Name: "Impl"})
g.AddEdge(&graph.Edge{From: "z.go::Impl", To: "z.go::Used", Kind: graph.EdgeImplements})
// A function reached only from a test file, via a text-matched edge.
g.AddNode(&graph.Node{ID: "z.go::Helper", Kind: graph.KindFunction, Name: "Helper", FilePath: "z.go"})
g.AddNode(&graph.Node{ID: "z_test.go::TestZ", Kind: graph.KindFunction, Name: "TestZ", FilePath: "z_test.go"})
g.AddEdge(&graph.Edge{
From: "z_test.go::TestZ", To: "z.go::Helper",
Kind: graph.EdgeCalls, Origin: graph.OriginTextMatched,
})
out := callAnalyze(t, srv, "edge_audit", map[string]any{})
require.Contains(t, out, "edge_tiers")
require.Contains(t, out, "summary")
ui := out["unimplemented_interfaces"].(map[string]any)
require.GreaterOrEqual(t, ui["count"].(float64), float64(1))
require.NotContains(t, sampleStrings(ui), "z.go::Used")
to := out["test_only_targets"].(map[string]any)
require.Contains(t, sampleStrings(to), "z.go::Helper")
wc := out["weak_call_edges"].(map[string]any)
require.GreaterOrEqual(t, wc["count"].(float64), float64(1))
}
func sampleStrings(bucket map[string]any) []string {
raw, _ := bucket["sample"].([]any)
out := make([]string, 0, len(raw))
for _, v := range raw {
if s, ok := v.(string); ok {
out = append(out, s)
}
}
return out
}