127 lines
8.4 KiB
JavaScript
127 lines
8.4 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { buildSummary, STATUS_WORD } from './summary.mjs';
|
|
import { FINAL_STATUSES } from './supervisor.mjs';
|
|
|
|
const base = { run_id: 'r-1', disk_free_gb: 42.5, workers: [], lines: [] };
|
|
|
|
describe('buildSummary', () => {
|
|
it('прогона не было вовсе — сводка всё равно есть и так и говорит', () => {
|
|
const s = buildSummary({ ...base, run_id: null });
|
|
expect(s.subject).toMatch(/сводка/i);
|
|
expect(s.text).toMatch(/прогона не было/i);
|
|
});
|
|
|
|
it('за прогон ничего не происходило — сводка всё равно есть', () => {
|
|
const s = buildSummary(base);
|
|
expect(s.text).toMatch(/дел не было/i);
|
|
});
|
|
|
|
it('всегда есть строка про свободное место', () => {
|
|
expect(buildSummary(base).text).toContain('42.5');
|
|
});
|
|
|
|
it('погашенный по восьми часам назван С МЕСТОМ (проверка 5)', () => {
|
|
const s = buildSummary({ ...base, workers: [{ worker_id: 'w-1', status: 'extinguished', reason: 'упёрся в восемь часов', at_item: '4.11' }] });
|
|
expect(s.text).toContain('w-1');
|
|
expect(s.text).toContain('4.11');
|
|
});
|
|
|
|
it('состояние работника напечатано ПО-РУССКИ: «погашен», а не extinguished', () => {
|
|
// 🔴 Владелец читает письмо глазами. Английское слово в письме — то же самое,
|
|
// что его отсутствие: он не поймёт, погас работник или сломался.
|
|
const s = buildSummary({ ...base, workers: [
|
|
{ worker_id: 'w-1', status: 'extinguished', at_item: '4.11' },
|
|
{ worker_id: 'w-2', status: 'stalled', at_item: '2.3' },
|
|
{ worker_id: 'w-3', status: 'paused', at_item: '1.1' },
|
|
] });
|
|
expect(s.text).toContain(STATUS_WORD.extinguished);
|
|
expect(s.text).toContain(STATUS_WORD.stalled);
|
|
expect(s.text).toContain(STATUS_WORD.paused);
|
|
expect(s.text).not.toContain('extinguished');
|
|
expect(s.text).not.toContain('stalled');
|
|
expect(s.text).not.toContain('paused');
|
|
});
|
|
|
|
// 🔴 СТОРОЖ КЛАССА, а не одного забытого слова. Он не перечисляет состояния руками —
|
|
// он берёт ИХ СОБСТВЕННЫЙ список у надзирателя (`FINAL_STATUSES`) и требует русское слово
|
|
// к каждому. Заведут завтра четвёртое состояние и опять забудут слово — эта проверка
|
|
// покраснеет сама, без единой правки. Проверка «одного слова» так не умеет: она стережёт
|
|
// ровно ту дыру, которую уже нашли, и слепа к следующей такой же.
|
|
it('КЛАСС: у КАЖДОГО итогового состояния работника есть русское слово', () => {
|
|
for (const status of FINAL_STATUSES) {
|
|
const slovo = STATUS_WORD[status];
|
|
expect(slovo, `состояние «${status}» завели, а русского слова к нему не дали — владелец прочитает английское`).toBeTruthy();
|
|
// 🪤 Мало «слово есть»: слово `born_dead` само себе тоже «есть». Латиница в ответе —
|
|
// признак, что состояние переписали английским, а не перевели.
|
|
expect(slovo, `слово состояния «${status}» написано латиницей — для владельца это пустой звук`).not.toMatch(/[A-Za-z]/);
|
|
}
|
|
});
|
|
|
|
it('мертворождённый назван по-русски, а не словом born_dead', () => {
|
|
// 🔴 Замерено живьём 03.08.2026: в таблице стояло «· w-4: born_dead — Работника не удалось…».
|
|
const s = buildSummary({ ...base, workers: [{ worker_id: 'w-4', status: 'born_dead', reason: 'Работника не удалось запустить.' }] });
|
|
expect(s.text).toContain(STATUS_WORD.born_dead);
|
|
expect(s.text).not.toContain('born_dead');
|
|
});
|
|
|
|
it('слово состояния незнакомое — печатается оно само, а не пустота', () => {
|
|
// Незнакомое слово тоже сведение: молча съесть его — соврать владельцу.
|
|
const s = buildSummary({ ...base, workers: [{ worker_id: 'w-1', status: 'что-то-новое' }] });
|
|
expect(s.text).toContain('что-то-новое');
|
|
expect(s.text).not.toMatch(/undefined/);
|
|
});
|
|
|
|
it('погашенный по «служба не ответила» назван этой причиной, а не часами (проверка 13)', () => {
|
|
const s = buildSummary({ ...base, workers: [{ worker_id: 'w-2', status: 'extinguished', reason: 'Погашен: служба не ответила.', at_item: '2.3' }] });
|
|
expect(s.text).toMatch(/служба не ответила/i);
|
|
expect(s.text).toContain('2.3');
|
|
});
|
|
|
|
it('по КАЖДОМУ работнику своя строка про деньги, права и видимое клиенту', () => {
|
|
const s = buildSummary({ ...base, workers: [
|
|
{ worker_id: 'w-1', touched: { money: true, rights: false, client_visible: false } },
|
|
{ worker_id: 'w-2', touched: { money: false, rights: false, client_visible: true } },
|
|
] });
|
|
const money = s.text.split('\n').filter(l => l.includes('деньги'));
|
|
expect(money.length).toBe(2);
|
|
});
|
|
|
|
it('строка «что трогали» говорит «по коммитам похоже», а не утверждает наверняка', () => {
|
|
const s = buildSummary({ ...base, workers: [{ worker_id: 'w-1', touched: { money: true, rights: false, client_visible: false } }] });
|
|
expect(s.text).toMatch(/по коммитам похоже/i);
|
|
expect(s.text).toMatch(/следов не видно/i);
|
|
expect(s.text).toMatch(/приблизительный/i); // признак честно назван приблизительным
|
|
});
|
|
|
|
it('надзиратель ничего про работника не записал — сводка говорит «не записано», а не «нет»', () => {
|
|
// 🪤 Прежде строка навсегда говорила бы «нет, нет, нет», потому что заполнять её было некому.
|
|
const s = buildSummary({ ...base, workers: [{ worker_id: 'w-1' }] });
|
|
expect(s.text).toMatch(/не записано/i);
|
|
});
|
|
|
|
it('готовые строки из ленты попадают в сводку без переписывания', () => {
|
|
const s = buildSummary({ ...base, lines: ['Убран рабочий угол работника w-1 (прогон r-1): задача не принята 7 дней.'] });
|
|
expect(s.text).toContain('Убран рабочий угол работника w-1');
|
|
});
|
|
|
|
it('сервер перезагружался — строка об этом есть', () => {
|
|
const s = buildSummary({ ...base, server_rebooted_at: Date.parse('2026-07-30T02:00:00+03:00') });
|
|
expect(s.text).toMatch(/перезагружался/i);
|
|
});
|
|
|
|
it('замер отдал ноль — это названо подозрительным, а не подано как правда', () => {
|
|
// 🪤 «Ноль почти всегда сбой» (замер 20.07.2026), но настоящий ноль означает кончившийся
|
|
// диск. Оба смысла названы вслух: тихо напечатать «0 ГБ» — соврать в обе стороны.
|
|
const s = buildSummary({ ...base, disk_free_gb: 0 });
|
|
expect(s.text).toMatch(/сбой замера/i);
|
|
expect(s.text).toMatch(/немедленно/i);
|
|
});
|
|
|
|
it('место замерить не удалось — так и сказано, а не «0 ГБ»', () => {
|
|
// Ноль почти всегда сбой, а не правда о мире: замер 20.07.2026.
|
|
const s = buildSummary({ ...base, disk_free_gb: null });
|
|
expect(s.text).toMatch(/замерить не удалось/i);
|
|
expect(s.text).not.toMatch(/0 ГБ/);
|
|
});
|
|
});
|