- 只翻注释/文档字符串:820 增 / 820 删 严格 1:1;剥掉整行注释与行尾注释尾巴后, 逐文件与 HEAD 比对「代码部分差异 = 0」;裁决/design 编号计数逐项与 HEAD 一致 - 保留可执行 SQL 样例、SQLite 报错原文、pgvector/HNSW/ANN/BLOB 等技术词原文 - 复核:go build 绿 + memory/projectindex/vector 三包测试绿;gofmt 仅剩 2 个既有未格式化文件(改动前即如此,未顺手修)
218 lines
6.4 KiB
Go
218 lines
6.4 KiB
Go
package memory
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
)
|
|
|
|
func TestStore_ListScope_User_全局共享(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
for _, p := range []string{"persona.a", "persona.b"} {
|
|
if _, err := s.AddScoped(ctx, p, MemoryItem{
|
|
PersonaID: p, Scope: ScopeUser, Kind: KindRelationship,
|
|
Content: "用户习惯-" + p,
|
|
}); err != nil {
|
|
t.Fatalf("AddScoped user for %s: %v", p, err)
|
|
}
|
|
}
|
|
|
|
// persona.a 能看到两个角色的用户作用域条目(全局共享)。
|
|
items, err := s.ListScope(ctx, ScopeUser, "persona.a")
|
|
if err != nil {
|
|
t.Fatalf("ListScope user: %v", err)
|
|
}
|
|
if len(items) != 2 {
|
|
t.Fatalf("ListScope(user) = %d items, want 2 (globally shared)", len(items))
|
|
}
|
|
}
|
|
|
|
func TestStore_ListScope_Persona_其他角色只读(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.AddScoped(ctx, "persona.a", MemoryItem{
|
|
Scope: ScopePersona, Kind: KindWorldEvent, Content: "a的日常",
|
|
}); err != nil {
|
|
t.Fatalf("AddScoped persona.a: %v", err)
|
|
}
|
|
|
|
// persona.b 可以读 persona.a 的角色作用域条目(只读规则)。
|
|
items, err := s.ListScope(ctx, ScopePersona, "persona.b")
|
|
if err != nil {
|
|
t.Fatalf("ListScope persona for b: %v", err)
|
|
}
|
|
if len(items) != 1 || items[0].PersonaID != "persona.a" {
|
|
t.Fatalf("ListScope(persona) for persona.b = %+v, want persona.a's item", items)
|
|
}
|
|
// AddScoped 用实施动作者自动填充了 PersonaID。
|
|
if items[0].PersonaID != "persona.a" {
|
|
t.Errorf("owner persona = %q, want persona.a", items[0].PersonaID)
|
|
}
|
|
}
|
|
|
|
func TestStore_AddScoped_Persona_跨角色写_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.AddScoped(ctx, "persona.a", MemoryItem{
|
|
Scope: ScopePersona, Kind: KindRelationship, Content: "a的日常",
|
|
}); err != nil {
|
|
t.Fatalf("AddScoped persona.a: %v", err)
|
|
}
|
|
|
|
// persona.b 不能写 persona.a 的角色作用域条目。
|
|
_, err := s.AddScoped(ctx, "persona.b", MemoryItem{
|
|
PersonaID: "persona.a", Scope: ScopePersona, Kind: KindRelationship,
|
|
Content: "b想改a的日常",
|
|
})
|
|
if !errors.Is(err, ErrReadOnly) {
|
|
t.Fatalf("AddScoped cross-persona err = %v, want wrap of ErrReadOnly", err)
|
|
}
|
|
}
|
|
|
|
func TestStore_AddScoped_UserProject_人人写(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
// 任何角色都可以写用户或项目作用域的条目。
|
|
for _, sc := range []Scope{ScopeUser, ScopeProject} {
|
|
item, err := s.AddScoped(ctx, "persona.b", MemoryItem{
|
|
Scope: sc, Kind: KindWorldEvent, Content: "共享-" + string(sc),
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("AddScoped %s: %v", sc, err)
|
|
}
|
|
// PersonaID 已用实施动作者自动填充。
|
|
if item.PersonaID != "persona.b" {
|
|
t.Errorf("AddScoped %s owner = %q, want persona.b", sc, item.PersonaID)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestStore_AddScoped_MissingActor_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.AddScoped(ctx, "", MemoryItem{
|
|
Content: "x", Scope: ScopeUser,
|
|
}); !errors.Is(err, ErrMissingPersona) {
|
|
t.Fatalf("AddScoped(empty actor) err = %v, want ErrMissingPersona", err)
|
|
}
|
|
}
|
|
|
|
func TestStore_AddScoped_InvalidScope_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.AddScoped(ctx, "persona.a", MemoryItem{
|
|
Content: "x", Scope: "bogus",
|
|
}); !errors.Is(err, ErrInvalidScope) {
|
|
t.Fatalf("AddScoped(bogus scope) err = %v, want ErrInvalidScope", err)
|
|
}
|
|
}
|
|
|
|
func TestStore_ListScope_前置校验_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.ListScope(ctx, ScopeUser, ""); !errors.Is(err, ErrMissingPersona) {
|
|
t.Fatalf("ListScope(empty persona) err = %v, want ErrMissingPersona", err)
|
|
}
|
|
if _, err := s.ListScope(ctx, "bogus", "persona.a"); !errors.Is(err, ErrInvalidScope) {
|
|
t.Fatalf("ListScope(bogus scope) err = %v, want ErrInvalidScope", err)
|
|
}
|
|
}
|
|
|
|
func TestStore_DeleteScoped_跨角色_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
stored, err := s.AddScoped(ctx, "persona.a", MemoryItem{
|
|
Scope: ScopePersona, Kind: KindRelationship, Content: "a的日常",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("AddScoped persona.a: %v", err)
|
|
}
|
|
|
|
// persona.a 删除自己的条目。
|
|
if err := s.DeleteScoped(ctx, "persona.a", stored.ID); err != nil {
|
|
t.Fatalf("DeleteScoped owner: %v", err)
|
|
}
|
|
|
|
// 重新添加,然后 persona.b 不能删除 persona.a 的条目。
|
|
stored2, err := s.AddScoped(ctx, "persona.a", MemoryItem{
|
|
Scope: ScopePersona, Kind: KindRelationship, Content: "a的日常2",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("AddScoped persona.a: %v", err)
|
|
}
|
|
if err := s.DeleteScoped(ctx, "persona.b", stored2.ID); !errors.Is(err, ErrReadOnly) {
|
|
t.Fatalf("DeleteScoped cross-persona err = %v, want ErrReadOnly", err)
|
|
}
|
|
// 他人的条目依然存在。
|
|
if _, err := s.Get(ctx, stored2.ID); err != nil {
|
|
t.Fatalf("foreign item removed: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStore_DeleteScoped_BlankActor_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if err := s.DeleteScoped(ctx, "", "some-id"); !errors.Is(err, ErrMissingPersona) {
|
|
t.Fatalf("DeleteScoped(empty actor) err = %v, want ErrMissingPersona", err)
|
|
}
|
|
}
|
|
|
|
func TestStore_ListProject_项目记忆(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.AddScoped(ctx, "persona.a", MemoryItem{
|
|
Scope: ScopeProject, Kind: KindWorldEvent, Content: "项目Alpha事实",
|
|
ProjectID: "project.alpha",
|
|
}); err != nil {
|
|
t.Fatalf("AddScoped project: %v", err)
|
|
}
|
|
if _, err := s.AddScoped(ctx, "persona.b", MemoryItem{
|
|
Scope: ScopeProject, Kind: KindWorldEvent, Content: "项目Beta事实",
|
|
ProjectID: "project.beta",
|
|
}); err != nil {
|
|
t.Fatalf("AddScoped project: %v", err)
|
|
}
|
|
|
|
items, err := s.ListProject(ctx, "project.alpha")
|
|
if err != nil {
|
|
t.Fatalf("ListProject: %v", err)
|
|
}
|
|
if len(items) != 1 || items[0].ProjectID != "project.alpha" {
|
|
t.Fatalf("ListProject(alpha) = %+v, want only alpha item", items)
|
|
}
|
|
if items[0].Content != "项目Alpha事实" {
|
|
t.Errorf("item content = %q, want the alpha item", items[0].Content)
|
|
}
|
|
}
|
|
|
|
func TestStore_ListProject_BlankID_异常(t *testing.T) {
|
|
t.Parallel()
|
|
ctx := context.Background()
|
|
s := newTestStore(t)
|
|
|
|
if _, err := s.ListProject(ctx, ""); !errors.Is(err, ErrMissingProjectID) {
|
|
t.Fatalf("ListProject(blank) err = %v, want ErrMissingProjectID", err)
|
|
}
|
|
}
|