Files
TJWaterFrontend_Refine/src/components/chat/chatStorage.test.ts
T

134 lines
3.8 KiB
TypeScript

import {
createEmptyChatState,
deleteChatSession,
listChatSessions,
loadChatSessionById,
updateChatSessionTitle,
} from "./chatStorage";
const apiFetch = jest.fn();
jest.mock("@/lib/apiFetch", () => ({
apiFetch: (...args: unknown[]) => apiFetch(...args),
}));
describe("chatStorage backend session operations", () => {
beforeEach(() => {
apiFetch.mockReset();
});
it("creates an empty initial conversation state without backend calls", () => {
const loaded = createEmptyChatState();
expect(loaded).toMatchObject({
title: undefined,
messages: [],
sessionId: undefined,
});
expect(apiFetch).not.toHaveBeenCalled();
});
it("lists backend sessions sorted by created time", async () => {
apiFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({
sessions: [
{
id: "session-old",
title: "旧会话",
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-02T00:00:00.000Z",
},
{
id: "session-new",
title: "新会话",
created_at: "2026-01-03T00:00:00.000Z",
updated_at: "2026-01-03T00:00:00.000Z",
is_streaming: true,
run_status: "running",
},
],
}),
});
await expect(listChatSessions()).resolves.toEqual([
expect.objectContaining({
id: "session-new",
title: "新会话",
isStreaming: true,
runStatus: "running",
}),
expect.objectContaining({
id: "session-old",
title: "旧会话",
}),
]);
expect(apiFetch.mock.calls[0][1]).toMatchObject({ method: "GET" });
});
it("loads a backend session state", async () => {
apiFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({
id: "session-1",
title: "管网分析",
is_title_manually_edited: true,
messages: [{ id: "message-1", role: "user", content: "查压力" }],
is_streaming: false,
}),
});
await expect(loadChatSessionById("session-1")).resolves.toMatchObject({
title: "管网分析",
isTitleManuallyEdited: true,
sessionId: "session-1",
messages: [{ id: "message-1", role: "user", content: "查压力" }],
});
expect(String(apiFetch.mock.calls[0][0])).toContain("/sessions/session-1");
expect(apiFetch.mock.calls[0][1]).toMatchObject({ method: "GET" });
});
it("updates a backend session title through the title endpoint", async () => {
apiFetch.mockResolvedValueOnce({
ok: true,
text: async () => "",
});
await updateChatSessionTitle("session-1", " 新标题 ", {
isTitleManuallyEdited: true,
});
expect(String(apiFetch.mock.calls[0][0])).toContain("/sessions/session-1");
expect(apiFetch.mock.calls[0][1]).toMatchObject({ method: "PATCH" });
expect(JSON.parse(String(apiFetch.mock.calls[0][1]?.body))).toEqual({
title: "新标题",
is_title_manually_edited: true,
});
});
it("deletes a backend session and returns the next active session id", async () => {
apiFetch
.mockResolvedValueOnce({
ok: true,
text: async () => "",
})
.mockResolvedValueOnce({
ok: true,
json: async () => ({
sessions: [
{
id: "session-next",
title: "下一会话",
created_at: "2026-01-01T00:00:00.000Z",
updated_at: "2026-01-01T00:00:00.000Z",
},
],
}),
});
await expect(deleteChatSession("session-1")).resolves.toBe("session-next");
expect(apiFetch.mock.calls[0][1]).toMatchObject({ method: "DELETE" });
expect(apiFetch.mock.calls[1][1]).toMatchObject({ method: "GET" });
});
});