ci / vet-and-test (pull_request) Successful in 41s
Why: memory recall lived as a product-side interface assertion with no library port, AgentSpec.MemoryScope had no consumer, and the model had no write-back path. The port (Agent-Native#34) now exists; the product adapts it over memory_entries. Evidence: go test -count=1 ./... green; TestStoreMemoryRecallScopeAndLimit (scope filters and limit), TestStoreMemoryAppendRoundtrip (project context inheritance), TestRunCtxStoreMemory (runCtx outlet), TestDirectRunInjectsMemoryBlock (the [记忆] block reaches the request with the pre-migration text shape and deterministic order). Impact: assembly-side injection removed (the loop injects per request — same text, no duplication); memory_write available on the kernel path; recall defaults to all scopes (pre-migration semantics), with AgentSpec.MemoryScope narrowing where hosts pass it. Refs: Agent-Native#34, Agent-Squad#38, N3 plan WP5
95 lines
3.0 KiB
Go
95 lines
3.0 KiB
Go
package runmgr
|
||
|
||
import (
|
||
"context"
|
||
"path/filepath"
|
||
"testing"
|
||
"time"
|
||
|
||
"agent-native/runtime/memory"
|
||
"agent-system-backend/internal/store"
|
||
core "agent-system-core"
|
||
)
|
||
|
||
// ---- 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)
|
||
}
|
||
}
|