// @vitest-environment node import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ handleChatSend: vi.fn(), handleChatHistory: vi.fn(), handleChatAbort: vi.fn(), handleSessionList: vi.fn(), handleSessionDelete: vi.fn(), handleProviderList: vi.fn(), handleProviderGetDefault: vi.fn(), handleSkillsStatus: vi.fn(), handleSkillsUpdate: vi.fn(), handleSkillsInstall: vi.fn(), })); vi.mock('../electron/gateway/handlers/chat', () => ({ handleChatSend: mocks.handleChatSend, handleChatHistory: mocks.handleChatHistory, handleChatAbort: mocks.handleChatAbort, handleSessionList: mocks.handleSessionList, handleSessionDelete: mocks.handleSessionDelete, })); vi.mock('../electron/gateway/handlers/provider', () => ({ handleProviderList: mocks.handleProviderList, handleProviderGetDefault: mocks.handleProviderGetDefault, })); vi.mock('../electron/gateway/handlers/skills', () => ({ handleSkillsStatus: mocks.handleSkillsStatus, handleSkillsUpdate: mocks.handleSkillsUpdate, handleSkillsInstall: mocks.handleSkillsInstall, })); describe('dispatchGatewayRpcMethod', () => { beforeEach(() => { vi.clearAllMocks(); }); it('routes chat history to the local chat handler', async () => { const messages = [{ role: 'user', content: 'hello' }]; mocks.handleChatHistory.mockReturnValue(messages); const { dispatchGatewayRpcMethod } = await import('../electron/gateway/rpc-dispatch'); const result = dispatchGatewayRpcMethod( 'chat.history', { sessionKey: 'agent:test:main', limit: 20 }, vi.fn(), ); expect(result).toEqual({ handled: true, result: messages }); expect(mocks.handleChatHistory).toHaveBeenCalledWith({ sessionKey: 'agent:test:main', limit: 20, }); }); it('routes chat send locally and forwards the broadcast callback', async () => { mocks.handleChatSend.mockReturnValue({ runId: 'run-1' }); const { dispatchGatewayRpcMethod } = await import('../electron/gateway/rpc-dispatch'); const broadcast = vi.fn(); const result = dispatchGatewayRpcMethod( 'chat.send', { sessionKey: 'agent:test:main', message: { role: 'user', content: 'hello' }, }, broadcast, ); expect(result).toEqual({ handled: true, result: { runId: 'run-1' } }); expect(mocks.handleChatSend).toHaveBeenCalledTimes(1); expect(mocks.handleChatSend.mock.calls[0]?.[1]).toBe(broadcast); }); it('prevents deleting the main session', async () => { const { dispatchGatewayRpcMethod } = await import('../electron/gateway/rpc-dispatch'); const result = dispatchGatewayRpcMethod( 'session.delete', { sessionKey: 'agent:test:main' }, vi.fn(), ); expect(result).toEqual({ handled: true, result: { success: false } }); expect(mocks.handleSessionDelete).not.toHaveBeenCalled(); }); it('routes non-main session deletion and session listing locally', async () => { mocks.handleSessionDelete.mockReturnValue({ success: true }); mocks.handleSessionList.mockReturnValue(['agent:test:main', 'agent:test:secondary']); const { dispatchGatewayRpcMethod } = await import('../electron/gateway/rpc-dispatch'); expect( dispatchGatewayRpcMethod('session.list', {}, vi.fn()), ).toEqual({ handled: true, result: ['agent:test:main', 'agent:test:secondary'], }); expect( dispatchGatewayRpcMethod( 'session.delete', { sessionKey: 'agent:test:secondary' }, vi.fn(), ), ).toEqual({ handled: true, result: { success: true }, }); expect(mocks.handleSessionDelete).toHaveBeenCalledWith({ sessionKey: 'agent:test:secondary', }); }); it('routes provider and skills methods locally and leaves unknown methods unhandled', async () => { mocks.handleProviderGetDefault.mockReturnValue({ accountId: 'provider-1' }); mocks.handleSkillsStatus.mockReturnValue({ skills: [] }); mocks.handleSkillsUpdate.mockReturnValue({ success: true }); mocks.handleSkillsInstall.mockResolvedValue({ success: true, slug: 'minimax-xlsx', baseDir: '/tmp/minimax-xlsx', source: 'github-url', enabled: true, }); const { dispatchGatewayRpcMethod } = await import('../electron/gateway/rpc-dispatch'); const broadcast = vi.fn(); expect( dispatchGatewayRpcMethod('provider.getDefault', {}, vi.fn()), ).toEqual({ handled: true, result: { accountId: 'provider-1' }, }); expect( dispatchGatewayRpcMethod('skills.status', {}, vi.fn()), ).toEqual({ handled: true, result: { skills: [] }, }); expect( dispatchGatewayRpcMethod('skills.update', { skillKey: 'demo' }, vi.fn()), ).toEqual({ handled: true, result: { success: true }, }); expect( dispatchGatewayRpcMethod( 'skills.install', { kind: 'github-url', url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md' }, broadcast, ), ).toEqual({ handled: true, result: expect.any(Promise), }); expect(mocks.handleSkillsInstall).toHaveBeenCalledWith( { kind: 'github-url', url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md' }, broadcast, ); expect( dispatchGatewayRpcMethod('gateway.ping', {}, vi.fn()), ).toEqual({ handled: false, }); }); });