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 sees both personas' user-scope items (globally shared). 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 may READ persona.a's persona-scope item (read-only rule). 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 auto-filled PersonaID from the acting actor. 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 cannot WRITE persona.a's persona-scope item. _, 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) // Any persona may write a user- or project-scope item. 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 was auto-filled with the acting actor. 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 deletes its own item. if err := s.DeleteScoped(ctx, "persona.a", stored.ID); err != nil { t.Fatalf("DeleteScoped owner: %v", err) } // Re-add, then persona.b cannot delete persona.a's item. 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) } // The foreign item survives. 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) } }