прописал статусы

This commit is contained in:
gorbenko.pavel
2026-08-12 16:41:35 +05:00
parent 9fc089c37a
commit 0b2a05cd94
8 changed files with 258 additions and 6 deletions
@@ -1,11 +1,15 @@
import {
activatedAt,
boolToTriState,
effectiveSlaHours,
formatSlaHours,
isRowOverdue,
isValidSlaHours,
parseSlaHours,
slaHoursToInput,
triStateToBool
} from './sla';
import { APPROVAL_PHASE } from '../models';
// Смысл всех этих проверок один: «не задано» и «ноль/нет» — РАЗНЫЕ состояния.
// Если они где-то схлопнутся, шаг с настройкой «напоминать сразу» станет
@@ -84,3 +88,98 @@ describe('triState', () => {
expect(triStateToBool(boolToTriState(null))).toBeNull();
});
});
describe('effectiveSlaHours', () => {
it('своё значение строки перекрывает тип', () => {
expect(effectiveSlaHours({ slaHours: 8 }, 48)).toBe(8);
});
it('0 на строке — это не "не задано", тип не подставляется', () => {
expect(effectiveSlaHours({ slaHours: 0 }, 48)).toBe(0);
});
it('строка не задана — берётся SLA типа', () => {
expect(effectiveSlaHours({ slaHours: null }, 48)).toBe(48);
});
it('не задано нигде — null', () => {
expect(effectiveSlaHours({ slaHours: null }, null)).toBeNull();
});
});
// Повторяет ReminderService.ActivatedAt на бэкенде — та же формула, поэтому и
// проверяется на тех же случаях (параллельные шаги, переход между фазами).
describe('activatedAt', () => {
const row = (order: number, phase: string, dateIso: string) => ({ order, phase, dateIso });
it('первый шаг согласования — от создания заявки', () => {
const rows = [row(1, APPROVAL_PHASE.approval, '')];
const result = activatedAt(rows[0], rows, '2026-08-01T00:00:00Z');
expect(result?.toISOString()).toBe('2026-08-01T00:00:00.000Z');
});
it('второй шаг — от решения предыдущего, а не от создания заявки', () => {
const rows = [
row(1, APPROVAL_PHASE.approval, '2026-08-10T12:00:00Z'),
row(2, APPROVAL_PHASE.approval, '')
];
const result = activatedAt(rows[1], rows, '2026-01-01T00:00:00Z');
expect(result?.toISOString()).toBe('2026-08-10T12:00:00.000Z');
});
it('параллельный предыдущий шаг — от ПОСЛЕДНЕГО решения, а не от первого', () => {
const rows = [
row(1, APPROVAL_PHASE.approval, '2026-08-10T08:00:00Z'),
row(1, APPROVAL_PHASE.approval, '2026-08-10T20:00:00Z'),
row(2, APPROVAL_PHASE.approval, '')
];
const result = activatedAt(rows[2], rows, '2026-01-01T00:00:00Z');
expect(result?.toISOString()).toBe('2026-08-10T20:00:00.000Z');
});
it('первый шаг исполнения — от последнего решения фазы согласования', () => {
const rows = [
row(1, APPROVAL_PHASE.approval, '2026-08-10T08:00:00Z'),
row(2, APPROVAL_PHASE.approval, '2026-08-11T09:00:00Z'),
row(1, APPROVAL_PHASE.execution, '')
];
const result = activatedAt(rows[2], rows, '2026-01-01T00:00:00Z');
expect(result?.toISOString()).toBe('2026-08-11T09:00:00.000Z');
});
it('предыдущий шаг ещё не решён — активации нет (undefined)', () => {
const rows = [
row(1, APPROVAL_PHASE.approval, ''),
row(2, APPROVAL_PHASE.approval, '')
];
expect(activatedAt(rows[1], rows, '2026-01-01T00:00:00Z')).toBeUndefined();
});
});
describe('isRowOverdue', () => {
const now = new Date('2026-08-12T12:00:00Z');
it('срок вышел — просрочено', () => {
const rows = [{ order: 1, phase: APPROVAL_PHASE.approval, dateIso: '', slaHours: 24 as number | null }];
expect(isRowOverdue(rows[0], rows, '2026-08-10T00:00:00Z', null, now)).toBe(true);
});
it('срок не вышел — не просрочено', () => {
const rows = [{ order: 1, phase: APPROVAL_PHASE.approval, dateIso: '', slaHours: 24 as number | null }];
expect(isRowOverdue(rows[0], rows, '2026-08-12T00:00:00Z', null, now)).toBe(false);
});
it('SLA не настроен нигде — не просрочено, хотя заявка старая', () => {
const rows = [{ order: 1, phase: APPROVAL_PHASE.approval, dateIso: '', slaHours: null as number | null }];
expect(isRowOverdue(rows[0], rows, '2020-01-01T00:00:00Z', null, now)).toBe(false);
});
it('SLA типа используется, если на строке не задан', () => {
const rows = [{ order: 1, phase: APPROVAL_PHASE.approval, dateIso: '', slaHours: null as number | null }];
expect(isRowOverdue(rows[0], rows, '2026-08-10T00:00:00Z', 24, now)).toBe(true);
});
});