refactor/channel & ipc (#349)
Co-authored-by: paisley <8197966+su8su@users.noreply.github.com> Co-authored-by: zuolingxuan <zuolingxuan@bytedance.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
8b45960662
commit
e28eba01e1
123
tests/unit/chat-session-actions.test.ts
Normal file
123
tests/unit/chat-session-actions.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
const invokeIpcMock = vi.fn();
|
||||
|
||||
vi.mock('@/lib/api-client', () => ({
|
||||
invokeIpc: (...args: unknown[]) => invokeIpcMock(...args),
|
||||
}));
|
||||
|
||||
type ChatLikeState = {
|
||||
currentSessionKey: string;
|
||||
sessions: Array<{ key: string; displayName?: string }>;
|
||||
messages: Array<{ role: string; timestamp?: number; content?: unknown }>;
|
||||
sessionLabels: Record<string, string>;
|
||||
sessionLastActivity: Record<string, number>;
|
||||
streamingText: string;
|
||||
streamingMessage: unknown | null;
|
||||
streamingTools: unknown[];
|
||||
activeRunId: string | null;
|
||||
error: string | null;
|
||||
pendingFinal: boolean;
|
||||
lastUserMessageAt: number | null;
|
||||
pendingToolImages: unknown[];
|
||||
loadHistory: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
|
||||
function makeHarness(initial?: Partial<ChatLikeState>) {
|
||||
let state: ChatLikeState = {
|
||||
currentSessionKey: 'agent:main:main',
|
||||
sessions: [{ key: 'agent:main:main' }],
|
||||
messages: [],
|
||||
sessionLabels: {},
|
||||
sessionLastActivity: {},
|
||||
streamingText: '',
|
||||
streamingMessage: null,
|
||||
streamingTools: [],
|
||||
activeRunId: null,
|
||||
error: null,
|
||||
pendingFinal: false,
|
||||
lastUserMessageAt: null,
|
||||
pendingToolImages: [],
|
||||
loadHistory: vi.fn(),
|
||||
...initial,
|
||||
};
|
||||
const set = (partial: Partial<ChatLikeState> | ((s: ChatLikeState) => Partial<ChatLikeState>)) => {
|
||||
const patch = typeof partial === 'function' ? partial(state) : partial;
|
||||
state = { ...state, ...patch };
|
||||
};
|
||||
const get = () => state;
|
||||
return { set, get, read: () => state };
|
||||
}
|
||||
|
||||
describe('chat session actions', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
invokeIpcMock.mockResolvedValue({ success: true });
|
||||
});
|
||||
|
||||
it('switchSession removes empty non-main leaving session and loads history', async () => {
|
||||
const { createSessionActions } = await import('@/stores/chat/session-actions');
|
||||
const h = makeHarness({
|
||||
currentSessionKey: 'agent:foo:session-a',
|
||||
sessions: [{ key: 'agent:foo:session-a' }, { key: 'agent:foo:main' }],
|
||||
messages: [],
|
||||
sessionLabels: { 'agent:foo:session-a': 'A' },
|
||||
sessionLastActivity: { 'agent:foo:session-a': 1 },
|
||||
});
|
||||
const actions = createSessionActions(h.set as never, h.get as never);
|
||||
|
||||
actions.switchSession('agent:foo:main');
|
||||
const next = h.read();
|
||||
expect(next.currentSessionKey).toBe('agent:foo:main');
|
||||
expect(next.sessions.find((s) => s.key === 'agent:foo:session-a')).toBeUndefined();
|
||||
expect(next.sessionLabels['agent:foo:session-a']).toBeUndefined();
|
||||
expect(next.sessionLastActivity['agent:foo:session-a']).toBeUndefined();
|
||||
expect(h.read().loadHistory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('deleteSession updates current session and keeps sidebar consistent', async () => {
|
||||
const { createSessionActions } = await import('@/stores/chat/session-actions');
|
||||
const h = makeHarness({
|
||||
currentSessionKey: 'agent:foo:session-a',
|
||||
sessions: [{ key: 'agent:foo:session-a' }, { key: 'agent:foo:main' }],
|
||||
sessionLabels: { 'agent:foo:session-a': 'A' },
|
||||
sessionLastActivity: { 'agent:foo:session-a': 1 },
|
||||
messages: [{ role: 'user' }],
|
||||
});
|
||||
const actions = createSessionActions(h.set as never, h.get as never);
|
||||
|
||||
await actions.deleteSession('agent:foo:session-a');
|
||||
const next = h.read();
|
||||
expect(invokeIpcMock).toHaveBeenCalledWith('session:delete', 'agent:foo:session-a');
|
||||
expect(next.currentSessionKey).toBe('agent:foo:main');
|
||||
expect(next.sessions.map((s) => s.key)).toEqual(['agent:foo:main']);
|
||||
expect(next.sessionLabels['agent:foo:session-a']).toBeUndefined();
|
||||
expect(next.sessionLastActivity['agent:foo:session-a']).toBeUndefined();
|
||||
expect(h.read().loadHistory).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('newSession creates a canonical session key and clears transient state', async () => {
|
||||
const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(1711111111111);
|
||||
const { createSessionActions } = await import('@/stores/chat/session-actions');
|
||||
const h = makeHarness({
|
||||
currentSessionKey: 'agent:foo:main',
|
||||
sessions: [{ key: 'agent:foo:main' }],
|
||||
messages: [{ role: 'assistant' }],
|
||||
streamingText: 'streaming',
|
||||
activeRunId: 'r1',
|
||||
pendingFinal: true,
|
||||
});
|
||||
const actions = createSessionActions(h.set as never, h.get as never);
|
||||
|
||||
actions.newSession();
|
||||
const next = h.read();
|
||||
expect(next.currentSessionKey).toBe('agent:foo:session-1711111111111');
|
||||
expect(next.sessions.some((s) => s.key === 'agent:foo:session-1711111111111')).toBe(true);
|
||||
expect(next.messages).toEqual([]);
|
||||
expect(next.streamingText).toBe('');
|
||||
expect(next.activeRunId).toBeNull();
|
||||
expect(next.pendingFinal).toBe(false);
|
||||
nowSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user