项目文件夹

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

107 行
4.2 KiB
Go

此文件含有模棱两可的 Unicode 字符
此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。
package rerank
import (
"testing"
"github.com/zzet/gortex/internal/graph"
)
func TestAlphaForContinuous_AnchorsAndBounds(t *testing.T) {
cases := []struct {
query string
want float64
}{
{"FooBar", AlphaSymbol}, // single identifier token
{"validate_token", AlphaSymbol}, // snake_case identifier
{"what does this do", AlphaNL}, // prose, stopword-heavy
{"", AlphaNL}, // empty
{"internal/auth/token.go", AlphaPath}, // path shape
{"func(ctx) error", AlphaSignature}, // signature shape
}
for _, c := range cases {
if got := AlphaForContinuous(c.query); got != c.want {
t.Errorf("AlphaForContinuous(%q) = %v, want %v", c.query, got, c.want)
}
}
// Every result stays within the discrete envelope [AlphaPath, AlphaNL].
for _, q := range []string{"validateToken handler", "auth middleware", "parse the jwt cache token please"} {
got := AlphaForContinuous(q)
if got < AlphaPath || got > AlphaNL {
t.Errorf("AlphaForContinuous(%q) = %v, out of [%v,%v]", q, got, AlphaPath, AlphaNL)
}
}
}
func TestAlphaForContinuous_Monotonic(t *testing.T) {
// More identifier-shaped → lower α (more BM25-leaning).
twoIdents := AlphaForContinuous("validateToken parseJWT")
mixed := AlphaForContinuous("validateToken handler")
prose := AlphaForContinuous("validate the user token")
if !(twoIdents <= mixed && mixed <= prose) {
t.Errorf("expected α to rise with prose-ness: twoIdents=%v mixed=%v prose=%v", twoIdents, mixed, prose)
}
if twoIdents != AlphaSymbol {
t.Errorf("two pure identifiers should pin to AlphaSymbol, got %v", twoIdents)
}
}
func TestContinuousClassMultiplier_AnchorsAndMonotonic(t *testing.T) {
// NL anchor reproduces the neutral 1.0/1.0 baseline exactly.
if got := continuousClassMultiplier(AlphaNL, SignalBM25); got != 1.0 {
t.Errorf("bm25 mult at AlphaNL = %v, want 1.0", got)
}
if got := continuousClassMultiplier(AlphaNL, SignalSemantic); got != 1.0 {
t.Errorf("semantic mult at AlphaNL = %v, want 1.0", got)
}
// Path anchor reproduces the discrete path-class multipliers.
if got := continuousClassMultiplier(AlphaPath, SignalBM25); !floatNear(got, classWeightTable[QueryClassPath].bm25, 1e-9) {
t.Errorf("bm25 mult at AlphaPath = %v, want %v", got, classWeightTable[QueryClassPath].bm25)
}
if got := continuousClassMultiplier(AlphaPath, SignalSemantic); !floatNear(got, classWeightTable[QueryClassPath].semantic, 1e-9) {
t.Errorf("semantic mult at AlphaPath = %v, want %v", got, classWeightTable[QueryClassPath].semantic)
}
// Non-text signals are never class-scaled.
if got := continuousClassMultiplier(AlphaPath, SignalFanIn); got != 1.0 {
t.Errorf("fan_in mult = %v, want 1.0 (only bm25/semantic are class-sensitive)", got)
}
// Monotonic: lowering α raises bm25 and lowers semantic.
if !(continuousClassMultiplier(AlphaSymbol, SignalBM25) > continuousClassMultiplier(AlphaNL, SignalBM25)) {
t.Errorf("lower α must raise bm25 multiplier")
}
if !(continuousClassMultiplier(AlphaSymbol, SignalSemantic) < continuousClassMultiplier(AlphaNL, SignalSemantic)) {
t.Errorf("lower α must lower semantic multiplier")
}
}
func TestRerank_ContinuousAlphaTunesTextVsSemantic(t *testing.T) {
g := newTestGraph()
textOnly := mustNode(g, "f.go::TextOnly", "TextOnly", graph.KindFunction)
vecOnly := mustNode(g, "f.go::VecOnly", "VecOnly", graph.KindFunction)
weights := map[string]float64{SignalBM25: 1.0, SignalSemantic: 1.0}
p := New(DefaultSignals(), weights)
score := func(alpha float64, id string) float64 {
cands := []*Candidate{
{Node: textOnly, TextRank: 0, VectorRank: -1},
{Node: vecOnly, TextRank: -1, VectorRank: 0},
}
p.Rerank("q", cands, &Context{Graph: g, Alpha: alpha})
for _, c := range cands {
if c.Node.ID == id {
return c.Score
}
}
t.Fatalf("candidate %s missing from rerank output", id)
return 0
}
// A BM25-leaning α (path end) lifts the text-only candidate and
// trims the semantic-only one relative to the neutral NL α.
if !(score(AlphaPath, textOnly.ID) > score(AlphaNL, textOnly.ID)) {
t.Errorf("low α must raise the text-only candidate's score")
}
if !(score(AlphaPath, vecOnly.ID) < score(AlphaNL, vecOnly.ID)) {
t.Errorf("low α must lower the semantic-only candidate's score")
}
}