# Результаты тестирования — v1.0.178 Дата: 25.06.2026 ## Сводка | Уровень | Тестов | Результат | |---------|--------|-----------| | Юнит-тесты (Node.js) | **131** | **✅ 131/131 PASS** | | `node -c` синтаксис | 6 файлов | ✅ чистый | | API smoke (curl) | 8 эндпоинтов | ✅ HTTP 200 | | Аудит кода | — | ✅ 4 бага исправлено | ## Запуск тестов ```bash cd contractor/deploy && node tests.js ``` Тесты не требуют браузера — все зависимости замоканы (document, window, lucide, crypto, XMLHttpRequest). --- ## Юнит-тесты (131 шт.) ### statusToHTML (21 тест) Чистая функция: `{ kind, pct?, text?, count?, elapsed? }` → HTML. | Группа | Тесты | |--------|-------| | Граничные | `null`, `undefined`, `{}`, `{ kind: '' }`, `{ kind: 'unknown' }` → пусто | | `uploading` | pct=0, 75, 100, -5, без pct (→0%) | | `uploaded` | базовая проверка | | `unzipping` / `parsing` | текстовые статусы | | `parsed` | count=5, 0; elapsed='2.3', '0', без elapsed | | `error` | с текстом, без текста (→дефолт), с пустым текстом (→дефолт) | ### applyParseResult (13 тестов) Чистая функция: применяет `{ status, element_count, error }` к entry. | Группа | Тесты | |--------|-------| | `parsed` | с elapsed, без elapsed, element_count=0, без element_count | | `error` | с текстом, без текста (→дефолт "ошибка парсинга") | | `null` parsed | → `{ kind: 'error', text: 'Неизвестная ошибка' }` | | Иммутабельность | старые поля entry не затираются | ### reconcileSelection (7 тестов) Чистая функция: фильтрует `files` по именам из `newFiles`. | Группа | Тесты | |--------|-------| | Норма | 3 файла → 2 осталось, порядок сохранён | | Пустые | оба массива пусты → `[]` | | Все совпадают | 1→1 | | Ни один | 2→0 | | Дубликаты | Set корректно обрабатывает повторы | | Иммутабельность | исходный массив не мутирован | ### renderGroupCard (10 тестов) Чистая HTML-функция: карточка необработанной группы. | Группа | Тесты | |--------|-------| | Базовая | номер, контрагент, типы документов, дата, data-gi, кнопка «Сравнить» | | Нет «✓ Готово» | для необработанной группы | | `compare.status === 'running'` | кнопка disabled | | Без контрагента | заглушка «контрагент не определён» | | Неизвестный doc_type | выводится как есть | | Пустые документы | карточка рендерится, кнопка есть | | Типы | contract→договор, supplement→допсоглашение, specification→спецификация | ### renderGroupCardDone (5 тестов) Чистая HTML-функция: карточка обработанной группы. | Группа | Тесты | |--------|-------| | Заголовок | ✓ Готово (12.5с), стрелка сворачивания | | bodyHTML | вставлен в карточку | | data-gi | на заголовке для обработчика клика | | Типы | specification→спецификация | ### renderUnresolvedCard (3 теста) Чистая HTML-функция: карточка нераспознанных файлов. | Группа | Тесты | |--------|-------| | Заголовок | «Не распознано» | | Причина 1 | «нет базового договора №999» (есть parent_number) | | Причина 2 | «ошибка классификации: LLM error» (classify_status=failed) | ### applyCompareEvent (14 тестов) Чистая функция: мутирует `sections` по SSE-событиям. | Группа | Тесты | |--------|-------| | `extract_start` | создаёт секцию с filename, status='extracting' | | `llm_done` | обновляет status, ops_count, mode, time_s | | `applied` | сохраняет summary, ops | | `extract_error` | на существующей секции → status='error' | | `extract_error` | на НЕсуществующей → создаёт новую с ошибкой | | `apply_error` | аналогично extract_error | | `applied` без summary | не падает | | `applied` с пустыми ops | не падает | | Неизвестный тип | секция НЕ создаётся (не падает) | | `llm_done` на несуществующей | не падает | ### renderCompareSectionHeader (5 тестов) Чистая функция: заголовок секции сравнения. | Статус | Проверка | |--------|----------| | `extracting` | ⏳ + filename | | `llm_done` | ✓ + filename + ops_count + mode + time_s | | `error` | ✗ + filename + текст ошибки | ### renderCompareOpsTable (6 тестов) Чистая функция: HTML-таблица операций. | Группа | Тесты | |--------|-------| | ADD/DELETE | правильные CSS-классы (diff-added, diff-deleted) | | Данные | имя услуги, цена, количество, сумма, дата | | Пустые ops | таблица рендерится с пустым tbody | | null new_row | пустые ячейки (не падает) | | MERGE (неизв.) | без классов diff-* | ### renderCompareSectionBody (7 тестов) Чистая функция: тело секции (summary + таблица). | Группа | Тесты | |--------|-------| | Граничные | null, {}, status='llm_done' → пусто | | applied | +3 ~1 -0 ?2, UPDATE → diff-changed | | Без unresolved | нет знака ? | | Отрицательные summary | рендерится без ошибок | | Частичный new_row | нет undefined в выводе | ### escHtml (7 тестов) | Вход | Выход | |------|-------| | `null`, `undefined` | `''` | | `'hello'` | `'hello'` | | `'