Why: Allow consumers to download an immutable module revision without nested source checkouts or local path replacements. Evidence: SELF package tests and the prior canonical-path rejection are recorded in the Dev dependency-workspace task card; remote consumption is replayed against the committed candidate. Impact: Change the module identity and its Go imports while preserving API behavior. Existing historical submodule revisions remain buildable. Refs: Versioned dependencies U1/U3; feature/dependency-workspace
95 lines
3.1 KiB
Go
95 lines
3.1 KiB
Go
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)
|
||
}
|
||
}
|