package loop import ( "context" "testing" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/memory" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/model" ) // TestPersistMemories_StampsRealSource 证明裁决 04.4.3 的落库值: // 提取调用点(persistMemories)从运行上下文盖真实 session/message id, // extractor 产出的自由文本来源被无条件覆盖。 func TestPersistMemories_StampsRealSource(t *testing.T) { t.Parallel() store := newTestMemoryStore(t) l := New(Options{Memory: store, Extractor: memory.NewManualExtractor("persona-a", "owner"), PersonaID: "persona-a"}) // extractor 刻意夹带一条伪造来源,证明持久化层会盖掉它。 bogus := []memory.MemoryItem{{ PersonaID: "persona-a", Scope: memory.ScopeUser, Kind: memory.KindRelationship, Content: "用户习惯:爱喝冰美式", SourceConversation: "模型瞎编的来源", }} ctx := withLoopSession(WithTurnID(WithSessionEnv(context.Background(), OwnerEnv()), "turn-1"), "web:abc") l.persistMemories(ctx, bogus, 1) items, err := store.ListByPersona(context.Background(), "persona-a") if err != nil { t.Fatalf("ListByPersona: %v", err) } if len(items) != 1 { t.Fatalf("stored %d items, want 1", len(items)) } ref, ok := memory.ParseSourceConversation(items[0].SourceConversation) if !ok { t.Fatalf("SourceConversation %q is not structured, want real ids", items[0].SourceConversation) } if ref.SessionID != "web:abc" || ref.MessageID != "turn-1" { t.Fatalf("source ref = %+v, want session web:abc / message turn-1", ref) } } // TestPersistMemories_SourceSessionOnly 证明未挂 turn id 的调用方 // (渠道路径现状)溯源降级为仅会话粒度 —— 真实、可解析,不伪造。 func TestPersistMemories_SourceSessionOnly(t *testing.T) { t.Parallel() store := newTestMemoryStore(t) l := New(Options{Memory: store, Extractor: memory.NewManualExtractor("persona-a", "owner"), PersonaID: "persona-a"}) ctx := withLoopSession(WithSessionEnv(context.Background(), OwnerEnv()), "qq-obv11:group:42") l.extractMemory(ctx, []model.Message{model.TextMessage(model.RoleUser, "记住 我爱喝冰美式")}, 1) items, err := store.ListByPersona(context.Background(), "persona-a") if err != nil { t.Fatalf("ListByPersona: %v", err) } if len(items) != 1 { t.Fatalf("stored %d items, want 1", len(items)) } ref, ok := memory.ParseSourceConversation(items[0].SourceConversation) if !ok { t.Fatalf("SourceConversation %q is not structured", items[0].SourceConversation) } if ref.SessionID != "qq-obv11:group:42" || ref.MessageID != "" { t.Fatalf("source ref = %+v, want session-only provenance", ref) } }