90 lines
2.7 KiB
TypeScript
90 lines
2.7 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('limits concurrent chat.history RPCs during label fetches', async () => {
|
|
// Track max concurrent RPCs
|
|
let currentConcurrency = 0;
|
|
let maxConcurrency = 0;
|
|
const resolvers: Array<() => void> = [];
|
|
|
|
invokeIpcMock.mockImplementation(async (channel: string, method: string) => {
|
|
if (method === 'sessions.list') {
|
|
return {
|
|
success: true,
|
|
result: {
|
|
sessions: Array.from({ length: 12 }, (_, i) => ({
|
|
key: `agent:main:session-${i}`,
|
|
label: `Session ${i}`,
|
|
})),
|
|
},
|
|
};
|
|
}
|
|
if (method === 'chat.history') {
|
|
currentConcurrency++;
|
|
maxConcurrency = Math.max(maxConcurrency, currentConcurrency);
|
|
await new Promise<void>((resolve) => resolvers.push(resolve));
|
|
currentConcurrency--;
|
|
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: {},
|
|
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();
|
|
|
|
// Wait for the label-fetch loop to start its batches
|
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
|
|
// Resolve first batch (up to 5 concurrent)
|
|
while (resolvers.length > 0 && resolvers.length <= 5) {
|
|
resolvers.shift()?.();
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
}
|
|
|
|
// Resolve remaining
|
|
while (resolvers.length > 0) {
|
|
resolvers.shift()?.();
|
|
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
}
|
|
|
|
// maxConcurrency should be capped at 5 (LABEL_FETCH_CONCURRENCY)
|
|
expect(maxConcurrency).toBeLessThanOrEqual(5);
|
|
});
|
|
});
|