Files
2026-08-14 23:41:57 +08:00

151 lines
4.9 KiB
Go

package runtime
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/smallnest/pigo/internal/agentcore"
"github.com/smallnest/pigo/internal/memory"
)
// openMemoryStore opens a memory.Store over a temp DB + root and writes the
// given memory files (path segments relative to root -> body), returning the
// store. Reconcile is left to the provider's ReconcileFirst.
func openMemoryStore(t *testing.T, files map[string]string) *memory.Store {
t.Helper()
base := t.TempDir()
root := filepath.Join(base, "mimo")
if err := os.MkdirAll(root, 0o755); err != nil {
t.Fatalf("mkdir root: %v", err)
}
for rel, body := range files {
full := filepath.Join(root, rel)
if err := os.MkdirAll(filepath.Dir(full), 0o755); err != nil {
t.Fatalf("mkdir for %q: %v", full, err)
}
if err := os.WriteFile(full, []byte(body), 0o644); err != nil {
t.Fatalf("write %q: %v", full, err)
}
}
st, err := memory.Open(filepath.Join(base, "memory.db"), root, "")
if err != nil {
t.Fatalf("memory.Open: %v", err)
}
t.Cleanup(func() { st.Close() })
return st
}
// userMsgs builds a MessageList with a single user text message.
func userMsgs(text string) agentcore.MessageList {
return agentcore.MessageList{
agentcore.UserMessage{
RoleField: agentcore.RoleUser,
Content: agentcore.ContentList{agentcore.NewTextContent(text)},
},
}
}
func TestMemoryReminderInjectsMatchingSnippet(t *testing.T) {
st := openMemoryStore(t, map[string]string{
filepath.Join("projects", "proj1", "notes", "auth.md"): "permission deadlock encountered during checkpoint save then retry succeeded",
filepath.Join("global", "user", "u1.md"): "unrelated grocery shopping list",
})
p := &MemoryReminderProvider{Store: st, MaxChars: 400}
body, ok := p.Reminder(context.Background(), userMsgs("how do I handle the permission deadlock?"))
if !ok {
t.Fatalf("expected a memory reminder to fire, got ok=false")
}
if !strings.Contains(body, "Relevant memory:") {
t.Errorf("body missing heading: %q", body)
}
if !strings.Contains(body, "permission") {
t.Errorf("body missing the matching snippet text: %q", body)
}
if len(body) > 400 {
t.Errorf("body exceeds MaxChars budget: len=%d body=%q", len(body), body)
}
// Second identical call dedupes.
if _, ok := p.Reminder(context.Background(), userMsgs("how do I handle the permission deadlock?")); ok {
t.Errorf("identical follow-up call should dedupe to ok=false")
}
}
func TestMemoryReminderRespectsMaxChars(t *testing.T) {
long := strings.Repeat("permission deadlock retry ", 40)
st := openMemoryStore(t, map[string]string{
filepath.Join("projects", "proj1", "notes", "big.md"): long,
})
p := &MemoryReminderProvider{Store: st, MaxChars: 120}
body, ok := p.Reminder(context.Background(), userMsgs("permission deadlock"))
if !ok {
t.Fatalf("expected a memory reminder to fire")
}
if len(body) > 120 {
t.Errorf("body exceeds MaxChars=120: len=%d", len(body))
}
}
func TestMemoryReminderNoUserMessage(t *testing.T) {
st := openMemoryStore(t, map[string]string{
filepath.Join("global", "user", "u1.md"): "permission deadlock note",
})
p := &MemoryReminderProvider{Store: st}
// Empty message list.
if _, ok := p.Reminder(context.Background(), nil); ok {
t.Errorf("empty message list must not fire")
}
// User message with no text content.
blank := agentcore.MessageList{agentcore.UserMessage{RoleField: agentcore.RoleUser}}
if _, ok := p.Reminder(context.Background(), blank); ok {
t.Errorf("empty user text must not fire")
}
}
func TestMemoryReminderNoMatch(t *testing.T) {
st := openMemoryStore(t, map[string]string{
filepath.Join("global", "user", "u1.md"): "grocery shopping list milk eggs",
})
p := &MemoryReminderProvider{Store: st}
if _, ok := p.Reminder(context.Background(), userMsgs("kubernetes ingress controller crash")); ok {
t.Errorf("no matching memory should not fire")
}
}
func TestMemoryReminderNilStore(t *testing.T) {
p := &MemoryReminderProvider{}
if _, ok := p.Reminder(context.Background(), userMsgs("anything")); ok {
t.Errorf("nil store must not fire")
}
var np *MemoryReminderProvider
if _, ok := np.Reminder(context.Background(), userMsgs("anything")); ok {
t.Errorf("nil provider must not fire")
}
}
func TestMemoryReminderMemoryMdFirst(t *testing.T) {
st := openMemoryStore(t, map[string]string{
filepath.Join("projects", "proj1", "notes", "detail.md"): "permission deadlock detail note here",
filepath.Join("projects", "proj1", "MEMORY.md"): "permission deadlock index overview",
})
p := &MemoryReminderProvider{Store: st, MaxChars: 800}
body, ok := p.Reminder(context.Background(), userMsgs("permission deadlock"))
if !ok {
t.Fatalf("expected reminder to fire")
}
idxMem := strings.Index(body, "MEMORY.md")
idxDetail := strings.Index(body, "detail.md")
if idxMem == -1 || idxDetail == -1 {
t.Fatalf("expected both files in body: %q", body)
}
if idxMem > idxDetail {
t.Errorf("MEMORY.md should sort before other results, got body: %q", body)
}
}