package runmgr import ( "context" "path/filepath" "testing" "time" "git-ssh.u1680832.nyat.app/weichen/Agent-Native.git/runtime/memory" "git-ssh.u1680832.nyat.app/weichen/Agent-System-Backend.git/internal/store" core "git-ssh.u1680832.nyat.app/weichen/Agent-System-Core.git" ) // ---- N3 WP5:storeMemory 适配器 ---- func openMemStore(t *testing.T) *store.Store { t.Helper() st, err := store.OpenSQLite(filepath.Join(t.TempDir(), "t.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { st.Close() }) return st } func TestStoreMemoryRecallScopeAndLimit(t *testing.T) { st := openMemStore(t) put := func(scope, project, typ, content string) { t.Helper() if _, err := st.PutMemory("u1", core.MemoryEntry{Scope: scope, ProjectID: core.ID(project), Type: typ, Content: content, CreatedAt: time.Now()}); err != nil { t.Fatal(err) } } put("user", "", "fact", "用户事实") put("project", "p1", "decision", "项目决定") put("project", "p2", "decision", "别的项目") m := NewStoreMemory(st, "u1", "p1") // 空 scope = 全量(迁移前 Memories() 语义)。 all, err := m.Recall(context.Background(), memory.RecallQuery{}) if err != nil || len(all) != 3 { t.Fatalf("全量召回:%d %v", len(all), err) } // user scope。 users, _ := m.Recall(context.Background(), memory.RecallQuery{Scope: "user"}) if len(users) != 1 || users[0].Content != "用户事实" { t.Fatalf("user scope:%+v", users) } // project scope 用适配器的项目上下文(p1,不含 p2)。 projs, _ := m.Recall(context.Background(), memory.RecallQuery{Scope: "project"}) if len(projs) != 1 || projs[0].Content != "项目决定" { t.Fatalf("project scope:%+v", projs) } // Limit。 limited, _ := m.Recall(context.Background(), memory.RecallQuery{Limit: 2}) if len(limited) != 2 { t.Fatalf("Limit:%d", len(limited)) } } func TestStoreMemoryAppendRoundtrip(t *testing.T) { st := openMemStore(t) m := NewStoreMemory(st, "u1", "p1") future := time.Now().Add(48 * time.Hour) if err := m.Append(context.Background(), memory.Entry{ Scope: "project", Type: "procedure", Content: "先跑 lint", Source: "agent", ExpiresAt: &future, }); err != nil { t.Fatal(err) } list, err := st.ListMemory("u1", "project", "p1") if err != nil || len(list) != 1 { t.Fatalf("回读:%d %v", len(list), err) } got := list[0] if got.Type != "procedure" || got.Content != "先跑 lint" || got.Source != "agent" || got.ProjectID != "p1" || got.ExpiresAt == nil { t.Fatalf("写入形态:%+v", got) } } // runCtx 出口:同一 store 上的 runCtx 暴露同一端口语义。 func TestRunCtxStoreMemory(t *testing.T) { c, st := newSpawnEnv(t, &gateRunner{}) if _, err := st.PutMemory(c.run.Owner, core.MemoryEntry{Scope: "user", Type: "fact", Content: "跨会话事实", CreatedAt: time.Now()}); err != nil { t.Fatal(err) } port := c.StoreMemory() if port == nil { t.Fatal("StoreMemory 必须非空") } entries, err := port.Recall(context.Background(), memory.RecallQuery{}) if err != nil || len(entries) != 1 || entries[0].Content != "跨会话事实" { t.Fatalf("runCtx 召回:%+v %v", entries, err) } }