项目文件夹

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

168 行
6.3 KiB
Go

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
package mcp
import (
"path/filepath"
"slices"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestKeywordTokens_DropsStopWordsAndGenerics(t *testing.T) {
got := keywordTokens("how do we validate the auth token function")
// Function words (how, do, we, the) and generic nouns (function)
// are dropped; the meaningful keywords survive.
for _, want := range []string{"validate", "auth", "token"} {
if !slices.Contains(got, want) {
t.Errorf("keywordTokens missing %q; got %v", want, got)
}
}
for _, drop := range []string{"how", "do", "the", "function", "we"} {
if slices.Contains(got, drop) {
t.Errorf("keywordTokens should have dropped %q; got %v", drop, got)
}
}
// camelCase queries split into component tokens.
cc := keywordTokens("validateAuthToken")
for _, want := range []string{"validate", "auth", "token"} {
if !slices.Contains(cc, want) {
t.Errorf("camelCase keyword split missing %q; got %v", want, cc)
}
}
}
// TestKeywordBoost_CrossQueryInheritance is the core feature test: a
// symbol recorded under one phrasing is inherited by a later query
// that shares keywords but is phrased differently.
func TestKeywordBoost_CrossQueryInheritance(t *testing.T) {
cm := &comboManager{now: func() int64 { return 1 }}
// Train: "validate auth token" leads to sym-auth, recorded enough
// times to clear the per-keyword min-hits gate.
for i := 0; i < 3; i++ {
cm.Record("validate auth token", "sym-auth")
}
// A differently-phrased query that shares the auth/token keywords.
// Its whole-query key never matched, so the exact-combo path is
// silent...
assert.Nil(t, cm.BoostMap("check auth token here"),
"a never-seen whole query should have no exact-combo boost")
// ...but the per-keyword path inherits the association.
kw := cm.KeywordBoostMap("check auth token here")
require.NotNil(t, kw, "keyword boost should inherit across phrasings")
assert.Greater(t, kw["sym-auth"], 1.0,
"sym-auth should be boosted via its auth/token keyword associations")
}
// TestKeywordBoost_CoverageScaling confirms a symbol matching more of
// the query's keywords gets a larger boost than one matching fewer.
func TestKeywordBoost_CoverageScaling(t *testing.T) {
cm := &comboManager{now: func() int64 { return 1 }}
// sym-full is recorded under all three keywords; sym-partial under
// only one. Record each enough to clear the gate.
for i := 0; i < 4; i++ {
cm.Record("parse encode validate", "sym-full")
cm.Record("validate", "sym-partial")
}
kw := cm.KeywordBoostMap("parse encode validate")
require.NotNil(t, kw)
require.Contains(t, kw, "sym-full")
require.Contains(t, kw, "sym-partial")
assert.Greater(t, kw["sym-full"], kw["sym-partial"],
"a symbol covering more query keywords should out-boost one covering fewer")
}
// TestKeywordBoost_CoverageIsLinear pins the boost above 1.0 to scale
// linearly with coverage when per-keyword strength is equal: a symbol
// matching 1 of 3 keywords earns ~1/3 of the full-coverage boost. Guards
// against the super-linear (coverage-times-summed-hits) regression.
func TestKeywordBoost_CoverageIsLinear(t *testing.T) {
cm := &comboManager{now: func() int64 { return 1 }}
// Equal per-keyword strength: sym-full under all 3 keywords, sym-partial
// under 1, each recorded the same number of times.
for i := 0; i < 4; i++ {
cm.Record("parse encode validate", "sym-full")
cm.Record("validate", "sym-partial")
}
kw := cm.KeywordBoostMap("parse encode validate")
require.NotNil(t, kw)
full := kw["sym-full"] - 1.0
partial := kw["sym-partial"] - 1.0
require.Greater(t, full, 0.0)
require.Greater(t, partial, 0.0)
// 3 keywords ⇒ full coverage boost is ~3× the 1-of-3 boost.
assert.InDelta(t, 3.0, full/partial, 0.05,
"partial-coverage boost should be ~1/N of full coverage (linear, not super-linear)")
}
// TestKeywordBoost_ExactQueryDominates confirms the exact whole-query
// combo boost out-ranks a keyword-only boost when both fire -- the
// keyword boost is capped below comboMaxBoost.
func TestKeywordBoost_ExactQueryDominates(t *testing.T) {
cm := &comboManager{now: func() int64 { return 1 }}
// Heavily train an exact query -> exact boost saturates near
// comboMaxBoost.
for i := 0; i < 50; i++ {
cm.Record("auth token flow", "sym-x")
}
exact := cm.BoostMap("auth token flow")
keyword := cm.KeywordBoostMap("auth token flow")
require.NotNil(t, exact)
require.NotNil(t, keyword)
assert.Greater(t, exact["sym-x"], keyword["sym-x"],
"exact whole-query combo must dominate the keyword-only boost")
assert.LessOrEqual(t, keyword["sym-x"], keywordMaxBoost+1e-9,
"keyword boost must stay under its cap")
}
// TestKeywordBoost_BelowGate confirms a keyword association below the
// min-hits gate does not boost.
func TestKeywordBoost_BelowGate(t *testing.T) {
cm := &comboManager{now: func() int64 { return 1 }}
cm.Record("auth token", "sym-q") // a single hit -- below keywordMinHits
assert.Nil(t, cm.KeywordBoostMap("auth token"),
"a single keyword hit should not clear the gate")
}
// TestKeywordBoost_Decay confirms stale keyword associations are
// reaped on access, just like the exact-combo store.
func TestKeywordBoost_Decay(t *testing.T) {
now := int64(1_000_000_000)
cm := &comboManager{mode: ModeAI, now: func() int64 { return now }}
for i := 0; i < 3; i++ {
cm.Record("auth token", "sym-stale")
}
require.NotNil(t, cm.KeywordBoostMap("auth token"), "fresh association should boost")
// Jump past the AI max-age window.
now += comboMaxAgeAISec + 1
assert.Nil(t, cm.KeywordBoostMap("auth token"),
"a keyword association past the decay window must be reaped")
}
// TestKeywordBoost_Persistence confirms the keyword store survives a
// manager reload from disk.
func TestKeywordBoost_Persistence(t *testing.T) {
cacheDir := t.TempDir()
repoPath := filepath.Join(t.TempDir(), "repo")
cm := newComboManager(cacheDir, repoPath, ModeAI)
for i := 0; i < 3; i++ {
cm.Record("validate auth token", "sym-persist")
}
cm2 := newComboManager(cacheDir, repoPath, ModeAI)
kw := cm2.KeywordBoostMap("check auth token now")
require.NotNil(t, kw, "keyword associations should survive a reload")
assert.Greater(t, kw["sym-persist"], 1.0)
}
// TestKeywordBoost_NilSafe confirms a nil manager is safe.
func TestKeywordBoost_NilSafe(t *testing.T) {
var cm *comboManager
assert.Nil(t, cm.KeywordBoostMap("auth token"))
}