Andrey Epifancev 1aec5d2c2d AURORA-1978: удаление пустой программы и проекта на форме 4
«Удалить строку» на строке программы (GROUP) или проекта (ITEM) ничего не
делала: у этих строк нет line_id, окно подтверждения не открывалось, а
события program_deleted / project_deleted, которые сервер уже поддерживает,
фронт не отправлял.

- getDeleteTarget: по выделенной строке выбирает программу, проект или
  строку бюджета; для непустых программы/проекта сразу показывает причину.
- RealtimeContext: deleteProgram / deleteProject и обработка ответов.
- useRealtimeData: строка убирается из дерева, пересчёт ROOT/GROUP из
  ответа сопоставляется по section_code / program_id / project_id
  (alignNodeRows), а не по sort_order — сервер нумерует строки заново, и у
  листа без строк бюджета нет ROOT (найдено при проверке на стенде).
- Окно подтверждения называет удаляемую программу/проект.
- getWsErrorMessage срезает префикс "<class 'asyncpg...'>: " — иначе отказ
  сервера (программа с проектами у устаревшего клиента) показывался с ним.

Refs #303

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 20:02:41 +04:00

74 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from 'node:assert/strict';
import { test } from 'node:test';
import { getWsErrorMessage } from './wsErrorUtils.js';
test('строка возвращается как есть', () => {
assert.equal(getWsErrorMessage('Ячейка уже редактируется другим пользователем'), 'Ячейка уже редактируется другим пользователем');
});
test('объект с message возвращает message', () => {
assert.equal(getWsErrorMessage({ code: 400, field: 'q1.transfer_econ', message: 'Нельзя указать отрицательное значение' }), 'Нельзя указать отрицательное значение');
});
test('объект без message возвращает detail', () => {
assert.equal(getWsErrorMessage({ detail: 'Ошибка подключения к базе данных' }), 'Ошибка подключения к базе данных');
});
test('объект без message и без detail возвращает дефолтный текст, а не undefined', () => {
const result = getWsErrorMessage({ code: 500 });
assert.notEqual(result, undefined);
assert.equal(typeof result, 'string');
assert.ok(!result.includes('undefined'));
});
test('префикс computed_field: заменяется человеческим текстом', () => {
assert.equal(getWsErrorMessage('computed_field: q1.corrected_plan'), 'Это расчётная ячейка, её нельзя изменить');
assert.equal(getWsErrorMessage({ message: 'computed_field: q1.corrected_plan' }), 'Это расчётная ячейка, её нельзя изменить');
});
test('префикс role_not_allowed: заменяется человеческим текстом', () => {
assert.equal(getWsErrorMessage('role_not_allowed: data.q1.transfer_econ'), 'Нет прав на изменение этой колонки');
});
test('префикс window_closed: заменяется человеческим текстом', () => {
assert.equal(getWsErrorMessage('window_closed: data.q1.transfer_econ'), 'Окно ввода для этой колонки закрыто');
});
test('префикс org_not_assigned заменяется человеческим текстом', () => {
assert.equal(getWsErrorMessage('org_not_assigned'), 'Вы не назначены на эту форму');
assert.equal(getWsErrorMessage({ message: 'org_not_assigned: form_id=123' }), 'Вы не назначены на эту форму');
});
test('неизвестный текст возвращается без технического префикса Server error:', () => {
const result = getWsErrorMessage('Database unavailable');
assert.equal(result, 'Database unavailable');
assert.ok(!result.startsWith('Server error:'));
});
test('пустая строка и отсутствующая ошибка не превращаются в undefined', () => {
assert.notEqual(getWsErrorMessage(''), undefined);
assert.notEqual(getWsErrorMessage(null), undefined);
assert.notEqual(getWsErrorMessage(undefined), undefined);
assert.ok(!String(getWsErrorMessage(undefined)).includes('undefined'));
});
// AURORA-1978: map_sqlalchemy_error берёт str(exc.orig), и для RAISE из
// PL/pgSQL текст приходит как "<class 'asyncpg.exceptions.RaiseError'>: ...".
// Технический префикс класса в тост не попадает; известные префиксы после него
// по-прежнему переводятся.
test('AURORA-1978: префикс класса исключения asyncpg срезается', () => {
assert.equal(
getWsErrorMessage({
code: 400,
field: '',
message: "<class 'asyncpg.exceptions.RaiseError'>: Нельзя удалить программу, содержащую проекты",
}),
'Нельзя удалить программу, содержащую проекты',
);
assert.equal(
getWsErrorMessage("<class 'asyncpg.exceptions.RaiseError'>: role_not_allowed: колонка plan.q1"),
'Нет прав на изменение этой колонки',
);
assert.equal(getWsErrorMessage("<class 'asyncpg.exceptions.RaiseError'>: "), 'Неизвестная ошибка сервера');
});