mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
Several routes answer with `{error: {code, message}}`, and the client
interpolated that object straight into an Error message, so the owner's toast
read `[object Object]` — an alarm carrying no fact. Read the object's own
sentence, then its code, and serialize as a last resort so nothing is
swallowed. The toast also wraps long unbroken tokens now instead of
overflowing its own box.
64 lines
2.3 KiB
JavaScript
64 lines
2.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
|
|
import { fetchJson } from '../modules/api_client.js';
|
|
|
|
function respond(status, body) {
|
|
const prior = globalThis.fetch;
|
|
globalThis.fetch = async () => ({
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
json: async () => body,
|
|
});
|
|
return () => { globalThis.fetch = prior; };
|
|
}
|
|
|
|
test('a structured error body becomes a sentence, never [object Object]', async () => {
|
|
const restore = respond(400, { error: { code: 'skill_not_finalized', message: 'Skill review is not finalized.' } });
|
|
try {
|
|
await assert.rejects(() => fetchJson('/api/x'), (error) => {
|
|
assert.equal(error.message, 'Skill review is not finalized.');
|
|
assert.equal(error.status, 400);
|
|
return true;
|
|
});
|
|
} finally { restore(); }
|
|
});
|
|
|
|
test('a structured error with only a code still names the code', async () => {
|
|
const restore = respond(409, { error: { code: 'conflict' } });
|
|
try {
|
|
await assert.rejects(() => fetchJson('/api/x'), /conflict/);
|
|
} finally { restore(); }
|
|
});
|
|
|
|
test('a structured error with neither message nor code is serialized, not swallowed', async () => {
|
|
const restore = respond(500, { error: { fields: ['a'] } });
|
|
try {
|
|
await assert.rejects(() => fetchJson('/api/x'), (error) => {
|
|
assert.equal(error.message, '{"fields":["a"]}');
|
|
assert.equal(error.message.includes('[object Object]'), false);
|
|
return true;
|
|
});
|
|
} finally { restore(); }
|
|
});
|
|
|
|
test('a plain string error and a bare status are unchanged', async () => {
|
|
let restore = respond(422, { error: 'Path escapes file browser root.' });
|
|
try {
|
|
await assert.rejects(() => fetchJson('/api/x'), /Path escapes file browser root\./);
|
|
} finally { restore(); }
|
|
restore = respond(503, {});
|
|
try {
|
|
await assert.rejects(() => fetchJson('/api/x'), /HTTP 503/);
|
|
} finally { restore(); }
|
|
});
|
|
|
|
test('rejectOkFalse still reads the structured error of a 200 body', async () => {
|
|
const restore = respond(200, { ok: false, error: { message: 'Nothing to restore.' } });
|
|
try {
|
|
await assert.rejects(
|
|
() => fetchJson('/api/x', {}, { rejectOkFalse: true }),
|
|
/Nothing to restore\./,
|
|
);
|
|
} finally { restore(); }
|
|
});
|