134 lines
3.8 KiB
TypeScript
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" });
|
|
});
|
|
});
|