feat(gateway): admin 列表端点接真实数据——persona/session/memory 补 List() 并注入 gateway.Deps
根因:gateway.Server 定义 SessionStore/PersonaStore/MemoryStore 接口但生产从未满足:
- 底层 manager 无 List() 方法;
- wireFrontend 的 gateway.Deps{} 未传 Session/Personas/Memories。
→ /api/v1/sessions|personas 与 /api/v1/admin/{sessions,personas,memories} 恒返回 []。
改动:
- persona/registry:Registry.List() []Character(并发安全、值拷贝)。
- channel/session:SessionManager.List(ctx)(查 channel_sessions 表+按 actor_role 重建)。
- runtime/memory:Store.List(ctx)(全表,复用 scanItems)。
- gateway 三 Store 接口改为返回域类型(Character/MemoryItem),映射统一放 collect* 侧。
- cmd/cyrene/wire.go:Deps 补传 Session/Personas/Memories。
- .gitignore:cyrene→/cyrene(锚定根,不再误伤 cmd/cyrene 目录)。
实机验证:/api/v1/admin/personas 返回 {id:cyrene,name:昔涟,isDefault:true}。
This commit is contained in:
1 parent
5dae2b9741
commit
04bfa2c7a7
11 files changed
+312
-18
No files matched your search
@@ -207,6 +207,94 @@ func TestGetOrCreateConcurrent(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m, _ := newTestManager(t)
|
||||
ctx := context.Background()
|
||||
|
||||
if _, err := m.GetOrCreate(ctx, directMsg("o-100"), testOwner()); err != nil {
|
||||
t.Fatalf("GetOrCreate(owner): %v", err)
|
||||
}
|
||||
if _, err := m.GetOrCreate(ctx, directMsg("stranger-1"), testOwner()); err != nil {
|
||||
t.Fatalf("GetOrCreate(stranger): %v", err)
|
||||
}
|
||||
if _, err := m.GetOrCreate(ctx, groupMsg("g-member-1"), testOwner()); err != nil {
|
||||
t.Fatalf("GetOrCreate(group): %v", err)
|
||||
}
|
||||
|
||||
got, err := m.List(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("List: %v", err)
|
||||
}
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("List = %d sessions, want 3", len(got))
|
||||
}
|
||||
var owner, stranger, group bool
|
||||
for _, s := range got {
|
||||
switch s.ChannelID {
|
||||
case "direct-o-100":
|
||||
if !s.Owner || !s.MemWritable || s.IsGroup {
|
||||
t.Errorf("owner session flags = %+v, want owner/writable/not-group", s)
|
||||
}
|
||||
owner = true
|
||||
case "direct-stranger-1":
|
||||
if s.Owner || s.MemWritable || s.IsGroup {
|
||||
t.Errorf("stranger session flags = %+v, want non-owner/read-only/not-group", s)
|
||||
}
|
||||
stranger = true
|
||||
case groupChannelPrefix + "g1":
|
||||
if !s.IsGroup || s.MemWritable {
|
||||
t.Errorf("group session flags = %+v, want group/read-only", s)
|
||||
}
|
||||
group = true
|
||||
}
|
||||
}
|
||||
if !owner || !stranger || !group {
|
||||
t.Fatalf("missing sessions: owner=%v stranger=%v group=%v", owner, stranger, group)
|
||||
}
|
||||
}
|
||||
|
||||
func TestListRecoveredFromTable(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctx := context.Background()
|
||||
handle, err := db.Open(ctx, ":memory:", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("db.Open = %v", err)
|
||||
}
|
||||
t.Cleanup(func() { _ = handle.Close() })
|
||||
|
||||
m1, err := New(ctx, handle, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("New#1 = %v", err)
|
||||
}
|
||||
if _, err := m1.GetOrCreate(ctx, directMsg("o-100"), testOwner()); err != nil {
|
||||
t.Fatalf("m1.GetOrCreate = %v", err)
|
||||
}
|
||||
|
||||
// A brand-new manager over the same DB has an empty cache: List must
|
||||
// reconstruct the session from the backing table (cross-restart path).
|
||||
m2, err := New(ctx, handle, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("New#2 = %v", err)
|
||||
}
|
||||
got, err := m2.List(ctx)
|
||||
if err != nil {
|
||||
t.Fatalf("List = %v", err)
|
||||
}
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("List = %d sessions, want 1", len(got))
|
||||
}
|
||||
s := got[0]
|
||||
if s.ChannelID != "direct-o-100" || s.SessionID == "" {
|
||||
t.Fatalf("recovered session = %+v, want direct-o-100", s)
|
||||
}
|
||||
if !s.Owner || !s.MemWritable {
|
||||
t.Errorf("recovered owner flags = %+v, want owner/writable", s)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetOrCreateInvalidChannel(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in new issue
Block a user