package history import ( "context" "strings" "testing" ) // sessionOf 从 ListSessions 里返回某个会话的 SessionMeta,会话缺失时使测试失败。 func sessionOf(t *testing.T, st *Store, id string) SessionMeta { t.Helper() sessions, err := st.ListSessions(context.Background()) if err != nil { t.Fatalf("list sessions: %v", err) } for _, m := range sessions { if m.ID == id { return m } } t.Fatalf("session %q not found in %+v", id, sessions) return SessionMeta{} } func TestListSessionsLastMessagePreview(t *testing.T) { t.Parallel() st := newStore(t) ctx := context.Background() // 空的主会话没有预览。 if got := sessionOf(t, st, MainSessionID); got.LastMessage != nil { t.Fatalf("empty main LastMessage = %+v, want nil", got.LastMessage) } sub, err := st.NewSubSession(ctx, "owner") if err != nil { t.Fatalf("new sub: %v", err) } if err := st.Append(ctx, Message{SessionID: MainSessionID, Role: RoleUser, Content: "first", CreatedAt: 1000}); err != nil { t.Fatalf("append main user: %v", err) } if err := st.Append(ctx, Message{SessionID: MainSessionID, Role: RoleAssistant, Content: "latest reply", CreatedAt: 2000}); err != nil { t.Fatalf("append main assistant: %v", err) } if err := st.Append(ctx, Message{SessionID: sub, Role: RoleUser, Content: "sub question", CreatedAt: 1500}); err != nil { t.Fatalf("append sub: %v", err) } // 主会话仍在最前,携带它最新的消息,而不是第一条。 sessions, err := st.ListSessions(ctx) if err != nil { t.Fatalf("list sessions: %v", err) } if len(sessions) != 2 || sessions[0].ID != MainSessionID { t.Fatalf("sessions = %+v, want main first", sessions) } main := sessions[0].LastMessage if main == nil || main.Role != RoleAssistant || main.Content != "latest reply" || main.CreatedAt != 2000 { t.Fatalf("main LastMessage = %+v, want assistant/latest reply/2000", main) } subPreview := sessions[1].LastMessage if subPreview == nil || subPreview.Role != RoleUser || subPreview.Content != "sub question" || subPreview.CreatedAt != 1500 { t.Fatalf("sub LastMessage = %+v, want user/sub question/1500", subPreview) } // 在主会话之后创建、但从未写入的子会话没有预览。 empty, err := st.NewSubSession(ctx, "owner") if err != nil { t.Fatalf("new empty sub: %v", err) } if got := sessionOf(t, st, empty); got.LastMessage != nil { t.Fatalf("empty sub LastMessage = %+v, want nil", got.LastMessage) } } func TestListSessionsLastMessageTruncatesAndCollapses(t *testing.T) { t.Parallel() st := newStore(t) ctx := context.Background() long := strings.Repeat("字", lastMessagePreviewRunes+10) + "\n\n尾部" if err := st.Append(ctx, Message{SessionID: MainSessionID, Role: RoleUser, Content: long, CreatedAt: 1000}); err != nil { t.Fatalf("append long: %v", err) } want := strings.Repeat("字", lastMessagePreviewRunes) + "…" if got := sessionOf(t, st, MainSessionID).LastMessage; got == nil || got.Content != want { t.Fatalf("truncated preview = %+v, want %q", got, want) } // 连续空白(含换行)折叠为单个空格,短消息原样返回。 sub, err := st.NewSubSession(ctx, "owner") if err != nil { t.Fatalf("new sub: %v", err) } if err := st.Append(ctx, Message{SessionID: sub, Role: RoleUser, Content: " 你好\n\t世界 ", CreatedAt: 1000}); err != nil { t.Fatalf("append spaced: %v", err) } if got := sessionOf(t, st, sub).LastMessage; got == nil || got.Content != "你好 世界" { t.Fatalf("collapsed preview = %+v, want %q", got, "你好 世界") } } func TestListSessionsLastMessageTieBreaksByInsertion(t *testing.T) { t.Parallel() st := newStore(t) ctx := context.Background() // 两条消息共享同一毫秒时间戳:后插入的是最新的。 for _, content := range []string{"older", "newer"} { if err := st.Append(ctx, Message{SessionID: MainSessionID, Role: RoleAssistant, Content: content, CreatedAt: 5000}); err != nil { t.Fatalf("append %s: %v", content, err) } } if got := sessionOf(t, st, MainSessionID).LastMessage; got == nil || got.Content != "newer" { t.Fatalf("tie-break preview = %+v, want newer", got) } } func TestPreviewContentBounds(t *testing.T) { t.Parallel() if got := previewContent(""); got != "" { t.Fatalf("empty preview = %q, want empty", got) } exact := strings.Repeat("a", lastMessagePreviewRunes) if got := previewContent(exact); got != exact { t.Fatalf("exact-length preview changed: %q", got) } over := strings.Repeat("a", lastMessagePreviewRunes+1) if got := previewContent(over); got != exact+"…" { t.Fatalf("over-length preview = %q, want ellipsis", got) } }