|
25 | 25 | // OPPOSITE overreach, a predicate widened to "any 4xx is expected", which |
26 | 26 | // would silence the un-coded 400 that `mapDataError` degrades an |
27 | 27 | // unrecognised error (a handler `TypeError`) to. |
| 28 | +// |
| 29 | +// [#5489] That last sentence describes the world before the unrecognised-error |
| 30 | +// fallback became a sanitised 500. The handler-bug case below now asserts 500; |
| 31 | +// its adversary is no longer a widened 4xx predicate but any future attempt to |
| 32 | +// add 500 to `isExpectedDataStatus`. The invariant it guards — a real handler |
| 33 | +// bug is never silent — is the same one, and is now carried by the status band |
| 34 | +// rather than by the absence of a `code`. |
28 | 35 |
|
29 | 36 | import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; |
30 | 37 | import { RestServer } from './rest-server'; |
@@ -168,20 +175,28 @@ describe('metadata routes — genuine faults keep the loud log (#4886)', () => { |
168 | 175 | expect(res.statusCode).toBe(500); |
169 | 176 | }); |
170 | 177 |
|
171 | | - it('an UNRECOGNISED error (handler bug) stays loud even though it maps to 400', async () => { |
172 | | - // This is the case a blanket "any 4xx is expected" predicate would |
173 | | - // wrongly silence: `mapDataError` degrades anything it recognises |
174 | | - // nothing about to an UN-CODED 400, and that is where a real handler |
175 | | - // bug lands. Silencing it would be the mirror-image of #4886. |
| 178 | + it('an UNRECOGNISED error (handler bug) stays loud — and is a 500, not a 400 (#5489)', async () => { |
| 179 | + // The loudness is what #4886 pinned, and it is unchanged. What moved is |
| 180 | + // WHY it is structural: this case used to land on `mapDataError`'s |
| 181 | + // un-coded 400 fallback, so the guard read "loud even though it maps to |
| 182 | + // 400" and its adversary was a predicate widened to "any 4xx is |
| 183 | + // expected". #5489 made that fallback a sanitised 500 |
| 184 | + // (`UNCLASSIFIED_FAULT`) because a handler bug is not the caller's |
| 185 | + // fault and an SDK must not read "do not retry" off it. 500 is outside |
| 186 | + // `isExpectedDataStatus` entirely, so the log line no longer depends on |
| 187 | + // the predicate staying narrow in the 4xx band. |
176 | 188 | const bug = new TypeError('Cannot read properties of undefined (reading \'name\')'); |
177 | 189 | const { rest } = setup({ getMetaItem: vi.fn().mockRejectedValue(bug) }); |
178 | 190 |
|
179 | 191 | const res = await callMetaItem(rest, { type: 'object', name: 'showcase_account' }); |
180 | 192 |
|
181 | 193 | expect(unhandledLogs()).toHaveLength(1); |
182 | 194 | expect(unhandledLogs()[0][1]).toBe(bug); |
183 | | - expect(res.statusCode).toBe(400); |
184 | | - expect(res.body?.code).toBeUndefined(); |
| 195 | + expect(res.statusCode).toBe(500); |
| 196 | + expect(res.body?.code).toBe('INTERNAL_ERROR'); |
| 197 | + // The bug's own words are the operator's, not the client's — and the |
| 198 | + // log line above is where they went. |
| 199 | + expect(JSON.stringify(res.body)).not.toContain('Cannot read properties'); |
185 | 200 | }); |
186 | 201 | }); |
187 | 202 |
|
|
0 commit comments