Files
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

178 lines
4.7 KiB
Go

package mcp
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zzet/gortex/internal/persistence"
)
func TestFeedbackManager_RecordAndQuery(t *testing.T) {
dir := t.TempDir()
fm := newFeedbackManager(dir, "/tmp/test-repo")
require.NoError(t, fm.Record(persistence.FeedbackEntry{
Task: "add MCP tool",
Useful: []string{"server.go::Server", "tools.go::register"},
NotNeeded: []string{"types.go::NodeKind"},
Missing: []string{"enhancements.go::registerEnhancementTools"},
Source: "smart_context",
}))
require.NoError(t, fm.Record(persistence.FeedbackEntry{
Task: "fix search bug",
Useful: []string{"server.go::Server", "search.go::Search"},
NotNeeded: []string{"types.go::NodeKind"},
Source: "smart_context",
}))
stats := fm.AggregatedStats("all", 10)
assert.Equal(t, 2, stats["total_entries"])
accuracy, ok := stats["accuracy"].(float64)
require.True(t, ok)
assert.Greater(t, accuracy, 0.5)
}
func TestFeedbackManager_ScoreComputation(t *testing.T) {
fm := &feedbackManager{}
// Record entries directly.
for range 8 {
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
Useful: []string{"good.go::Func"},
Source: "smart_context",
})
}
for range 2 {
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
NotNeeded: []string{"good.go::Func"},
Source: "smart_context",
})
}
// 8 useful, 2 not_needed → score = (8-2)/10 = 0.6
score := fm.GetSymbolScore("good.go::Func")
assert.InDelta(t, 0.6, score, 0.001)
}
func TestFeedbackManager_NegativeScore(t *testing.T) {
fm := &feedbackManager{}
for range 5 {
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
NotNeeded: []string{"bad.go::Func"},
Source: "smart_context",
})
}
// 0 useful, 5 not_needed → score = -1.0
score := fm.GetSymbolScore("bad.go::Func")
assert.InDelta(t, -1.0, score, 0.001)
}
func TestFeedbackManager_EmptyStore(t *testing.T) {
fm := &feedbackManager{}
score := fm.GetSymbolScore("unknown.go::Func")
assert.Equal(t, 0.0, score)
assert.False(t, fm.HasData())
}
func TestFeedbackManager_Persistence(t *testing.T) {
dir := t.TempDir()
// Write with first manager.
fm1 := newFeedbackManager(dir, "/tmp/repo")
require.NoError(t, fm1.Record(persistence.FeedbackEntry{
Task: "task1",
Useful: []string{"a.go::Foo"},
Source: "smart_context",
}))
// Read with second manager (simulates server restart).
fm2 := newFeedbackManager(dir, "/tmp/repo")
assert.True(t, fm2.HasData())
score := fm2.GetSymbolScore("a.go::Foo")
assert.InDelta(t, 1.0, score, 0.001)
}
func TestFeedbackManager_MissedSymbols(t *testing.T) {
fm := &feedbackManager{}
for range 3 {
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
Missing: []string{"missed.go::Func", "rare.go::Func"},
Source: "smart_context",
})
}
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
Missing: []string{"missed.go::Func"},
Source: "smart_context",
})
// missed.go::Func has 4 misses, rare.go::Func has 3.
symbols := fm.MissedSymbols(3)
require.Len(t, symbols, 2)
assert.Equal(t, "missed.go::Func", symbols[0])
assert.Equal(t, "rare.go::Func", symbols[1])
// With higher threshold, only missed.go qualifies.
symbols = fm.MissedSymbols(4)
require.Len(t, symbols, 1)
assert.Equal(t, "missed.go::Func", symbols[0])
}
func TestFeedbackManager_FilterByToolSource(t *testing.T) {
fm := &feedbackManager{}
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
Useful: []string{"a.go::Foo"},
Source: "smart_context",
})
_ = fm.Record(persistence.FeedbackEntry{
Task: "task",
Useful: []string{"b.go::Bar"},
Source: "prefetch_context",
})
// Filter to smart_context only.
stats := fm.AggregatedStats("smart_context", 10)
assert.Equal(t, 1, stats["total_entries"])
// All sources.
stats = fm.AggregatedStats("all", 10)
assert.Equal(t, 2, stats["total_entries"])
}
func TestFeedbackManager_EntriesBounded(t *testing.T) {
fm := &feedbackManager{} // dir="" — no disk trim, exercises the in-memory cap
total := feedbackMaxEntries + 128
for i := 0; i < total; i++ {
require.NoError(t, fm.Record(persistence.FeedbackEntry{
Task: "task",
Useful: []string{fmt.Sprintf("sym-%d", i)},
Source: "smart_context",
}))
}
require.Len(t, fm.store.Entries, feedbackMaxEntries, "in-memory log capped")
// Newest retained, oldest aged out.
last := fm.store.Entries[len(fm.store.Entries)-1]
assert.Equal(t, []string{fmt.Sprintf("sym-%d", total-1)}, last.Useful, "newest entry retained")
first := fm.store.Entries[0]
assert.Equal(t, []string{fmt.Sprintf("sym-%d", total-feedbackMaxEntries)}, first.Useful, "oldest surviving entry")
}