8.7 KiB
8.7 KiB
Результаты тестирования — v1.0.178
Дата: 25.06.2026
Сводка
| Уровень | Тестов | Результат |
|---|---|---|
| Юнит-тесты (Node.js) | 131 | ✅ 131/131 PASS |
node -c синтаксис |
6 файлов | ✅ чистый |
| API smoke (curl) | 8 эндпоинтов | ✅ HTTP 200 |
| Аудит кода | — | ✅ 4 бага исправлено |
Запуск тестов
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' |
'<script>' |
'<script>' |
'a&b' |
'a&b' |
'"quote"' |
'"quote"' |
123 |
'123' |
API Smoke Tests (curl)
| Эндпоинт | Код |
|---|---|
GET / |
200 |
POST /api/cleanup |
200 |
GET /static/state.js |
200 |
GET /static/app_utils.js |
200 |
GET /static/files.js |
200 |
GET /static/groups.js |
200 |
GET /static/compare.js |
200 |
GET /static/app.js |
200 |
Баги, найденные и исправленные при аудите
| # | Баг | Файл | Исправление |
|---|---|---|---|
| 1 | SITE_URL — неиспользуемая переменная |
app.js:8 | Удалена |
| 2 | UNZIP_URL — двойное объявление |
app.js:6-7 | Удалён дубль |
| 3 | promptEditor — двойное объявление |
app.js:467,470 | Удалён дубль |
| 4 | llmStart — неиспользуемая после Фазы 3 |
app.js:301 | Удалена |
Структура модулей (финальная)
contractor/deploy/
├── state.js (70 строк) Центральное состояние
├── app_utils.js (120 строк) escHtml, formatSize, removeFile, ...
├── files.js (480 строк) upload, parse, ZIP, renderFiles, syncDB
├── groups.js (230 строк) loadGroupsAction, renderGroups, renderGroupCard
├── compare.js (230 строк) startCompareSSE, applyCompareEvent, renderCompareOpsTable
├── app.js (450 строк) Оркестратор: render, classify, compare, prompts, chat
└── tests.js (400 строк) 131 юнит-тест
Загрузка в index.cfm: state → app_utils → files → groups → compare → app.
Команды для регресса
# Синтаксис всех модулей
cd contractor/deploy && for f in state.js app_utils.js files.js groups.js compare.js app.js; do node -c "$f" || echo "FAIL: $f"; done
# Юнит-тесты
node tests.js
# Деплой JS на ВМ
scp -i ~/.ssh/naeel_vm_id_ed25519 *.js naeel@5.172.178.213:/home/naeel/contracts/
# Дым API
curl -s --max-time 5 -o /dev/null -w "%{http_code}\n" https://contracts.kube5s.ru/static/state.js