Andrey Epifancev 6ba677cd39 feat(formulas): утилиты карты формул и API чтения формул (AURORA-1896)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 20:07:04 +04:00

111 lines
3.8 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyFormulaEvent,
buildFormulaMap,
clearLegacyFormulaStorage,
formatFormulaHint,
formulaKey,
isFormulaActual,
} from './cellFormulaUtils.js';
const user = { id: 7, full_name: 'Иванов И.И.' };
test('formulaKey склеивает line_id и column', () => {
assert.equal(formulaKey(5, 'q1'), '5|q1');
});
test('buildFormulaMap строит карту по ключу', () => {
const map = buildFormulaMap([{ line_id: 1, column: 'a', expression: '1+2', value: 3, updated_by: user, updated_at: 'x' }]);
assert.equal(map.size, 1);
assert.equal(map.get('1|a').expression, '1+2');
assert.equal(buildFormulaMap(null).size, 0);
});
test('applyFormulaEvent: строка записывает формулу', () => {
const map = new Map();
const next = applyFormulaEvent(map, {
line_id: 1,
column: 'a',
value: '150',
formula: '100+50',
formula_meta: { updated_by: user, updated_at: '2026-10-27T14:03:00' },
});
assert.notEqual(next, map);
assert.equal(map.size, 0);
assert.deepEqual(next.get('1|a'), {
line_id: 1,
column: 'a',
expression: '100+50',
value: 150,
updated_by: user,
updated_at: '2026-10-27T14:03:00',
});
});
test('applyFormulaEvent: formula null удаляет, вход не мутируется', () => {
const map = buildFormulaMap([{ line_id: 1, column: 'a', expression: '1', value: 1 }]);
const next = applyFormulaEvent(map, { line_id: 1, column: 'a', value: 2, formula: null });
assert.equal(next.size, 0);
assert.equal(map.size, 1);
});
test('applyFormulaEvent: без поля formula удаляет', () => {
const map = buildFormulaMap([{ line_id: 1, column: 'a', expression: '1', value: 1 }]);
const next = applyFormulaEvent(map, { line_id: 1, column: 'a', value: 2 });
assert.equal(next.size, 0);
});
test('applyFormulaEvent: фоновое событие без line_id/column возвращает ту же карту', () => {
const map = buildFormulaMap([{ line_id: 1, column: 'a', expression: '1', value: 1 }]);
assert.equal(applyFormulaEvent(map, { source_sheet: 'AHR_RENT' }), map);
assert.equal(applyFormulaEvent(map, { line_id: 1, source_sheet: 'X' }), map);
assert.equal(map.size, 1);
});
test('isFormulaActual', () => {
const f = { expression: '100+50', value: 150 };
assert.equal(isFormulaActual(f, 150), true);
assert.equal(isFormulaActual(f, '150'), true);
assert.equal(isFormulaActual({ ...f, value: '150' }, 150.0000000001), true);
assert.equal(isFormulaActual({ ...f, value: 150 }, 151), false);
assert.equal(isFormulaActual(null, 150), false);
assert.equal(isFormulaActual({ ...f, value: 150 }, 'abc'), false);
assert.equal(isFormulaActual({ ...f, value: 150 }, null), false);
assert.equal(isFormulaActual({ ...f, value: 150 }, ''), false);
});
test('formatFormulaHint', () => {
assert.equal(
formatFormulaHint({ expression: '100+50', updated_by: user, updated_at: '2026-10-27T14:03:00' }),
'=100+50 · Иванов И.И. · 27.10.2026 14:03',
);
assert.equal(
formatFormulaHint({ expression: '1+1', updated_by: { id: 7 }, updated_at: '2026-10-27T14:03:00' }),
'=1+1 · Пользователь #7 · 27.10.2026 14:03',
);
assert.equal(formatFormulaHint({ expression: '1+1', updated_by: user }), '=1+1 · Иванов И.И.');
});
test('clearLegacyFormulaStorage', () => {
const keys = ['formula_v2_a', 'formula_x', 'other'];
const storage = {
get length() {
return keys.length;
},
key: (i) => keys[i] ?? null,
removeItem: (k) => {
keys.splice(keys.indexOf(k), 1);
},
};
assert.equal(clearLegacyFormulaStorage(storage), 2);
assert.deepEqual(keys, ['other']);
const broken = {
get length() {
throw new Error('denied');
},
};
assert.equal(clearLegacyFormulaStorage(broken), 0);
assert.equal(clearLegacyFormulaStorage(null), 0);
});