| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { describe, test } from 'node:test'; |
| import assert from 'node:assert/strict'; |
| import { readFileSync } from 'node:fs'; |
| import { fileURLToPath } from 'node:url'; |
| import { dirname, join } from 'node:path'; |
|
|
| const __dirname = dirname(fileURLToPath(import.meta.url)); |
| const CHAT_JS = readFileSync(join(__dirname, '..', 'src/handlers/chat.js'), 'utf8'); |
|
|
| describe('upstream-timeout cascade invalidation (#101)', () => { |
| test('stream catch block matches the timeout patterns and sets reuseEntryDead', () => { |
| |
| |
| |
| |
| const m = CHAT_JS.match(/lastErr = err;\s+reuseEntry = null;[\s\S]{0,1500}?const isAuthFail = /); |
| assert.ok(m, 'stream catch block region not found β refactor may have changed shape'); |
| const region = m[0]; |
| assert.match(region, /context deadline exceeded/i, |
| 'stream timeout regex must mention "context deadline exceeded"'); |
| assert.match(region, /context cancellation while reading body/i, |
| 'stream timeout regex must mention "context cancellation while reading body"'); |
| assert.match(region, /client\\?\.timeout/i, |
| 'stream timeout regex must include Client.Timeout fallback'); |
| assert.match(region, /reuseEntryDead = true/, |
| 'stream timeout branch must set reuseEntryDead = true'); |
| }); |
|
|
| test('non-stream branch matches the same timeout patterns and sets reuseEntryDead', () => { |
| |
| |
| |
| |
| |
| const m = CHAT_JS.match(/if \(result\.reuseEntryInvalid\) reuseEntryDead = true;[\s\S]{0,800}?lastErr = result;/); |
| assert.ok(m, 'non-stream invalidation region not found β refactor may have changed shape'); |
| const region = m[0]; |
| assert.match(region, /context deadline exceeded/i); |
| assert.match(region, /context cancellation while reading body/i); |
| assert.match(region, /client\\?\.timeout/i); |
| assert.match(region, /reuseEntryDead = true/); |
| }); |
|
|
| test('regex actually matches the user-reported error message verbatim', () => { |
| |
| |
| |
| |
| |
| const re = /context deadline exceeded|context cancellation while reading body|client\.timeout/i; |
| const userError = 'Encountered retryable error from model provider: context deadline exceeded (Client.Timeout or context cancellation while reading body)'; |
| assert.ok(re.test(userError), |
| 'regex must match the literal error string from issue #101'); |
| }); |
|
|
| test('regex does NOT match unrelated rate-limit / panel-state errors', () => { |
| |
| |
| |
| const re = /context deadline exceeded|context cancellation while reading body|client\.timeout/i; |
| assert.equal(re.test('rate limit exceeded for model claude-opus-4-7'), false); |
| assert.equal(re.test('Panel state not found for sessionId xxx'), false); |
| assert.equal(re.test('cascade not_found upstream after 3 retries'), false); |
| assert.equal(re.test('internal error occurred (Error ID: abc)'), false); |
| }); |
| }); |
|
|