slash-command-web-handlers.test.ts
1 import { beforeEach, describe, expect, it, vi } from 'vitest' 2 3 import { 4 buildWebHelpSlashCommandResult, 5 runWebFetchSlashCommand, 6 runWebSearchSlashCommand, 7 } from '@/server/chat/slash-command-web-handlers' 8 import { 9 executeWebFetchTool, 10 executeWebSearchTool, 11 } from '@/server/tools/web-tool-execution' 12 13 vi.mock('@/server/tools/web-tool-execution', () => ({ 14 executeWebSearchTool: vi.fn(), 15 executeWebFetchTool: vi.fn(), 16 })) 17 18 describe('slash-command-web-handlers', () => { 19 beforeEach(() => { 20 vi.clearAllMocks() 21 }) 22 23 it('renders web search results with image fields and cache marker', async () => { 24 vi.mocked(executeWebSearchTool).mockResolvedValue({ 25 ok: true, 26 response: { 27 provider: 'duckduckgo', 28 cached: true, 29 searchTimeMs: 10, 30 results: [ 31 { 32 title: 'Result A', 33 url: 'https://example.com/a', 34 snippet: 'A snippet', 35 imageUrl: 'https://img.example.com/a.jpg', 36 thumbnailUrl: 'https://img.example.com/a-thumb.jpg', 37 }, 38 ], 39 }, 40 results: [ 41 { 42 title: 'Result A', 43 url: 'https://example.com/a', 44 snippet: 'A snippet', 45 imageUrl: 'https://img.example.com/a.jpg', 46 thumbnailUrl: 'https://img.example.com/a-thumb.jpg', 47 }, 48 ], 49 }) 50 51 const result = await runWebSearchSlashCommand({ 52 query: 'kittens', 53 searchType: 'image', 54 attachmentNote: '\n\nNote: attachments ignored.', 55 }) 56 57 expect(result.provider).toBe('web-search') 58 expect(result.mocked).toBe(false) 59 expect(result.text).toContain('**Web Search Results** (image) (duckduckgo)') 60 expect(result.text).toContain('') 61 expect(result.text).toContain('*Results from cache*') 62 expect(result.text).toContain('Note: attachments ignored.') 63 }) 64 65 it('renders web search errors', async () => { 66 vi.mocked(executeWebSearchTool).mockResolvedValue({ 67 ok: false, 68 error: 'search unavailable', 69 }) 70 71 const result = await runWebSearchSlashCommand({ 72 query: 'kittens', 73 attachmentNote: '', 74 }) 75 76 expect(result).toEqual({ 77 text: 'Web search failed: search unavailable', 78 provider: 'web-search', 79 mocked: false, 80 }) 81 }) 82 83 it('renders web fetch preview and truncation markers', async () => { 84 vi.mocked(executeWebFetchTool).mockResolvedValue({ 85 ok: true, 86 response: { 87 cached: true, 88 fetchTimeMs: 20, 89 result: { 90 title: 'Example Page', 91 url: 'https://example.com', 92 content: `${'x'.repeat(7501)}`, 93 contentType: 'markdown', 94 truncated: true, 95 bytesRead: 1234, 96 }, 97 }, 98 }) 99 100 const result = await runWebFetchSlashCommand({ 101 url: 'https://example.com', 102 attachmentNote: '', 103 }) 104 105 expect(result.provider).toBe('web-fetch') 106 expect(result.text).toContain('**Example Page**') 107 expect(result.text).toContain('URL: https://example.com') 108 expect(result.text).toContain('*... content truncated*') 109 expect(result.text).toContain('*Content was truncated to 1234 bytes*') 110 expect(result.text).toContain('*Results from cache*') 111 }) 112 113 it('renders web fetch errors', async () => { 114 vi.mocked(executeWebFetchTool).mockResolvedValue({ 115 ok: false, 116 error: 'fetch unavailable', 117 }) 118 119 const result = await runWebFetchSlashCommand({ 120 url: 'https://example.com', 121 attachmentNote: '', 122 }) 123 124 expect(result).toEqual({ 125 text: 'Web fetch failed: fetch unavailable', 126 provider: 'web-fetch', 127 mocked: false, 128 }) 129 }) 130 131 it('renders web help text', () => { 132 const result = buildWebHelpSlashCommandResult({ 133 attachmentNote: '\n\nNote: attachments ignored.', 134 }) 135 136 expect(result.provider).toBe('web-help') 137 expect(result.text).toContain('/websearch --image <query>') 138 expect(result.text).toContain('/webfetch <url>') 139 expect(result.text).toContain('Note: attachments ignored.') 140 }) 141 })