// @vitest-environment node import { beforeEach, describe, expect, it, vi } from 'vitest'; const mocks = vi.hoisted(() => ({ appendMessage: vi.fn(), setActiveRun: vi.fn(), clearActiveRun: vi.fn(), handleSkillsInstall: vi.fn(), parseGitHubSkillUrl: vi.fn((url: string) => ({ owner: 'MiniMax-AI', repo: 'skills', ref: 'main', skillPath: 'skills/minimax-xlsx', defaultSlug: 'minimax-xlsx', archiveUrl: 'https://api.github.com/repos/MiniMax-AI/skills/zipball/main', repositoryUrl: 'https://github.com/MiniMax-AI/skills.git', originalUrl: url, })), appendTranscriptLine: vi.fn(), logger: { error: vi.fn(), }, })); vi.mock('../electron/gateway/session-store', () => ({ sessionStore: { appendMessage: mocks.appendMessage, setActiveRun: mocks.setActiveRun, clearActiveRun: mocks.clearActiveRun, }, })); vi.mock('../electron/gateway/handlers/skills', () => ({ handleSkillsInstall: mocks.handleSkillsInstall, })); vi.mock('@electron/service/skill-install-service', () => ({ parseGitHubSkillUrl: mocks.parseGitHubSkillUrl, })); vi.mock('@electron/utils/token-usage-writer', () => ({ appendTranscriptLine: mocks.appendTranscriptLine, })); vi.mock('@electron/service/logger', () => ({ default: mocks.logger, })); function flushAsyncTasks(): Promise { return new Promise((resolve) => setTimeout(resolve, 0)); } describe('gateway skill install shortcut', () => { beforeEach(() => { vi.clearAllMocks(); }); it('extracts a github-url install intent from an explicit install request', async () => { const { extractSkillInstallIntent } = await import('../electron/gateway/skill-install-shortcut'); expect( extractSkillInstallIntent( 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md,帮我安装这个skill', ), ).toEqual({ request: { kind: 'github-url', url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md', }, description: 'minimax-xlsx', }); }); it('extracts a marketplace install intent when the user explicitly names a skill slug', async () => { const { extractSkillInstallIntent } = await import('../electron/gateway/skill-install-shortcut'); expect( extractSkillInstallIntent('帮我安装 minimax-xlsx 这个 skill'), ).toEqual({ request: { kind: 'marketplace', slug: 'minimax-xlsx', }, description: 'minimax-xlsx', }); }); it('runs a skill install shortcut and emits tool status before the final assistant message', async () => { mocks.handleSkillsInstall.mockResolvedValue({ success: true, slug: 'minimax-xlsx', baseDir: '/tmp/minimax-xlsx', source: 'github-url', enabled: true, }); const { maybeHandleSkillInstallMessage } = await import('../electron/gateway/skill-install-shortcut'); const broadcast = vi.fn(); const handled = maybeHandleSkillInstallMessage( 'agent:test:main', 'run-1', { role: 'user', content: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md,帮我安装这个skill', }, broadcast, ); expect(handled).toBe(true); expect(mocks.setActiveRun).toHaveBeenCalledWith( 'agent:test:main', 'run-1', expect.any(AbortController), ); await flushAsyncTasks(); expect(mocks.handleSkillsInstall).toHaveBeenCalledWith( { kind: 'github-url', url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md', }, broadcast, ); expect(broadcast).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: 'tool:status', runId: 'run-1', toolName: 'skills.install', status: 'running', })); expect(broadcast).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: 'tool:status', runId: 'run-1', toolName: 'skills.install', status: 'completed', })); expect(mocks.appendMessage).toHaveBeenCalledWith( 'agent:test:main', expect.objectContaining({ role: 'assistant', content: '已安装并启用 skill minimax-xlsx(github-url)。位置:/tmp/minimax-xlsx', _toolStatuses: [ expect.objectContaining({ name: 'skills.install', status: 'completed', input: { kind: 'github-url', url: 'https://github.com/MiniMax-AI/skills/blob/main/skills/minimax-xlsx/SKILL.md', }, result: expect.objectContaining({ slug: 'minimax-xlsx', baseDir: '/tmp/minimax-xlsx', source: 'github-url', }), }), ], }), ); expect(broadcast).toHaveBeenNthCalledWith(3, expect.objectContaining({ type: 'chat:final', runId: 'run-1', message: expect.objectContaining({ _toolStatuses: [ expect.objectContaining({ name: 'skills.install', status: 'completed', }), ], }), })); }); });