jan/web-app/src/hooks/__tests__/useChat.instructions.test.ts
2025-08-17 00:24:00 -07:00

139 lines
4.3 KiB
TypeScript

import { renderHook, act } from '@testing-library/react'
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { useChat } from '../useChat'
// Use hoisted storage for our mock to avoid hoist errors
const hoisted = vi.hoisted(() => ({
builderMock: vi.fn(() => ({
addUserMessage: vi.fn(),
addAssistantMessage: vi.fn(),
getMessages: vi.fn(() => []),
})),
}))
vi.mock('@/lib/messages', () => ({
CompletionMessagesBuilder: hoisted.builderMock,
}))
// Mock dependencies similar to existing tests, but customize assistant
vi.mock('../../hooks/usePrompt', () => ({
usePrompt: vi.fn(() => ({ prompt: 'test prompt', setPrompt: vi.fn() })),
}))
vi.mock('../../hooks/useAppState', () => ({
useAppState: Object.assign(
vi.fn(() => ({
tools: [],
updateTokenSpeed: vi.fn(),
resetTokenSpeed: vi.fn(),
updateTools: vi.fn(),
updateStreamingContent: vi.fn(),
updateLoadingModel: vi.fn(),
setAbortController: vi.fn(),
})),
{ getState: vi.fn(() => ({ tokenSpeed: { tokensPerSecond: 10 } })) }
),
}))
vi.mock('../../hooks/useAssistant', () => ({
useAssistant: vi.fn(() => ({
assistants: [
{
id: 'test-assistant',
instructions: 'Today is {{current_date}}',
parameters: { stream: true },
},
],
currentAssistant: {
id: 'test-assistant',
instructions: 'Today is {{current_date}}',
parameters: { stream: true },
},
})),
}))
vi.mock('../../hooks/useModelProvider', () => ({
useModelProvider: vi.fn(() => ({
getProviderByName: vi.fn(() => ({ provider: 'openai', models: [] })),
selectedModel: { id: 'test-model', capabilities: ['tools'] },
selectedProvider: 'openai',
updateProvider: vi.fn(),
})),
}))
vi.mock('../../hooks/useThreads', () => ({
useThreads: vi.fn(() => ({
getCurrentThread: vi.fn(() => ({ id: 'test-thread', model: { id: 'test-model', provider: 'openai' } })),
createThread: vi.fn(() => Promise.resolve({ id: 'test-thread', model: { id: 'test-model', provider: 'openai' } })),
updateThreadTimestamp: vi.fn(),
})),
}))
vi.mock('../../hooks/useMessages', () => ({
useMessages: vi.fn(() => ({ getMessages: vi.fn(() => []), addMessage: vi.fn() })),
}))
vi.mock('../../hooks/useToolApproval', () => ({
useToolApproval: vi.fn(() => ({ approvedTools: [], showApprovalModal: vi.fn(), allowAllMCPPermissions: false })),
}))
vi.mock('../../hooks/useModelContextApproval', () => ({
useContextSizeApproval: vi.fn(() => ({ showApprovalModal: vi.fn() })),
}))
vi.mock('../../hooks/useModelLoad', () => ({
useModelLoad: vi.fn(() => ({ setModelLoadError: vi.fn() })),
}))
vi.mock('@tanstack/react-router', () => ({
useRouter: vi.fn(() => ({ navigate: vi.fn() })),
}))
vi.mock('@/lib/completion', () => ({
emptyThreadContent: { thread_id: 'test-thread', content: '' },
extractToolCall: vi.fn(),
newUserThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'user message' })),
newAssistantThreadContent: vi.fn(() => ({ thread_id: 'test-thread', content: 'assistant message' })),
sendCompletion: vi.fn(() => Promise.resolve({ choices: [{ message: { content: '' } }] })),
postMessageProcessing: vi.fn(),
isCompletionResponse: vi.fn(() => true),
}))
vi.mock('@/services/mcp', () => ({ getTools: vi.fn(() => Promise.resolve([])) }))
vi.mock('@/services/models', () => ({
startModel: vi.fn(() => Promise.resolve()),
stopModel: vi.fn(() => Promise.resolve()),
stopAllModels: vi.fn(() => Promise.resolve()),
}))
vi.mock('@/services/providers', () => ({ updateSettings: vi.fn(() => Promise.resolve()) }))
vi.mock('@tauri-apps/api/event', () => ({ listen: vi.fn(() => Promise.resolve(vi.fn())) }))
describe('useChat instruction rendering', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('renders assistant instructions by replacing {{current_date}} with today', async () => {
vi.useFakeTimers()
vi.setSystemTime(new Date('2025-08-16T00:00:00Z'))
const { result } = renderHook(() => useChat())
await act(async () => {
await result.current.sendMessage('Hello')
})
expect(hoisted.builderMock).toHaveBeenCalled()
const calls = (hoisted.builderMock as any).mock.calls as any[]
const call = calls[0]
expect(call[0]).toEqual([])
expect(call[1]).toMatch(/^Today is /)
expect(call[1]).not.toContain('{{current_date}}')
vi.useRealTimers()
})
})