Files
zzet--gortex/internal/mcp/tools_search_docchannel_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

105 lines
3.8 KiB
Go

package mcp
import (
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"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/indexer"
"github.com/zzet/gortex/internal/query"
)
// docChannelServer indexes MANY code symbols whose names share the
// query token "deploy", plus a single Markdown doc whose Deployment
// section is about deploying. The flood of code hits is what would
// crowd the lone prose section out of a tight primary fetch -- the
// docs retrieval channel must rescue it.
func docChannelServer(t *testing.T) *Server {
t.Helper()
dir := t.TempDir()
var b strings.Builder
b.WriteString("package app\n\n")
for i := 0; i < 40; i++ {
// Every function name contains "deploy" so a "deploy" query
// matches all of them on the code side.
b.WriteString("func Deploy" + strconv.Itoa(i) + "Service() {}\n")
}
require.NoError(t, os.WriteFile(filepath.Join(dir, "deploy.go"), []byte(b.String()), 0o644))
require.NoError(t, os.WriteFile(filepath.Join(dir, "GUIDE.md"),
[]byte("# Guide\n\n## Deployment\n\n"+
"To deploy the service push the container image and apply the manifest.\n"), 0o644))
g := graph.New()
reg := testRegistry()
cfg := config.Default()
idx := indexer.New(g, reg, cfg.Index, zap.NewNop())
_, err := idx.Index(dir)
require.NoError(t, err)
eng := query.NewEngine(g)
eng.SetSearchProvider(idx.Search)
return NewServer(eng, g, idx, nil, zap.NewNop(), nil)
}
// TestSearchSymbols_DocChannelRescuesCrowdedProse is the core feature
// test: with a tight limit, a flood of "deploy"-named code symbols
// would crowd the single Deployment prose section out of the primary
// fetch. The docs retrieval channel over-fetches and merges the prose
// hit, so corpus:docs still returns it.
func TestSearchSymbols_DocChannelRescuesCrowdedProse(t *testing.T) {
srv := docChannelServer(t)
// A very tight limit. Under a pure post-filter over one
// code-shaped fetch, the prose section (one doc among 40 code
// hits) would never enter the candidate pool.
docs := corpusSearch(t, srv, map[string]any{
"query": "deploy", "corpus": "docs", "limit": float64(3),
})
require.NotEmpty(t, docs, "the docs channel should surface the prose section despite the code flood")
foundDeployment := false
for _, d := range docs {
require.Equal(t, "doc", d["kind"], "corpus:docs must return only doc-kind nodes")
if name, _ := d["name"].(string); strings.Contains(name, "Deployment") {
foundDeployment = true
}
}
require.True(t, foundDeployment, "the Deployment prose section must be retrieved via the docs channel")
}
// TestSearchSymbols_DocChannelAllIncludesProse confirms corpus:all
// also admits the prose section through the channel even under a tight
// limit dominated by code hits.
func TestSearchSymbols_DocChannelAllIncludesProse(t *testing.T) {
srv := docChannelServer(t)
all := corpusSearch(t, srv, map[string]any{
"query": "deploy", "corpus": "all", "limit": float64(5),
})
require.NotEmpty(t, all)
kinds := map[string]bool{}
for _, r := range all {
kinds[r["kind"].(string)] = true
}
require.True(t, kinds["doc"], "corpus:all should include the prose section via the docs channel")
require.True(t, kinds["function"], "corpus:all should still include code hits")
}
// TestSearchSymbols_DocChannelInertForCode confirms the docs channel
// does NOT fire for the default code corpus -- a code-only query pays
// no extra fetch and returns no doc nodes.
func TestSearchSymbols_DocChannelInertForCode(t *testing.T) {
srv := docChannelServer(t)
code := corpusSearch(t, srv, map[string]any{"query": "deploy", "limit": float64(5)})
require.NotEmpty(t, code)
for _, c := range code {
require.NotEqual(t, "doc", c["kind"], "corpus:code must never return prose-section nodes")
}
}