diff --git a/src/components/chat/hooks/useAgentChatSession.test.tsx b/src/components/chat/hooks/useAgentChatSession.test.tsx new file mode 100644 index 0000000..14f391f --- /dev/null +++ b/src/components/chat/hooks/useAgentChatSession.test.tsx @@ -0,0 +1,101 @@ +"use client"; + +import { act, renderHook, waitFor } from "@testing-library/react"; + +import { useAgentChatSession } from "./useAgentChatSession"; + +jest.mock("@/lib/chatStream", () => ({ + abortAgentChat: jest.fn(async () => undefined), + forkAgentChat: jest.fn(async () => "forked-session"), + streamAgentChat: jest.fn(async () => undefined), +})); + +const loadActiveChatState = jest.fn(); +const listChatSessions = jest.fn(); + +jest.mock("../chatStorage", () => ({ + deleteChatSession: jest.fn(async () => undefined), + listChatSessions: (...args: unknown[]) => listChatSessions(...args), + loadActiveChatState: (...args: unknown[]) => loadActiveChatState(...args), + loadChatSessionById: jest.fn(async () => ({ + storageSessionId: "session-loaded", + title: "已存在会话", + isTitleManuallyEdited: false, + messages: [], + sessionId: undefined, + branchGroups: [], + })), + saveActiveChatState: jest.fn(async (state) => state.storageSessionId), + updateChatSessionTitle: jest.fn(async () => undefined), +})); + +describe("useAgentChatSession", () => { + beforeEach(() => { + loadActiveChatState.mockReset(); + listChatSessions.mockReset(); + + loadActiveChatState.mockResolvedValue({ + storageSessionId: undefined, + title: undefined, + isTitleManuallyEdited: false, + messages: [], + sessionId: undefined, + branchGroups: [], + }); + }); + + it("does not add a new empty session to history until there is actual chat content", async () => { + listChatSessions.mockResolvedValue([]); + + const { result } = renderHook(() => + useAgentChatSession({ + onToolCall: jest.fn(), + }), + ); + + await waitFor(() => expect(result.current.isHydrating).toBe(false)); + + act(() => { + void result.current.createSession(); + }); + + await waitFor(() => expect(result.current.sessionTitle).toBe("新对话")); + expect(result.current.chatSessions).toEqual([]); + expect(result.current.activeStorageSessionId).toBeUndefined(); + expect(result.current.messages).toEqual([]); + expect(result.current.isStreaming).toBe(false); + expect(listChatSessions).toHaveBeenCalledTimes(1); + }); + + it("keeps existing history entries when creating a blank new session", async () => { + listChatSessions.mockResolvedValue([ + { + id: "session-1", + title: "已有会话", + createdAt: 1, + updatedAt: 1, + }, + ]); + + const { result } = renderHook(() => + useAgentChatSession({ + onToolCall: jest.fn(), + }), + ); + + await waitFor(() => expect(result.current.isHydrating).toBe(false)); + + act(() => { + void result.current.createSession(); + }); + + expect(result.current.chatSessions).toEqual([ + { + id: "session-1", + title: "已有会话", + createdAt: 1, + updatedAt: 1, + }, + ]); + }); +}); diff --git a/src/components/chat/hooks/useAgentChatSession.ts b/src/components/chat/hooks/useAgentChatSession.ts index 2ac31db..e716cb1 100644 --- a/src/components/chat/hooks/useAgentChatSession.ts +++ b/src/components/chat/hooks/useAgentChatSession.ts @@ -9,17 +9,14 @@ import type { BranchGroup, BranchTransition, ChatProgress, - ChatSessionSummary, LoadedChatState, Message, } from "../GlobalChatbox.types"; import { cloneBranchGroups, cloneMessages, - createId, } from "../GlobalChatbox.utils"; import { - createEmptyChatSession, deleteChatSession, listChatSessions, loadActiveChatState, @@ -550,21 +547,23 @@ export const useAgentChatSession = ({ const controller = abortRef.current; controller?.abort(); setBranchTransition(null); - - const newState = await createEmptyChatSession(); - const sessions = await listChatSessions(); - hydrationNonceRef.current += 1; titleUpdateNonceRef.current += 1; - storageSessionIdRef.current = newState.storageSessionId; - sessionIdRef.current = newState.sessionId; - lastPersistedStateKeyRef.current = createPersistedStateKey(newState); - setMessages(newState.messages); - setSessionTitle(newState.title); - setIsSessionTitleManuallyEdited(newState.isTitleManuallyEdited ?? false); - setSessionId(newState.sessionId); - setBranchGroups(newState.branchGroups); - setChatSessions(sessions); + storageSessionIdRef.current = undefined; + sessionIdRef.current = undefined; + lastPersistedStateKeyRef.current = createPersistedStateKey({ + storageSessionId: undefined, + title: "新对话", + isTitleManuallyEdited: false, + messages: [], + sessionId: undefined, + branchGroups: [], + }); + setMessages([]); + setSessionTitle("新对话"); + setIsSessionTitleManuallyEdited(false); + setSessionId(undefined); + setBranchGroups([]); setIsStreaming(false); }, [isHydrating, isStreaming]);