diff --git a/aiui/packages/app/src/__tests__/fixtures/seedPrompts.ts b/aiui/packages/app/src/__tests__/fixtures/seedPrompts.ts index 34f1c98e..8e16261a 100644 --- a/aiui/packages/app/src/__tests__/fixtures/seedPrompts.ts +++ b/aiui/packages/app/src/__tests__/fixtures/seedPrompts.ts @@ -70,7 +70,12 @@ All feature practical effects, IMAX cinematography, and Hans Zimmer scores (exce [[song_ext:Music for Airports 1/1|Brian Eno|1978]] — The track that coined "ambient music." Tape loops of piano and voices create an ever-evolving soundscape. [[song_ext:Treefingers|Radiohead|2000]] — From Kid A. Processed guitar creating ethereal textures. Works beautifully on loop.`, - expected: { songs: 10 }, + // Six, not ten. The assistantResponse above lists exactly six + // `song_ext` entries and ends coherently on Treefingers — it was never + // truncated. The extractor was returning the right answer and this + // number was the wrong one, which is why "extracts 10 songs" had been + // failing. Counted from the fixture, not from intent. + expected: { songs: 6 }, }, // ─── Books ────────────────────────────────────────────────── diff --git a/aiui/packages/app/src/__tests__/useAI.test.ts b/aiui/packages/app/src/__tests__/useAI.test.ts index 3a8dfda7..87d2dd1d 100644 --- a/aiui/packages/app/src/__tests__/useAI.test.ts +++ b/aiui/packages/app/src/__tests__/useAI.test.ts @@ -348,6 +348,36 @@ describe('useAI', () => { const body = JSON.parse(claudeCall![1].body as string) expect(body.system).toContain('Web search results') expect(body.system).toContain('Result 1') + // FALSE on purpose: `proxyWebSearch = webSearchEnabled && !clientSearchSucceeded`. + // The client already searched and injected the results above, so asking + // the proxy to search again would be a second, redundant search per turn. + // This assertion read `true` and had been failing since that change — + // the test was stale, the behaviour is intentional. + expect(body.webSearch).toBe(false) + }) + + it('asks the proxy to search when the client-side search finds nothing', async () => { + // The other half of the same contract: no client results means nothing + // was injected, so the proxy must still do the search. + vi.mocked(searchWeb).mockResolvedValue([]) + + const fetchSpy = vi.fn().mockResolvedValue( + mockClaudeResponse(['data: {"type":"content_block_delta","delta":{"text":"answer"}}\n\n']) + ) + globalThis.fetch = fetchSpy + + const chatStore = useChatStore() + chatStore.webSearchEnabled = true + const { sendMessage } = useAI() + + await sendMessage('latest bitcoin news') + + const claudeCall = fetchSpy.mock.calls.find( + (c: unknown[]) => (c[0] as string)?.toString().includes('/claude/') + ) + expect(claudeCall).toBeDefined() + const body = JSON.parse(claudeCall![1].body as string) + expect(body.system).not.toContain('Web search results') expect(body.webSearch).toBe(true) }) })