8.3 KiB
Схема Опуса — генератор + симулятор (v1.0)
Ответ Опуса от 25.06.2026 на запрос History/opus-generator-request.md.
Оценка схемы
Сильные стороны
-
v1→v2 diff-пары — ключевая идея. Генератор закладывает конкретные изменения между версиями спецификаций (цена, объём, ADD/DELETE/UPDATE), и верификатор проверяет что compare нашёл именно их. Это даёт детерминированную проверку самой важной функции сервиса.
-
manifest.json как ground truth — каждая проверка сверяется с эталоном, а не с «примерно ожидаемым». Никакой неоднозначности.
-
Пул услуг из реальных данных — стойко-место, PDU, L2-канал, cross-connect, Cloud, IPv4. Не выдуманные, а те что в реальных файлах.
-
17 ошибочных кейсов — покрытие крайне широкое: от битого XML до сиротских спецификаций.
-
Структура папок —
testgen/+sim/с чётким разделением генерации и симуляции.
Что нужно уточнить/доработать
-
Симулятор: API или браузер? Опус описывает «реальное приложение (API/UI)». Два варианта:
- curl-симулятор (только API): дёргаем
/upload,/api/classify-batch,/api/groups,/process-v2. Быстро, дёшево, но не проверяет фронтенд (duplicate confirm, renderFiles, SSE в UI). - Playwright (полный UI): открывает браузер, кликает кнопки, читает DOM. Медленно, но проверяет ВСЁ включая confirm-диалоги.
- Рекомендация: curl для генерации/классификации/групп + выборочно Playwright для UI-специфичных сценариев (дубликаты, confirm, удаление).
- curl-симулятор (только API): дёргаем
-
.doc файлы — парсинг
допник-1-XXX002-01200_3.docне удался (старый формат). Нужен LibreOffice для конвертации, либо исключить .doc из генерации и использовать только .docx. -
Стресс-сценарий F — rapid-fire загрузки во время classify. Это может быть сложно воспроизвести и проверить. Вероятно, отложить на потом.
-
Количество файлов — раскладка даёт ~92 файла + edge cases. Можно докрутить до ровно 100 добавив ещё дубликатов или мусора.
Исходная схема Опуса
Часть 1 — Генерация ~100 файлов
Базовая структура: 10 компаний × 10 договоров × (спецификация v1 + v2 + 1-3 допника)
Раскладка:
| Категория | Кол-во | Зачем |
|---|---|---|
| Договоры (валидные) | 10 | по 1 на компанию |
| Спецификации v1 | 10 | базовая версия |
| Спецификации v2 (ревизия) | 10 | diff для сравнения |
| Допники | 18 | 1–3 на договор |
| Битые/edge .docx | 15 | ошибки парсинга/классификации |
| Форматы .doc/.pdf | 12 | ветка конвертера |
| ZIP-архивы | 5 | внутри — наборы файлов |
| Дубликаты (имя/контент) | 10 | upload-логика |
| Мусор (не договор) | 10 | негативная классификация |
| Итого | ~100 |
Оси вариативности:
- Компания: ЗАО/ООО/АО/ПАО/ИП, латиница/кириллица
- Номер: 5 цифр, опц. суффикс
_N, edge — буквы/длинный/пустой - Дата: 12.2025–06.2026, форматы
01.02.2026и «01 февраля 2026» - Цены: базовые ±20%, разрядность с пробелом
- Услуги (пул): стойко-место, PDU, L2-канал, IPv4 /30 и /32, cross-connect ВОЛС/UTP, Cloud Intel/AMD, Интернет, порт, подсеть
- Таблица: 3–12 строк, 6 или 7 колонок
Различия v1→v2 (для компаратора): изменение цены, изменение объёма, добавленная услуга, удалённая услуга, переименование, сдвиг даты, пересчёт итога.
Ошибочные кейсы (17 шт): нет номера, нестандартный заголовок, битый XML, пустой файл, только таблицы, только текст, .doc формат, битый PDF, дубликат имени, дубликат контента, гигант (500 строк), не-договор, сирота (спека без договора), рассинхрон (имя файла ≠ номер в тексте).
Parent-child: ключ — НОМЕР. Генератор связывает договор→спеку/допник по НОМЕРу.
Шаблоны python-docx:
- Билдеры:
build_contract(ctx),build_spec(ctx),build_addendum(ctx) - ctx = {company, number, date, parties, services[], totals}
- Хелперы:
add_title,add_section,add_services_table - Битый XML: валидный docx → распаковать zip → испортить document.xml → запаковать
- .doc: docx → libreoffice --convert-to doc
- Битый PDF: docx → pdf → обрезать байты
Часть 2 — Симулятор
Словарь действий: upload, delete, classify, groups, compare (SSE), confirm/cancel, reset.
Сценарии:
- A. Хэппи-путь: договор + спека v1 + v2 → classify → группа → compare → diff совпал
- B. Зигзаг: загрузил 3 → удалил 1 → добавил 2 → classify → проверить консистентность
- C. Bulk: все ~100 разом → classify → число групп, битые ✗
- D. Тупые действия: дубликат (OK/Отмена), удалить всё, смешать форматы, classify на нуле, compare с одной версией
- E. ZIP: валидный, с дубликатами, с битым файлом внутри
- F. Стресс: случайные паузы, rapid-fire, загрузка во время classify
Тайминги: think-time (random 0.1-3с), classify — поллинг, compare — SSE до done/таймаут.
Часть 3 — Верификация
После каждого шага:
state.filescount == ожидаемого- Статусы ✓/✗ соответствуют (битые → ✗)
- После classify: группы есть, состав по НОМЕРу верный
- Сироты без краша
- Compare: diff == заложенному (ground truth из manifest.json)
- Дубликаты: счётчик после OK ≠ после Отмена
- Нет 500-х, нет необработанных исключений
- Идемпотентность classify
Ground truth: manifest.json — что сгенерировано + ожидаемые группы и diff-ы.
Структура папок
testgen/
pools.py # услуги, компании, цены
templates.py # build_contract / build_spec / build_addendum
corrupt.py # порча docx/pdf, конвертация .doc
generate.py # оркестратор → out/ + manifest.json
out/{valid,errors,formats,zips,duplicates}/
manifest.json # ground truth
sim/
actions.py # обёртки над API
scenarios.py # A..F
verify.py # чек-лист
run.py