|
4 | 4 |
|
5 | 5 | import { sleep } from '@sim/utils/helpers' |
6 | 6 | import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest' |
| 7 | +import { getMothershipChatResponseSchema } from '@/lib/api/contracts/mothership-chats' |
7 | 8 | import type { MothershipResource } from '@/lib/mothership/resources/types' |
8 | 9 |
|
9 | 10 | const { queryClient, suspendBrowserScope, suspendTerminalScope, clearChat } = vi.hoisted(() => ({ |
@@ -165,6 +166,77 @@ describe('tasks query boundary parsing', () => { |
165 | 166 | }) |
166 | 167 | }) |
167 | 168 |
|
| 169 | + describe.each(['primary', 'fallback'] as const)('%s history endpoint', (endpoint) => { |
| 170 | + it.each(getMothershipChatResponseSchema.shape.chat.shape.mode.options)( |
| 171 | + 'reopens a saved %s transcript without sending a new message', |
| 172 | + async (mode) => { |
| 173 | + const messages = [ |
| 174 | + { |
| 175 | + id: 'user-1', |
| 176 | + role: 'user', |
| 177 | + content: 'Investigate incidents', |
| 178 | + timestamp: '2026-09-23T00:19:00Z', |
| 179 | + requestMode: mode, |
| 180 | + }, |
| 181 | + { |
| 182 | + id: 'assistant-1', |
| 183 | + role: 'assistant', |
| 184 | + content: 'Saved investigation', |
| 185 | + timestamp: '2026-09-23T00:36:00Z', |
| 186 | + requestMode: mode, |
| 187 | + }, |
| 188 | + ] |
| 189 | + if (endpoint === 'fallback') { |
| 190 | + vi.mocked(fetch).mockResolvedValueOnce(new Response('Not found', { status: 404 })) |
| 191 | + } |
| 192 | + vi.mocked(fetch).mockResolvedValueOnce( |
| 193 | + jsonResponse({ |
| 194 | + success: true, |
| 195 | + chat: { |
| 196 | + id: 'chat-plan', |
| 197 | + mode, |
| 198 | + title: 'Incident triage', |
| 199 | + messages, |
| 200 | + activeStreamId: null, |
| 201 | + resources: [], |
| 202 | + }, |
| 203 | + }) |
| 204 | + ) |
| 205 | + |
| 206 | + const history = await fetchMothershipChatHistory('chat-plan') |
| 207 | + |
| 208 | + expect(history.mode).toBe(mode) |
| 209 | + expect(history.messages).toEqual( |
| 210 | + messages.map((message) => expect.objectContaining(message)) |
| 211 | + ) |
| 212 | + expect(fetch).toHaveBeenCalledTimes(endpoint === 'fallback' ? 2 : 1) |
| 213 | + expect( |
| 214 | + vi.mocked(fetch).mock.calls.every(([, init]) => !init?.method || init.method === 'GET') |
| 215 | + ).toBe(true) |
| 216 | + } |
| 217 | + ) |
| 218 | + |
| 219 | + it('rejects an unknown conversation mode', async () => { |
| 220 | + if (endpoint === 'fallback') { |
| 221 | + vi.mocked(fetch).mockResolvedValueOnce(new Response('Not found', { status: 404 })) |
| 222 | + } |
| 223 | + vi.mocked(fetch).mockResolvedValueOnce( |
| 224 | + jsonResponse({ |
| 225 | + success: true, |
| 226 | + chat: { |
| 227 | + id: 'chat-invalid', |
| 228 | + mode: 'unknown', |
| 229 | + title: null, |
| 230 | + messages: [], |
| 231 | + activeStreamId: null, |
| 232 | + resources: [], |
| 233 | + }, |
| 234 | + }) |
| 235 | + ) |
| 236 | + await expect(fetchMothershipChatHistory('chat-invalid')).rejects.toThrow() |
| 237 | + }) |
| 238 | + }) |
| 239 | + |
168 | 240 | it('retains saved table views, file paths and execution identities when reading chat resources', async () => { |
169 | 241 | const resources: MothershipResource[] = [ |
170 | 242 | { type: 'table', id: 'table-1', title: 'Invoices', viewId: 'overdue' }, |
|
0 commit comments