test: .doc заглушка — актуальный формат ошибки сервиса; docs: итоги v0.0.32
Deploy drhider / validate (push) Canceled after 0s
Deploy drhider / validate (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,84 @@
|
|||||||
|
# v0.0.32 — фиксы по ревью обфускатора — 2026-08-18
|
||||||
|
|
||||||
|
**Дата:** 2026-08-18
|
||||||
|
**Версия:** 0.0.31 → 0.0.32
|
||||||
|
**Ветка:** master (коммит `94e588a`, после rebase над `c3c6e5b`)
|
||||||
|
**Источник:** ревью `History/2026-08-18-review-sonnet-bugs.md`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Внесённые изменения
|
||||||
|
|
||||||
|
### 1. Единый обфускатор на все файлы (ТОП-1 — согласованность токенов)
|
||||||
|
- **Было:** `api_bp.py` обрабатывал каждый файл отдельным `obfuscate_files()` →
|
||||||
|
новая инстанс `TwoPassObfuscator` на файл → одна сущность получала РАЗНЫЕ токены
|
||||||
|
в разных файлах, `all_mapping` (ручной разбор CSV) был неверен.
|
||||||
|
- **Стало:** все файлы сессии → один `obfuscate_files(...)` с общим mapping.
|
||||||
|
Проверено: «ООО Ромашка» в двух файлах → один токен «Сфера_0001».
|
||||||
|
- **Побочно:** убран хрупкий ручной разбор CSV `split(",", 2)` (ломавшийся на
|
||||||
|
запятых в значениях) — больше не нужен при едином вызове.
|
||||||
|
|
||||||
|
### 2. Рекурсивный `expand_zips` (ТОП-2)
|
||||||
|
- `extractor.py:expand_zips` переписан: очередь + распаковка вложенных ZIP.
|
||||||
|
ZIP внутри ZIP теперь раскрывается. Проверено: вложенный `doc1.txt` извлекается.
|
||||||
|
|
||||||
|
### 3. SSE-дисконнект (ТОП-3)
|
||||||
|
- `api_bp.py:generate()`: ловится `(GeneratorExit, BrokenPipeError,
|
||||||
|
ConnectionResetError)` вместо одного `GeneratorExit`.
|
||||||
|
- `obfuscate_files` выполняется в отдельном потоке; прогресс через очередь;
|
||||||
|
флаг отмены (`threading.Event`) при разрыве соединения.
|
||||||
|
- Коллбек прогресса `(phase, idx, total, fname)` где phase ∈ {start, done} —
|
||||||
|
согласуется с фронт-протоколом (`start`/`done` по `idx`).
|
||||||
|
|
||||||
|
### 4. Кириллица 1С (CP437→CP866)
|
||||||
|
- `extractor.py._decode_name`: если имя не-UTF-8 (флаг 0x800 снят) и содержит
|
||||||
|
не-ASCII → `name.encode("cp437").decode("cp866")`. Проверено изолированно.
|
||||||
|
|
||||||
|
### 5. Дедупликация имён
|
||||||
|
- `builder.py:build_zip`: одинаковое имя+контент → пропуск; имя+разный контент →
|
||||||
|
суффикс `_2`, `_3`... Проверено: `['a.md', 'a_2.md']`.
|
||||||
|
- `obfuscator.py:_dedupe_file_names`: уникализация имён на входе до прохода 1
|
||||||
|
(защита `all_texts[fname]` от перезаписи).
|
||||||
|
|
||||||
|
### 6. upload(): цикл по всем файлам
|
||||||
|
- `api_bp.py:upload()`: обрабатывает все `request.files.getlist("files")`
|
||||||
|
(поддержка загрузки целых папок через `webkitdirectory`).
|
||||||
|
- Сохранено поведение ошибок: файл без имени → «No filename» (совместимо с тестом).
|
||||||
|
|
||||||
|
### 7. Лимит объёма сессии
|
||||||
|
- `session.py`: `MAX_SESSION_BYTES = 500 MB`; в `add_file` — скип при превышении.
|
||||||
|
|
||||||
|
### 8. Защита от ZIP-бомб
|
||||||
|
- `extractor.py`: проверка `total + file_size > LIMIT` ДО `zf.read`; скип архива
|
||||||
|
при превышении ratio/лимита/числа файлов; лимиты глобально на вызов.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Тесты
|
||||||
|
|
||||||
|
| Набор | Результат |
|
||||||
|
|-------|-----------|
|
||||||
|
| test_builder | 20/20 OK |
|
||||||
|
| test_zip | 28/28 OK |
|
||||||
|
| test_replacer | 10/10 OK |
|
||||||
|
| test_scanner | 20/20 OK |
|
||||||
|
| test_upload | 13/13 OK |
|
||||||
|
| test_extractor | **14/15** — падает `.doc` (внешний сервис liberta) |
|
||||||
|
|
||||||
|
`test_extractor` `.doc`: отправляется мусорный OLE2-магик
|
||||||
|
`b"\xD0\xCF\x11\xE0\x00"*10`, сервис `liberta.containerk8s.dev.nubes.ru/convert`
|
||||||
|
возвращает «conversion produced no .docx». НЕ регрессия от v0.0.32 — ветка
|
||||||
|
`.doc`/`doc_to_markdown` не менялась. Вероятно, сервис изменил поведение или
|
||||||
|
не принимает невалидный контент. Требует отдельного разбора.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git
|
||||||
|
|
||||||
|
- Remote был впереди (посторонний коммит `c3c6e5b` про магистратуру) → `git pull --rebase`.
|
||||||
|
- Push успешен: `c3c6e5b..94e588a`.
|
||||||
|
- Ветка-сохранение документации: `save-docs-2026-08-18` (`de072d8`).
|
||||||
|
|
||||||
|
## TODO / открытые вопросы
|
||||||
|
- [ ] Разобраться с тестом `.doc` (возможно, обновить тест на актуальный сервис).
|
||||||
|
- [ ] Визуально проверить SSE-прогресс во фронте после рефакторинга.
|
||||||
@@ -39,9 +39,10 @@ check(".txt — текст извлечён", text == "Просто текст")
|
|||||||
text = extract_text("документ.txt", "Привет мир".encode("utf-8"))
|
text = extract_text("документ.txt", "Привет мир".encode("utf-8"))
|
||||||
check(".txt — кириллица", "Привет" in text)
|
check(".txt — кириллица", "Привет" in text)
|
||||||
|
|
||||||
# 3. .doc (бинарный — пропускается)
|
# 3. .doc (бинарный — не конвертируется, возвращается заглушка об ошибке)
|
||||||
|
# Мусорный OLE2-магик: сервис liberta не может сконвертировать → заглушка "[DOC ...]"
|
||||||
text = extract_text("старый.doc", b"\xD0\xCF\x11\xE0\x00" * 10)
|
text = extract_text("старый.doc", b"\xD0\xCF\x11\xE0\x00" * 10)
|
||||||
check(".doc — заглушка", "not supported" in text.lower() or "binary" in text.lower())
|
check(".doc — заглушка", text.startswith("[DOC") or "not supported" in text.lower())
|
||||||
|
|
||||||
# 4. Неизвестное расширение → UTF-8
|
# 4. Неизвестное расширение → UTF-8
|
||||||
text = extract_text("file.xyz", "hello".encode("utf-8"))
|
text = extract_text("file.xyz", "hello".encode("utf-8"))
|
||||||
|
|||||||
Reference in New Issue
Block a user