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
105 lines
3.8 KiB
Go
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")
|
|
}
|
|
}
|