From 538d7bcedd3469190308805c3f5efd897a29a64d Mon Sep 17 00:00:00 2001 From: wujunchen Date: Mon, 27 Apr 2026 12:44:42 +0800 Subject: [PATCH] test: add structured output fallback and non-retryable error tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cover the two key paths in requestJsonBodyWithStructuredFallback: 1. 400 with response_format mention → retry without structured output 2. 500 server error → no retry, error propagated Change-Id: If8d0fc484e49238699444131cbadcf1b18bf992f --- tests/modules.test.js | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/tests/modules.test.js b/tests/modules.test.js index dbdbdb5..9f9f027 100644 --- a/tests/modules.test.js +++ b/tests/modules.test.js @@ -804,10 +804,81 @@ async function testRunCliEdgeCases() { } // Wrap the tail in an async runner so module-level tests can include async cases. +async function testStructuredOutputFallback() { + const previousRequestUrlMock = requestUrlMock; + let callCount = 0; + requestUrlMock = async (params) => { + callCount++; + if (callCount === 1) { + // First call: structured output rejected with 400 + return { + status: 400, + json: { error: 'response_format is not supported' }, + text: JSON.stringify({ error: 'response_format is not supported' }), + }; + } + // Second call: fallback succeeds + return openAiCardsResponse([ + { title: 'Fallback', anchor: 'fallback anchor', gist: 'G', bullets: ['B'] }, + ]); + }; + + try { + const sections = await t.summarizeDocument( + 'intro\nfallback anchor\nend', + { + ...baseSettings, + streaming: false, + promptLanguage: 'en', + minCards: 1, + maxCards: 3, + maxDocChars: 10000, + }, + { phase: 'queued', onCancel: () => {}, throwIfCancelled: () => {}, setPhase: () => {} }, + ); + assert.strictEqual(callCount, 2, 'structured output fallback made two requests'); + assert.strictEqual(sections[0].title, 'Fallback', 'fallback response parsed correctly'); + } finally { + requestUrlMock = previousRequestUrlMock; + } +} + +async function testStructuredOutputNonRetryableError() { + const previousRequestUrlMock = requestUrlMock; + requestUrlMock = async () => ({ + status: 500, + json: { error: 'Internal server error' }, + text: JSON.stringify({ error: 'Internal server error' }), + }); + + try { + await assert.rejects( + () => t.summarizeDocument( + 'test content', + { + ...baseSettings, + streaming: false, + promptLanguage: 'en', + minCards: 1, + maxCards: 3, + maxDocChars: 10000, + }, + { phase: 'queued', onCancel: () => {}, throwIfCancelled: () => {}, setPhase: () => {} }, + ), + /500/, + 'non-retryable error (500) is not caught by structured fallback', + ); + } finally { + requestUrlMock = previousRequestUrlMock; + } +} + (async () => { await testRunCliEdgeCases(); await testCacheManagerMove(); await testSummarizeDocumentAnchorSorting(); + await testStructuredOutputFallback(); + await testStructuredOutputNonRetryableError(); console.log('modules tests passed'); })().catch((e) => { console.error(e);