zzet--gortex
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
93 行
2.9 KiB
Go
93 行
2.9 KiB
Go
package rerank
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/zzet/gortex/internal/graph"
|
|
)
|
|
|
|
func cand(id string, textRank, vectorRank int) *Candidate {
|
|
return &Candidate{Node: &graph.Node{ID: id}, TextRank: textRank, VectorRank: vectorRank}
|
|
}
|
|
|
|
func TestSelectCentralitySeeds(t *testing.T) {
|
|
cands := []*Candidate{
|
|
cand("c", 2, -1),
|
|
cand("a", 0, -1),
|
|
cand("b", 1, -1),
|
|
cand("v", -1, 0), // vector-only, best vector rank
|
|
cand("z", -1, -1),
|
|
}
|
|
got := selectCentralitySeeds(cands, 3)
|
|
want := []string{"a", "v", "b"} // ranks 0, 0, 1 — ties break on ID
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("seeds = %v, want %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestProximitySignalNormalisesAndScores(t *testing.T) {
|
|
cands := []*Candidate{
|
|
cand("a", 0, -1),
|
|
cand("b", 1, -1),
|
|
cand("c", 2, -1),
|
|
}
|
|
called := false
|
|
ctx := &Context{
|
|
Centrality: func(seeds []string) map[string]float64 {
|
|
called = true
|
|
// seeds should be the top-ranked candidates.
|
|
if len(seeds) == 0 || seeds[0] != "a" {
|
|
t.Errorf("unexpected seeds %v", seeds)
|
|
}
|
|
// Raw, un-normalised scores; prepare must rescale to [0,1].
|
|
return map[string]float64{"a": 10, "b": 5, "c": 0}
|
|
},
|
|
}
|
|
ctx.prepare(cands)
|
|
if !called {
|
|
t.Fatal("Centrality closure was not invoked")
|
|
}
|
|
|
|
sig := ProximitySignal{}
|
|
if got := sig.Contribute("", cands[0], ctx); got != 1.0 {
|
|
t.Fatalf("proximity(a) = %v, want 1.0 (batch max)", got)
|
|
}
|
|
if got := sig.Contribute("", cands[1], ctx); got != 0.5 {
|
|
t.Fatalf("proximity(b) = %v, want 0.5", got)
|
|
}
|
|
if got := sig.Contribute("", cands[2], ctx); got != 0 {
|
|
t.Fatalf("proximity(c) = %v, want 0 (zero raw dropped)", got)
|
|
}
|
|
}
|
|
|
|
func TestProximitySignalNilProviderDegrades(t *testing.T) {
|
|
cands := []*Candidate{cand("a", 0, -1)}
|
|
ctx := &Context{} // no Centrality wired
|
|
ctx.prepare(cands)
|
|
if got := (ProximitySignal{}).Contribute("", cands[0], ctx); got != 0 {
|
|
t.Fatalf("proximity with no provider = %v, want 0", got)
|
|
}
|
|
}
|
|
|
|
func TestProximityClassMultiplier(t *testing.T) {
|
|
// Concept queries lean hardest on proximity; path/symbol dampen it.
|
|
concept := ClassWeightMultiplier(QueryClassConcept, SignalProximity)
|
|
path := ClassWeightMultiplier(QueryClassPath, SignalProximity)
|
|
symbol := ClassWeightMultiplier(QueryClassSymbol, SignalProximity)
|
|
if !(concept > symbol && symbol > path) {
|
|
t.Fatalf("expected concept(%.2f) > symbol(%.2f) > path(%.2f)", concept, symbol, path)
|
|
}
|
|
if concept <= 1.0 {
|
|
t.Fatalf("concept proximity multiplier should boost (>1.0), got %.2f", concept)
|
|
}
|
|
// Continuous lever: NL anchor reproduces the concept boost, the
|
|
// most BM25-leaning anchor reproduces the path damping.
|
|
if nl := continuousClassMultiplier(AlphaNL, SignalProximity); nl != concept {
|
|
t.Fatalf("continuous NL proximity = %.3f, want concept %.3f", nl, concept)
|
|
}
|
|
if p := continuousClassMultiplier(AlphaPath, SignalProximity); p != path {
|
|
t.Fatalf("continuous path proximity = %.3f, want path %.3f", p, path)
|
|
}
|
|
}
|