import test from 'node:test'; import assert from 'node:assert/strict'; import { CONFLICT_CHECK_REASON, CONFLICT_CHECK_STATE, conflictCheckPresentation, currentWeekDateRange, formatLocalDate, loadDepartmentSchedules } from '../admin/js/dashboard-conflicts.js'; process.env.TZ = 'Europe/Moscow'; test('неделя для 2026-07-02 охватывает 2026-06-29…2026-07-05', () => { assert.deepEqual( currentWeekDateRange(new Date('2026-07-01T21:15:00Z')), { startDate: '2026-06-29', endDate: '2026-07-05' } ); }); test('неделя корректно пересекает границу года', () => { assert.deepEqual( currentWeekDateRange(new Date(2026, 0, 1, 8, 30, 0)), { startDate: '2025-12-29', endDate: '2026-01-04' } ); }); test('date-only формируется из локальных компонентов', () => { assert.equal(formatLocalDate(new Date('2026-01-01T21:15:00Z')), '2026-01-02'); }); test('полный успех разрешает достоверный вывод по всем кафедрам', async () => { const result = await loadDepartmentSchedules( [{ id: 1 }, { id: 2 }], async department => [{ departmentId: department.id }] ); assert.equal(result.state, CONFLICT_CHECK_STATE.COMPLETE); assert.equal(result.checkedDepartmentCount, 2); assert.equal(result.totalDepartmentCount, 2); assert.deepEqual(result.lessons, [{ departmentId: 1 }, { departmentId: 2 }]); assert.deepEqual(result.failedDepartments, []); }); test('единичный отказ даёт частичный результат, а не пустой успешный ответ', async () => { const departments = [ { id: 1, departmentName: 'Физика' }, { id: 2, departmentName: 'Математика' } ]; const result = await loadDepartmentSchedules(departments, async department => { if (department.id === 2) { throw new Error('тестовая техническая причина'); } return [{ departmentId: department.id }]; }); assert.equal(result.state, CONFLICT_CHECK_STATE.PARTIAL); assert.equal(result.checkedDepartmentCount, 1); assert.deepEqual(result.lessons, [{ departmentId: 1 }]); assert.deepEqual(result.failedDepartments, [{ id: 2, name: 'Математика' }]); assert.equal('reason' in result.failedDepartments[0], false); }); test('отказ всех запросов означает, что проверка не выполнена', async () => { const result = await loadDepartmentSchedules( [{ id: 1 }, { id: 2 }], async () => { throw new Error('тестовый отказ'); } ); assert.equal(result.state, CONFLICT_CHECK_STATE.NOT_RUN); assert.equal(result.reason, CONFLICT_CHECK_REASON.ALL_REQUESTS_FAILED); assert.equal(result.checkedDepartmentCount, 0); assert.deepEqual(result.lessons, []); }); test('отсутствие кафедр не изображается как зелёная успешная проверка', async () => { const result = await loadDepartmentSchedules([], async () => []); assert.equal(result.state, CONFLICT_CHECK_STATE.NOT_RUN); assert.equal(result.reason, CONFLICT_CHECK_REASON.NO_DEPARTMENTS); }); test('зелёная карточка разрешена только для полной проверки без конфликтов', () => { const complete = { state: CONFLICT_CHECK_STATE.COMPLETE, checkedDepartmentCount: 2, totalDepartmentCount: 2 }; const range = { startDate: '2026-06-29', endDate: '2026-07-05' }; assert.equal(conflictCheckPresentation(complete, 0, range).tone, 'success'); assert.equal(conflictCheckPresentation(complete, 1, range), null); }); test('некорректный контракт результата не может породить зелёный статус', () => { const range = { startDate: '2026-06-29', endDate: '2026-07-05' }; assert.throws( () => conflictCheckPresentation({ state: CONFLICT_CHECK_STATE.COMPLETE, checkedDepartmentCount: 1, totalDepartmentCount: 2 }, 0, range), /Полная проверка должна охватывать все кафедры/ ); assert.throws( () => conflictCheckPresentation({ state: CONFLICT_CHECK_STATE.COMPLETE, checkedDepartmentCount: 2, totalDepartmentCount: 2 }, Number.NaN, range), /Количество конфликтов должно быть целым неотрицательным числом/ ); }); test('частичная проверка без конфликтов остаётся предупреждением', () => { const presentation = conflictCheckPresentation({ state: CONFLICT_CHECK_STATE.PARTIAL, checkedDepartmentCount: 1, totalDepartmentCount: 2 }, 0, { startDate: '2026-06-29', endDate: '2026-07-05' }); assert.equal(presentation.tone, 'warning'); assert.equal(presentation.title, 'Проверка выполнена частично'); assert.match(presentation.description, /во всём расписании нельзя/); }); test('частичная проверка с конфликтами предупреждает о неполноте результата', () => { const presentation = conflictCheckPresentation({ state: CONFLICT_CHECK_STATE.PARTIAL, checkedDepartmentCount: 1, totalDepartmentCount: 3 }, 2, { startDate: '2026-06-29', endDate: '2026-07-05' }); assert.equal(presentation.tone, 'warning'); assert.match(presentation.description, /Показаны найденные конфликты/); }); test('полный отказ формирует явное состояние «Проверка не выполнена»', () => { const presentation = conflictCheckPresentation({ state: CONFLICT_CHECK_STATE.NOT_RUN, reason: CONFLICT_CHECK_REASON.ALL_REQUESTS_FAILED, checkedDepartmentCount: 0, totalDepartmentCount: 2 }, 0, { startDate: '2026-06-29', endDate: '2026-07-05' }); assert.equal(presentation.tone, 'error'); assert.equal(presentation.title, 'Проверка не выполнена'); assert.match(presentation.description, /Подтвердить отсутствие конфликтов нельзя/); });