Files
2026-07-13 13:20:22 +08:00

119 lines
3.4 KiB
TypeScript

/**
* @license
* Copyright 2025 AionUi (aionui.com)
* SPDX-License-Identifier: Apache-2.0
*/
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { BackendHttpError } from '@/common/adapter/httpBridge';
import { ipcBridge } from '@/common';
import type { TChatConversation } from '@/common/config/storage';
import { mutate } from 'swr';
import { getConversationOrNull, refreshConversationCache } from '@/renderer/pages/conversation/utils/conversationCache';
vi.mock('@/common', () => ({
ipcBridge: {
conversation: {
get: {
invoke: vi.fn(),
},
},
},
}));
vi.mock('swr', () => ({
mutate: vi.fn(),
}));
const mockConversation = {
id: 'conv-1',
name: 'Test conversation',
type: 'acp',
status: 'finished',
extra: {},
} as TChatConversation;
describe('conversationCache', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('getConversationOrNull', () => {
it('returns null when the backend reports a missing conversation', async () => {
const error = new BackendHttpError({
method: 'GET',
path: '/api/conversations/missing',
status: 404,
body: {
success: false,
error: 'Not found: Conversation missing not found',
code: 'NOT_FOUND',
},
});
vi.mocked(ipcBridge.conversation.get.invoke).mockRejectedValue(error);
await expect(getConversationOrNull('missing')).resolves.toBeNull();
});
it('returns the conversation when the backend lookup succeeds', async () => {
vi.mocked(ipcBridge.conversation.get.invoke).mockResolvedValue(mockConversation);
await expect(getConversationOrNull('conv-1')).resolves.toBe(mockConversation);
});
it('rethrows non-404 backend errors so database failures remain visible', async () => {
const error = new BackendHttpError({
method: 'GET',
path: '/api/conversations/conv-1',
status: 500,
body: {
success: false,
error: 'Internal error: Database error: no such table: conversations',
code: 'INTERNAL_ERROR',
},
});
vi.mocked(ipcBridge.conversation.get.invoke).mockRejectedValue(error);
await expect(getConversationOrNull('conv-1')).rejects.toBe(error);
});
});
describe('refreshConversationCache', () => {
it('skips cache mutation when the conversation is missing', async () => {
const error = new BackendHttpError({
method: 'GET',
path: '/api/conversations/missing',
status: 404,
body: {
success: false,
error: 'Not found: Conversation missing not found',
code: 'NOT_FOUND',
},
});
vi.mocked(ipcBridge.conversation.get.invoke).mockRejectedValue(error);
await expect(refreshConversationCache('missing')).resolves.toBeUndefined();
expect(mutate).not.toHaveBeenCalled();
});
it('rethrows non-404 backend errors instead of hiding them', async () => {
const error = new BackendHttpError({
method: 'GET',
path: '/api/conversations/conv-1',
status: 500,
body: {
success: false,
error: 'Internal error: Database error: no such table: conversations',
code: 'INTERNAL_ERROR',
},
});
vi.mocked(ipcBridge.conversation.get.invoke).mockRejectedValue(error);
await expect(refreshConversationCache('conv-1')).rejects.toBe(error);
expect(mutate).not.toHaveBeenCalled();
});
});
});