mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
补齐测试: - pipeline_test.go: 29 个测试 (蒸馏/刷盘/加载/三连提取/工具函数) - social_test.go: 11 个测试 (特质CRUD/社交关系/网络/安全 nil 守卫) - text/memory_test.go: 20 个测试 (追加/回放/并发/旋转/清理/持久化) - indexer_test.go: 15 个测试 (同步/上下文/召回过滤/关键词/工具定义) 图质量评估: - reorgGraph 新增 evaluateGraphQuality 步骤 - 自动识别蒸馏噪音 (用户-提及/AI-回应) 和低 confidence 关系 - 通过 enqueueConsolidationTask 交由 LLM 逐条判断保留/删除 文件日志: - 启动时创建 data/log/ 目录 - io.MultiWriter 同时输出到 stderr 和 homed_<时间>.log
333 lines
6.4 KiB
Go
333 lines
6.4 KiB
Go
package text
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestNew(t *testing.T) {
|
|
m := New(t.TempDir())
|
|
if m == nil {
|
|
t.Fatal("expected non-nil memory")
|
|
}
|
|
}
|
|
|
|
func TestAppendAndReplay(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
evt := Event{
|
|
Timestamp: time.Now().Unix(),
|
|
Source: "test",
|
|
Input: "hello",
|
|
Response: "world",
|
|
}
|
|
if err := m.Append(evt); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
var count int
|
|
err := m.Replay(func(e Event) error {
|
|
count++
|
|
if e.Input != "hello" || e.Response != "world" {
|
|
t.Errorf("unexpected event: %+v", e)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 1 {
|
|
t.Errorf("expected 1 event, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestAppendMultiple(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
for i := 0; i < 10; i++ {
|
|
m.Append(Event{
|
|
Timestamp: time.Now().Unix(),
|
|
Source: "test",
|
|
Input: "msg",
|
|
})
|
|
}
|
|
|
|
var count int
|
|
m.Replay(func(e Event) error { count++; return nil })
|
|
if count != 10 {
|
|
t.Errorf("expected 10 events, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestRecentEvents(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
for i := 0; i < 10; i++ {
|
|
m.Append(Event{
|
|
Timestamp: time.Now().Unix(),
|
|
Source: "test",
|
|
Input: "msg",
|
|
})
|
|
}
|
|
|
|
recent, err := m.RecentEvents(3)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(recent) != 3 {
|
|
t.Errorf("expected 3 recent events, got %d", len(recent))
|
|
}
|
|
}
|
|
|
|
func TestFileCount(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
if m.FileCount() != 1 {
|
|
t.Errorf("expected 1 file, got %d", m.FileCount())
|
|
}
|
|
}
|
|
|
|
func TestConcurrentAppend(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 20; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
m.Append(Event{
|
|
Timestamp: time.Now().Unix(),
|
|
Source: "test",
|
|
Input: "concurrent",
|
|
})
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
|
|
var count int
|
|
m.Replay(func(e Event) error { count++; return nil })
|
|
if count != 20 {
|
|
t.Errorf("expected 20 events, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestPurgeByFilter(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
m.Append(Event{Timestamp: 1, Source: "keep", Input: "a"})
|
|
m.Append(Event{Timestamp: 2, Source: "delete", Input: "b"})
|
|
m.Append(Event{Timestamp: 3, Source: "keep", Input: "c"})
|
|
|
|
removed, err := m.PurgeByFilter(func(e Event) bool {
|
|
return e.Source == "delete"
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if removed != 1 {
|
|
t.Errorf("expected 1 removed, got %d", removed)
|
|
}
|
|
|
|
var count int
|
|
m.Replay(func(e Event) error { count++; return nil })
|
|
if count != 2 {
|
|
t.Errorf("expected 2 events after purge, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestReplaceByFilter(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
m.Append(Event{Timestamp: 1, Source: "old", Input: "a"})
|
|
|
|
replaced, err := m.ReplaceByFilter(
|
|
func(e Event) bool { return e.Source == "old" },
|
|
func(e Event) Event { e.Source = "new"; return e },
|
|
)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if replaced != 1 {
|
|
t.Errorf("expected 1 replaced, got %d", replaced)
|
|
}
|
|
|
|
var evt Event
|
|
m.Replay(func(e Event) error { evt = e; return nil })
|
|
if evt.Source != "new" {
|
|
t.Errorf("expected source 'new', got %q", evt.Source)
|
|
}
|
|
}
|
|
|
|
func TestRotation(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir, WithMaxSizeBytes(100)) // small max size to trigger rotation
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
// Write enough data to trigger rotation
|
|
for i := 0; i < 50; i++ {
|
|
m.Append(Event{
|
|
Timestamp: time.Now().Unix(),
|
|
Source: "test",
|
|
Input: strings.Repeat("x", 50),
|
|
})
|
|
}
|
|
|
|
if m.FileCount() > 1 {
|
|
t.Logf("rotation triggered: %d files", m.FileCount())
|
|
}
|
|
}
|
|
|
|
func TestStats(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir, WithMaxSizeBytes(1000), WithRotationInterval(30*time.Minute))
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
m.Append(Event{Timestamp: time.Now().Unix(), Source: "test", Input: "hello"})
|
|
|
|
stats := m.Stats()
|
|
if stats["file_count"] != 1 {
|
|
t.Errorf("expected 1 file, got %v", stats["file_count"])
|
|
}
|
|
if stats["rotation_bytes"].(int64) != 1000 {
|
|
t.Errorf("expected 1000 bytes, got %v", stats["rotation_bytes"])
|
|
}
|
|
}
|
|
|
|
func TestStopWithoutStart(t *testing.T) {
|
|
m := New(t.TempDir())
|
|
// Should not panic
|
|
m.Stop()
|
|
}
|
|
|
|
func TestEmptyDir(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
var count int
|
|
m.Replay(func(e Event) error { count++; return nil })
|
|
if count != 0 {
|
|
t.Errorf("expected 0 events in empty dir, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestFilePersistence(t *testing.T) {
|
|
dir := t.TempDir()
|
|
|
|
// Write events
|
|
m1 := New(dir)
|
|
m1.Start()
|
|
m1.Append(Event{Timestamp: 100, Source: "test", Input: "persist"})
|
|
m1.Stop()
|
|
|
|
// Read back with new instance
|
|
m2 := New(dir)
|
|
m2.Start()
|
|
defer m2.Stop()
|
|
|
|
var count int
|
|
m2.Replay(func(e Event) error {
|
|
count++
|
|
return nil
|
|
})
|
|
if count != 1 {
|
|
t.Errorf("expected 1 persisted event, got %d", count)
|
|
}
|
|
}
|
|
|
|
func TestListFiles(t *testing.T) {
|
|
dir := t.TempDir()
|
|
// Create a non-text file that should be ignored
|
|
os.WriteFile(filepath.Join(dir, "other.txt"), []byte("ignored"), 0644)
|
|
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
m.Append(Event{Timestamp: 1, Source: "test", Input: "a"})
|
|
|
|
files, err := m.listFiles()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(files) != 1 {
|
|
t.Errorf("expected 1 file, got %d", len(files))
|
|
}
|
|
}
|
|
|
|
func TestPurgeAll(t *testing.T) {
|
|
dir := t.TempDir()
|
|
m := New(dir)
|
|
if err := m.Start(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer m.Stop()
|
|
|
|
m.Append(Event{Timestamp: 1, Source: "test", Input: "a"})
|
|
m.Append(Event{Timestamp: 2, Source: "test", Input: "b"})
|
|
|
|
removed, err := m.PurgeByFilter(func(e Event) bool { return true })
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if removed != 2 {
|
|
t.Errorf("expected 2 removed, got %d", removed)
|
|
}
|
|
|
|
var count int
|
|
m.Replay(func(e Event) error { count++; return nil })
|
|
if count != 0 {
|
|
t.Errorf("expected 0 events after full purge, got %d", count)
|
|
}
|
|
}
|