v1.0.178: документация результатов тестирования (131 тест, API smoke, баги)

This commit is contained in:
“Naeel”
2026-06-25 09:43:53 +04:00
parent e61d1a19cd
commit 1545bcf31b
+208
View File
@@ -0,0 +1,208 @@
# Результаты тестирования — 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'` |
| `'<script>'` | `'&lt;script&gt;'` |
| `'a&b'` | `'a&amp;b'` |
| `'"quote"'` | `'&quot;quote&quot;'` |
| `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.
---
## Команды для регресса
```bash
# Синтаксис всех модулей
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
```