项目文件夹

文件
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

139 行
5.6 KiB
Go

package mcp
import (
"context"
"encoding/json"
"testing"
mcplib "github.com/mark3labs/mcp-go/mcp"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"github.com/zzet/gortex/internal/config"
"github.com/zzet/gortex/internal/graph"
"github.com/zzet/gortex/internal/query"
"github.com/zzet/gortex/internal/search"
)
// equivalenceTestServer builds a single-repo server with no LLM
// provider, so any expansion observed comes purely from the
// deterministic equivalence channel.
func equivalenceTestServer(t *testing.T, names []string, equivEnabled *bool) *Server {
t.Helper()
g := graph.New()
bm := search.NewBM25()
for i, n := range names {
id := "pkg/" + n + ".go::" + n
g.AddNode(&graph.Node{
ID: id, Kind: graph.KindFunction, Name: n,
FilePath: "pkg/" + n + ".go", StartLine: i + 1, EndLine: i + 5, Language: "go",
})
bm.Add(id, n, "pkg/"+n+".go", "")
}
eng := query.NewEngine(g)
eng.SetSearch(bm)
srv := NewServer(eng, g, nil, nil, zap.NewNop(), nil)
srv.SetSearchConfig(config.SearchConfig{EquivalenceClasses: equivEnabled})
srv.RunAnalysis() // builds the auto-concept vocabulary
return srv
}
func searchIDs(t *testing.T, srv *Server, args map[string]any) map[string]bool {
t.Helper()
req := mcplib.CallToolRequest{}
req.Params.Name = "search_symbols"
req.Params.Arguments = args
res, err := srv.handleSearchSymbols(context.Background(), req)
require.NoError(t, err)
require.Falsef(t, res.IsError, "search errored: %v", res.Content)
var resp map[string]any
require.NoError(t, json.Unmarshal([]byte(res.Content[0].(mcplib.TextContent).Text), &resp))
ids := map[string]bool{}
if results, ok := resp["results"].([]any); ok {
for _, r := range results {
ids[r.(map[string]any)["id"].(string)] = true
}
}
return ids
}
// TestSearchSymbols_EquivalenceBridgesVocabulary is the core feature
// test: a query for "auth" must reach a LoginService symbol via the
// curated equivalence class -- with NO LLM provider configured.
func TestSearchSymbols_EquivalenceBridgesVocabulary(t *testing.T) {
srv := equivalenceTestServer(t, []string{
"LoginService", "SigninController", "ParseConfig", "UnrelatedThing",
}, nil) // nil EquivalenceClasses == enabled by default
ids := searchIDs(t, srv, map[string]any{"query": "auth"})
require.Truef(t, ids["pkg/LoginService.go::LoginService"],
"equivalence expansion should bridge 'auth' to LoginService; got %v", ids)
require.Truef(t, ids["pkg/SigninController.go::SigninController"],
"equivalence expansion should bridge 'auth' to SigninController; got %v", ids)
}
// TestSearchSymbols_EquivalenceDeleteRemove confirms the delete/remove
// bridge works in both directions through the BM25 OR-merge.
func TestSearchSymbols_EquivalenceDeleteRemove(t *testing.T) {
srv := equivalenceTestServer(t, []string{
"RemoveUser", "DropTable", "FetchProfile",
}, nil)
ids := searchIDs(t, srv, map[string]any{"query": "delete"})
require.Truef(t, ids["pkg/RemoveUser.go::RemoveUser"],
"'delete' should bridge to RemoveUser; got %v", ids)
require.Truef(t, ids["pkg/DropTable.go::DropTable"],
"'delete' should bridge to DropTable; got %v", ids)
}
// TestSearchSymbols_ExpandOffDisablesEquivalence confirms expand:"off"
// suppresses the equivalence channel -- "auth" then matches nothing.
func TestSearchSymbols_ExpandOffDisablesEquivalence(t *testing.T) {
srv := equivalenceTestServer(t, []string{"LoginService", "SigninController"}, nil)
ids := searchIDs(t, srv, map[string]any{"query": "auth", "expand": "off"})
require.Falsef(t, ids["pkg/LoginService.go::LoginService"],
"expand:off must not bridge 'auth' to LoginService; got %v", ids)
}
// TestSearchSymbols_EquivalenceDisabledViaConfig confirms the config
// toggle (equivalence_classes:false) disables the channel.
func TestSearchSymbols_EquivalenceDisabledViaConfig(t *testing.T) {
off := false
srv := equivalenceTestServer(t, []string{"LoginService"}, &off)
ids := searchIDs(t, srv, map[string]any{"query": "auth"})
require.Falsef(t, ids["pkg/LoginService.go::LoginService"],
"equivalence_classes:false must disable the channel; got %v", ids)
}
// TestSearchSymbols_EquivalenceAutoConcepts confirms the per-repo
// auto-mined concept vocabulary bridges a domain phrase: a query for
// "blast" reaches a symbol named only with "radius" because the two
// words co-occur across the repo's symbol names.
func TestSearchSymbols_EquivalenceAutoConcepts(t *testing.T) {
srv := equivalenceTestServer(t, []string{
"handleBlastRadius", "blastRadiusOf", "BlastRadiusReport",
"computeBlastRadius", "radiusOnlySymbol",
}, nil)
ids := searchIDs(t, srv, map[string]any{"query": "blast"})
require.Truef(t, ids["pkg/radiusOnlySymbol.go::radiusOnlySymbol"],
"auto-concept mining should bridge 'blast' to a radius-named symbol; got %v", ids)
}
// TestSearchSymbols_ConceptThesaurusBridge confirms the concept-
// relatedness thesaurus bridges an adjacent (non-synonym) concept
// under expand:"equivalence" with NO LLM provider: a query for "auth"
// reaches a symbol named only with "token".
func TestSearchSymbols_ConceptThesaurusBridge(t *testing.T) {
srv := equivalenceTestServer(t, []string{
"LoginService", "TokenStore", "UnrelatedThing",
}, nil)
ids := searchIDs(t, srv, map[string]any{"query": "auth", "expand": "equivalence"})
// Direct synonym bridge still works.
require.Truef(t, ids["pkg/LoginService.go::LoginService"],
"synonym bridge auth -> LoginService should still hold; got %v", ids)
// Thesaurus (adjacent-concept) bridge: auth relates to the token
// class, so TokenStore is reachable even though "token" is not a
// synonym of "auth".
require.Truef(t, ids["pkg/TokenStore.go::TokenStore"],
"thesaurus bridge auth -> token -> TokenStore should hold; got %v", ids)
}