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

152 lines
4.2 KiB
Go

package mcp
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestCountFromResultText(t *testing.T) {
cases := []struct {
name string
text string
want int
}{
{"empty", "", 0},
{"json_total", `{"todos":[{"a":1}],"total":7}`, 7},
{"json_symbols_array", `{"symbols":[{"id":"a"},{"id":"b"},{"id":"c"}]}`, 3},
{"json_results_zero", `{"results":[]}`, 0},
{"json_relevant_symbols", `{"task":"x","relevant_symbols":[{"id":"a"},{"id":"b"}]}`, 2},
{"json_top_array", `[{"id":"a"},{"id":"b"}]`, 2},
{"not_modified", `{"not_modified":true,"etag":"abc"}`, 0},
{"gcx_rows", "GCX1 tool=search_symbols fields=id,name\nrow1\nrow2\nrow3\n", 3},
{"toon_text", "first line\nsecond line\n", 2},
{"largest_array_wins", `{"a":[1],"b":[1,2,3,4]}`, 4},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
if got := countFromResultText(c.text); got != c.want {
t.Fatalf("countFromResultText(%q) = %d, want %d", c.text, got, c.want)
}
})
}
}
func TestFirstStringArg(t *testing.T) {
args := map[string]any{
"id": "",
"symbol": "pkg/foo.go::Bar",
"seeds": []any{"a", "b", "c"},
"n": float64(5),
}
if got := firstStringArg(args, []string{"id", "symbol"}); got != "pkg/foo.go::Bar" {
t.Fatalf("firstStringArg id->symbol = %q", got)
}
if got := firstStringArg(args, []string{"seeds"}); got != "a,b,c" {
t.Fatalf("firstStringArg seeds = %q", got)
}
if got := firstStringArg(args, []string{"missing"}); got != "" {
t.Fatalf("firstStringArg missing = %q", got)
}
}
func TestQueryLoggerAppendAndTail(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "query-log.jsonl")
t.Setenv("GORTEX_QUERY_LOG", path)
t.Setenv("GORTEX_QUERY_LOG_DISABLE", "")
q := newQueryLogger()
if !q.enabled {
t.Fatal("logger should be enabled")
}
if q.Path() != path {
t.Fatalf("path = %q, want %q", q.Path(), path)
}
for i := 0; i < 5; i++ {
q.append([]byte(`{"ts":"2026-06-05T00:00:0` + string(rune('0'+i)) + `Z","tool":"search_symbols","question":"q","nodes_returned":0,"zero_result":true,"ok":true}`))
}
q.Close()
records, scanned, err := readQueryLogTail(path, 100)
if err != nil {
t.Fatalf("readQueryLogTail: %v", err)
}
if scanned != 5 || len(records) != 5 {
t.Fatalf("scanned=%d records=%d, want 5/5", scanned, len(records))
}
zero := 0
for _, r := range records {
if r.Tool != "search_symbols" {
t.Fatalf("unexpected tool %q", r.Tool)
}
if r.ZeroResult {
zero++
}
}
if zero != 5 {
t.Fatalf("zero=%d, want 5", zero)
}
// Tail limit keeps the newest N.
tail, _, _ := readQueryLogTail(path, 2)
if len(tail) != 2 {
t.Fatalf("tail len=%d, want 2", len(tail))
}
if !strings.Contains(tail[1].TS, "04") {
t.Fatalf("tail should end with newest record, got %q", tail[1].TS)
}
}
func TestQueryLoggerDisabled(t *testing.T) {
t.Setenv("GORTEX_QUERY_LOG_DISABLE", "1")
q := newQueryLogger()
if q.enabled {
t.Fatal("logger should be disabled")
}
if q.shouldLog("search_symbols") {
t.Fatal("disabled logger should not log")
}
if q.Path() != "" {
t.Fatalf("disabled logger path = %q, want empty", q.Path())
}
}
func TestQueryLoggerRotation(t *testing.T) {
dir := t.TempDir()
path := filepath.Join(dir, "query-log.jsonl")
t.Setenv("GORTEX_QUERY_LOG", path)
t.Setenv("GORTEX_QUERY_LOG_DISABLE", "")
q := newQueryLogger()
q.maxBytes = 200 // force rotation after a couple of lines
for i := 0; i < 20; i++ {
q.append([]byte(`{"ts":"t","tool":"search_symbols","question":"some longer question text here"}`))
}
q.Close()
if _, err := os.Stat(path + ".1"); err != nil {
t.Fatalf("expected rotated backup at %s.1: %v", path, err)
}
if fi, err := os.Stat(path); err != nil || fi.Size() == 0 {
t.Fatalf("expected non-empty live log after rotation: %v", err)
}
}
func TestShouldLogToolSet(t *testing.T) {
t.Setenv("GORTEX_QUERY_LOG", filepath.Join(t.TempDir(), "q.jsonl"))
t.Setenv("GORTEX_QUERY_LOG_DISABLE", "")
q := newQueryLogger()
for _, tool := range []string{"search_symbols", "smart_context", "find_usages", "search_text"} {
if !q.shouldLog(tool) {
t.Fatalf("%s should be logged", tool)
}
}
for _, tool := range []string{"edit_file", "graph_stats", "store_memory"} {
if q.shouldLog(tool) {
t.Fatalf("%s should NOT be logged", tool)
}
}
}