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
107 lines
3.1 KiB
Go
107 lines
3.1 KiB
Go
package mcp
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/zzet/gortex/internal/persistence"
|
|
)
|
|
|
|
func TestFrecency_ScoreDecaysOverTime(t *testing.T) {
|
|
ft := &frecencyTracker{mode: ModeAI, now: func() int64 { return 1000 }}
|
|
ft.Record("sym-a")
|
|
|
|
// Same instant: full weight (1.0).
|
|
assert.InDelta(t, 1.0, ft.Score("sym-a"), 0.0001)
|
|
|
|
// Advance clock by one full half-life (3 days @ AI mode).
|
|
ft.now = func() int64 { return 1000 + int64(3*86400) }
|
|
assert.InDelta(t, 0.5, ft.Score("sym-a"), 0.01, "one half-life should halve the score")
|
|
|
|
// Six days out: quarter weight.
|
|
ft.now = func() int64 { return 1000 + int64(6*86400) }
|
|
assert.InDelta(t, 0.25, ft.Score("sym-a"), 0.01)
|
|
}
|
|
|
|
func TestFrecency_MultipleAccessesAddUp(t *testing.T) {
|
|
ft := &frecencyTracker{mode: ModeAI, now: func() int64 { return 1000 }}
|
|
ft.Record("sym-a")
|
|
ft.Record("sym-a")
|
|
ft.Record("sym-a")
|
|
// All at t=0 → each contributes weight 1 → total = 3.
|
|
assert.InDelta(t, 3.0, ft.Score("sym-a"), 0.0001)
|
|
}
|
|
|
|
func TestFrecency_HumanModeSlowerDecay(t *testing.T) {
|
|
now := int64(1000)
|
|
ai := &frecencyTracker{mode: ModeAI, now: func() int64 { return now }}
|
|
human := &frecencyTracker{mode: ModeHuman, now: func() int64 { return now }}
|
|
ai.Record("x")
|
|
human.Record("x")
|
|
|
|
// Move forward 5 days.
|
|
now += int64(5 * 86400)
|
|
aiScore := ai.Score("x")
|
|
humanScore := human.Score("x")
|
|
assert.Less(t, aiScore, humanScore, "AI mode decays faster than human")
|
|
}
|
|
|
|
func TestFrecency_HistoryBounded(t *testing.T) {
|
|
ft := &frecencyTracker{mode: ModeAI, now: func() int64 { return 1 }}
|
|
cap := persistence.MaxFrecencyAccesses()
|
|
for i := 0; i < cap*3; i++ {
|
|
ft.Record("sym")
|
|
}
|
|
ft.mu.Lock()
|
|
defer ft.mu.Unlock()
|
|
require.Len(t, ft.store.Symbols, 1)
|
|
assert.LessOrEqual(t, len(ft.store.Symbols[0].Times), cap)
|
|
}
|
|
|
|
func TestFrecency_BoostCappedAtMax(t *testing.T) {
|
|
ft := &frecencyTracker{mode: ModeAI, now: func() int64 { return 1 }}
|
|
// Record way more accesses than the cap — BoostFor must saturate, not
|
|
// grow unboundedly.
|
|
for i := 0; i < 100; i++ {
|
|
ft.Record("hot")
|
|
}
|
|
b := ft.BoostFor("hot")
|
|
assert.LessOrEqual(t, b, maxFrecencyBoost+0.01)
|
|
assert.Greater(t, b, 1.0)
|
|
}
|
|
|
|
func TestFrecency_UnknownSymbolReturnsUnitBoost(t *testing.T) {
|
|
ft := &frecencyTracker{mode: ModeAI, now: func() int64 { return 1 }}
|
|
assert.Equal(t, 0.0, ft.Score("missing"))
|
|
assert.Equal(t, 1.0, ft.BoostFor("missing"))
|
|
}
|
|
|
|
func TestFrecency_Persistence(t *testing.T) {
|
|
dir := t.TempDir()
|
|
repoPath := filepath.Join(dir, "repo")
|
|
cacheDir := filepath.Join(dir, "cache")
|
|
|
|
ft := newFrecencyTracker(cacheDir, repoPath, ModeAI)
|
|
ft.now = func() int64 { return 1 }
|
|
ft.Record("alpha")
|
|
ft.Record("beta")
|
|
ft.Record("alpha")
|
|
require.True(t, ft.HasData())
|
|
|
|
// Reload: history survives.
|
|
ft2 := newFrecencyTracker(cacheDir, repoPath, ModeAI)
|
|
ft2.now = func() int64 { return 1 }
|
|
assert.InDelta(t, 2.0, ft2.Score("alpha"), 0.001)
|
|
assert.InDelta(t, 1.0, ft2.Score("beta"), 0.001)
|
|
}
|
|
|
|
func TestFrecency_NilSafe(t *testing.T) {
|
|
var ft *frecencyTracker
|
|
ft.Record("x")
|
|
assert.Equal(t, 0.0, ft.Score("x"))
|
|
assert.Equal(t, 1.0, ft.BoostFor("x"))
|
|
assert.False(t, ft.HasData())
|
|
}
|