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) } }