168 lines
5.1 KiB
TypeScript
168 lines
5.1 KiB
TypeScript
import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|
|
|
const invokeIpcMock = vi.fn();
|
|
|
|
vi.mock('@/lib/api-client', () => ({
|
|
invokeIpc: (...args: unknown[]) => invokeIpcMock(...args),
|
|
}));
|
|
|
|
vi.mock('@/stores/chat/helpers', () => ({
|
|
getCanonicalPrefixFromSessions: () => 'agent:main',
|
|
getMessageText: (content: unknown) => typeof content === 'string' ? content : '',
|
|
toMs: (v: unknown) => typeof v === 'number' ? v : 0,
|
|
}));
|
|
|
|
describe('session label fetch concurrency', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
it('skips sessions with existing frontend or backend labels', async () => {
|
|
invokeIpcMock.mockImplementation(async (_channel: string, method: string) => {
|
|
if (method === 'sessions.list') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
sessions: [
|
|
{ key: 'agent:main:session-a', label: 'Backend label', updatedAt: 1000 },
|
|
{ key: 'agent:main:session-b', displayName: 'Session B', updatedAt: 1001 },
|
|
{ key: 'agent:main:main', displayName: 'Main', updatedAt: 1002 },
|
|
],
|
|
},
|
|
};
|
|
}
|
|
if (method === 'chat.history') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
messages: [{ role: 'user', content: 'hello', timestamp: Date.now() }],
|
|
},
|
|
};
|
|
}
|
|
return { success: false };
|
|
});
|
|
|
|
vi.resetModules();
|
|
const { createSessionActions } = await import('@/stores/chat/session-actions');
|
|
const state = {
|
|
currentSessionKey: 'agent:main:main',
|
|
messages: [],
|
|
sessions: [],
|
|
sessionLabels: { 'agent:main:session-b': 'Already labeled' },
|
|
sessionLastActivity: {},
|
|
};
|
|
const set = vi.fn();
|
|
const get = vi.fn().mockReturnValue({
|
|
...state,
|
|
loadHistory: vi.fn(),
|
|
});
|
|
|
|
const actions = createSessionActions(set as never, get as never);
|
|
await actions.loadSessions();
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
const chatHistoryCalls = invokeIpcMock.mock.calls.filter(([, method]) => method === 'chat.history');
|
|
expect(chatHistoryCalls).toHaveLength(0);
|
|
});
|
|
|
|
it('does not re-request unchanged sessions after an empty hydration result', async () => {
|
|
invokeIpcMock.mockImplementation(async (_channel: string, method: string) => {
|
|
if (method === 'sessions.list') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
sessions: [
|
|
{ key: 'agent:main:session-a', displayName: 'Session A', updatedAt: 1000 },
|
|
{ key: 'agent:main:main', displayName: 'Main', updatedAt: 1001 },
|
|
],
|
|
},
|
|
};
|
|
}
|
|
if (method === 'chat.history') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
messages: [],
|
|
},
|
|
};
|
|
}
|
|
return { success: false };
|
|
});
|
|
|
|
vi.resetModules();
|
|
const { createSessionActions } = await import('@/stores/chat/session-actions');
|
|
const state = {
|
|
currentSessionKey: 'agent:main:main',
|
|
messages: [],
|
|
sessions: [],
|
|
sessionLabels: {},
|
|
sessionLastActivity: {},
|
|
};
|
|
const set = vi.fn();
|
|
const get = vi.fn().mockReturnValue({
|
|
...state,
|
|
loadHistory: vi.fn(),
|
|
});
|
|
|
|
const actions = createSessionActions(set as never, get as never);
|
|
await actions.loadSessions();
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
await actions.loadSessions();
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
const chatHistoryCalls = invokeIpcMock.mock.calls.filter(([, method]) => method === 'chat.history');
|
|
expect(chatHistoryCalls).toHaveLength(1);
|
|
});
|
|
|
|
it('re-requests a session when updatedAt changes after an empty result', async () => {
|
|
let updatedAt = 1000;
|
|
invokeIpcMock.mockImplementation(async (_channel: string, method: string) => {
|
|
if (method === 'sessions.list') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
sessions: [
|
|
{ key: 'agent:main:session-a', displayName: 'Session A', updatedAt },
|
|
{ key: 'agent:main:main', displayName: 'Main', updatedAt: 1001 },
|
|
],
|
|
},
|
|
};
|
|
}
|
|
if (method === 'chat.history') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
messages: [],
|
|
},
|
|
};
|
|
}
|
|
return { success: false };
|
|
});
|
|
|
|
vi.resetModules();
|
|
const { createSessionActions } = await import('@/stores/chat/session-actions');
|
|
const state = {
|
|
currentSessionKey: 'agent:main:main',
|
|
messages: [],
|
|
sessions: [],
|
|
sessionLabels: {},
|
|
sessionLastActivity: {},
|
|
};
|
|
const set = vi.fn();
|
|
const get = vi.fn().mockReturnValue({
|
|
...state,
|
|
loadHistory: vi.fn(),
|
|
});
|
|
|
|
const actions = createSessionActions(set as never, get as never);
|
|
await actions.loadSessions();
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
updatedAt = 2000;
|
|
await actions.loadSessions();
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
const chatHistoryCalls = invokeIpcMock.mock.calls.filter(([, method]) => method === 'chat.history');
|
|
expect(chatHistoryCalls).toHaveLength(2);
|
|
});
|
|
});
|