Files
tolaria/src/hooks/useMcpStatus.test.ts
Test 83009d8fb9 feat: make MCP restore command always available in Cmd+K
The "Install MCP Server" command was only enabled when status was
"not_installed", preventing users from re-registering when MCP got
removed or broken. Now the command is always available:
- Shows "Install MCP Server" when not installed
- Shows "Restore MCP Server" when already installed
- Added restore/fix/repair keywords for discoverability
- Context-aware toast: "installed" vs "restored"
- Menu bar label updated to "Restore MCP Server"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:33:18 +01:00

179 lines
5.9 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { renderHook, act, waitFor } from '@testing-library/react'
import { useMcpStatus } from './useMcpStatus'
vi.mock('@tauri-apps/api/core', () => ({
invoke: vi.fn(),
}))
vi.mock('../mock-tauri', () => ({
isTauri: () => false,
mockInvoke: vi.fn(),
}))
const { mockInvoke } = await import('../mock-tauri') as { mockInvoke: ReturnType<typeof vi.fn> }
describe('useMcpStatus', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('starts in checking state and resolves to installed', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('installed')
if (cmd === 'register_mcp_tools') return Promise.resolve('updated')
return Promise.resolve(null)
})
const onToast = vi.fn()
const { result } = renderHook(() => useMcpStatus('/vault', onToast))
expect(result.current.mcpStatus).toBe('checking')
await waitFor(() => {
expect(result.current.mcpStatus).toBe('installed')
})
})
it('resolves to not_installed when check returns not_installed', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('not_installed')
if (cmd === 'register_mcp_tools') return Promise.reject(new Error('fail'))
return Promise.resolve(null)
})
const onToast = vi.fn()
const { result } = renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(result.current.mcpStatus).toBe('not_installed')
})
})
it('resolves to no_claude_cli when check returns no_claude_cli', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('no_claude_cli')
if (cmd === 'register_mcp_tools') return Promise.reject(new Error('no cli'))
return Promise.resolve(null)
})
const onToast = vi.fn()
const { result } = renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(result.current.mcpStatus).toBe('no_claude_cli')
})
})
it('install action calls register_mcp_tools and updates status', async () => {
// Auto-register fails (e.g. node not found), leaving status as not_installed
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('not_installed')
if (cmd === 'register_mcp_tools') return Promise.reject(new Error('no node'))
return Promise.resolve(null)
})
const onToast = vi.fn()
const { result } = renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(result.current.mcpStatus).toBe('not_installed')
})
// Now manual install succeeds
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'register_mcp_tools') return Promise.resolve('registered')
return Promise.resolve(null)
})
await act(async () => {
await result.current.installMcp()
})
expect(result.current.mcpStatus).toBe('installed')
expect(onToast).toHaveBeenCalledWith('MCP server installed successfully')
})
it('install action shows error toast on failure', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('not_installed')
if (cmd === 'register_mcp_tools') return Promise.reject(new Error('disk full'))
return Promise.resolve(null)
})
const onToast = vi.fn()
const { result } = renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(result.current.mcpStatus).toBe('not_installed')
})
await act(async () => {
await result.current.installMcp()
})
expect(result.current.mcpStatus).toBe('not_installed')
expect(onToast).toHaveBeenCalledWith(expect.stringContaining('MCP install failed'))
})
it('shows toast when registered for the first time', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('installed')
if (cmd === 'register_mcp_tools') return Promise.resolve('registered')
return Promise.resolve(null)
})
const onToast = vi.fn()
renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(onToast).toHaveBeenCalledWith('Laputa registered as MCP tool for Claude Code')
})
})
it('install action shows restored toast when status was installed', async () => {
// First call: status already installed
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('installed')
if (cmd === 'register_mcp_tools') return Promise.resolve('updated')
return Promise.resolve(null)
})
const onToast = vi.fn()
const { result } = renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(result.current.mcpStatus).toBe('installed')
})
// Clear toasts from auto-register
onToast.mockClear()
// Now manually trigger install (restore)
await act(async () => {
await result.current.installMcp()
})
expect(result.current.mcpStatus).toBe('installed')
expect(onToast).toHaveBeenCalledWith('MCP server restored successfully')
})
it('does not show toast when already registered', async () => {
mockInvoke.mockImplementation((cmd: string) => {
if (cmd === 'check_mcp_status') return Promise.resolve('installed')
if (cmd === 'register_mcp_tools') return Promise.resolve('updated')
return Promise.resolve(null)
})
const onToast = vi.fn()
renderHook(() => useMcpStatus('/vault', onToast))
await waitFor(() => {
expect(mockInvoke).toHaveBeenCalledWith('register_mcp_tools', { vaultPath: '/vault' })
})
// 'updated' should not trigger a toast
expect(onToast).not.toHaveBeenCalledWith('Laputa registered as MCP tool for Claude Code')
})
})