Unify Harbor error handling across server and client
All checks were successful
Build and Deploy Gateway / build-and-push (push) Successful in 15s
Build and Deploy Gateway / deploy (push) Successful in 12s

This commit is contained in:
2026-07-11 20:38:41 +03:00
parent 457dd912d1
commit 9da4fef1f0
16 changed files with 493 additions and 100 deletions

View File

@@ -0,0 +1,61 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
HarborApiError,
request,
validationStatusForError,
} from '../../src/web/api.js';
const response = (status, error) => ({
ok: status >= 200 && status < 300,
status,
json: async () => ({ success: false, error }),
});
test('frontend exposes retry only for retryable structured errors', async () => {
await assert.rejects(
request('/api/test', {}, async () => response(502, {
code: 'PROVIDER_UNAVAILABLE',
message: 'untrusted server copy',
retryable: false,
correlationId: 'provider-reference',
})),
(error) => {
assert.ok(error instanceof HarborApiError);
assert.equal(error.message, 'Провайдер подписки временно недоступен.');
assert.equal(error.retryable, true);
assert.equal(error.correlationId, 'provider-reference');
return true;
},
);
await assert.rejects(
request('/api/test', {}, async () => response(400, {
code: 'SUBSCRIPTION_INVALID',
retryable: true,
})),
(error) => error.retryable === false,
);
});
test('network failures become retryable control errors', async () => {
await assert.rejects(
request('/api/state', {}, async () => { throw new TypeError('fetch failed'); }),
(error) => error.code === 'CONTROL_UNREACHABLE' && error.retryable === true,
);
});
test('local unknown errors get a safe message and diagnostic reference', () => {
const error = new HarborApiError({ code: 'NOT_A_REAL_CODE' });
assert.equal(error.code, 'UNKNOWN');
assert.equal(error.message, 'Не удалось выполнить действие.');
assert.equal(typeof error.correlationId, 'string');
assert.ok(error.correlationId.length >= 8);
});
test('subscription validation distinguishes bad input from provider outage', () => {
assert.equal(validationStatusForError({ code: 'SUBSCRIPTION_INVALID' }), 'invalid');
assert.equal(validationStatusForError({ code: 'PROVIDER_UNAVAILABLE' }), 'unavailable');
});