# Как создать автотесты для другого проекта Этот гайд описывает полный цикл создания автотестов для SEE и SMC на любом проекте — от первого запуска до полного набора asserts. ## Архитектура тестов Тесты **универсальные**: не содержат хардкода имён моделей. Всё привязывается динамически: | Компонент | Где | Что делает | |-----------|-----|-----------| | `TEXT_RULES` | В начале каждого теста | По ключевым словам в имени модели подбирает текст для запроса | | `DEFAULT_HOST` / `DEFAULT_PORT` | В начале каждого теста | Куда стучаться (меняешь под свой проект) | | `asserts.json` | Отдельный файл | Опциональные проверки — какой ответ ожидается от каждой модели | ## Шаг 1: Подготовка тестов к новому проекту Скопируй `test_see_models.py` и/или `test_smc_models.py` в репозиторий нового проекта. ### 1.1 Укажи адрес сервера В начале каждого тестового файла поменяй `DEFAULT_HOST` и `DEFAULT_PORT`: ```python DEFAULT_HOST = "10.101.60.27" # IP вашего SEE/SMC DEFAULT_PORT = 6184 # порт SEE: 6184, SMC: 6181 ``` ### 1.2 Настрой TEXT_RULES для своих моделей `TEXT_RULES` — список `(keyword, text)`. **Первое совпадение побеждает**, поэтому специфичные правила должны идти раньше общих. **Принцип**: для каждой модели нужен текст, который гарантированно вернёт непустой ответ. Пример для SEE (`test_see_models.py`): ```python TEXT_RULES = [ # специфичные — раньше ("fio", "Иванов Иван Иванович"), # модель ФИО → имя ("date", "на завтра"), # модель даты → фраза с датой ("phone", "8 913 456 78 90"), # модель телефона → номер ("number", "пятьсот двадцать три"), # модель числа → число прописью ("weekday", "в пятницу"), # модель дня недели → день ("mobilizac", "оператор"), # конкретная модель ("trud", "зарегистрироваться в цзн"), # конкретная модель # общие — позже ("mfc", "записаться в мфц"), # fallback для всего MFC ("test", "test message"), # fallback для тестовых ] ``` **Как подобрать правила для нового проекта:** 1. Посмотри список моделей: `curl http://HOST:PORT/SEE_OR_SMC/models` 2. Для каждой модели подбери текст, который точно вернёт ответ 3. Сгруппируй по ключевым словам (одно правило может покрыть несколько моделей) 4. Расположи специфичные правила **выше**, общие — **ниже** Пример для SMC (`test_smc_models.py`): ```python TEXT_RULES = [ ("филиал", "родники"), # филиалы → название филиала ("offices", "родники"), ("мфц", "записаться в мфц"), # модели МФЦ ("mfc", "записаться в мфц"), ("ekc", "записаться в екц"), # модели ЕКЦ ("екц", "записаться в екц"), ("minsoc", "какие выплаты"), # минсоц ("техническая", "подключите оператора"), # техподдержка ("тест", "тестовый запрос"), ] ``` **TEXT_RULES покрывает только типовые модели** (fio, date, phone, mfc, zags...) — те, для которых по ключевому слову можно угадать подходящий текст. Одно правило покрывает десятки похожих моделей. Для узкоспецифичных моделей проекта, где ключевое слово не даёт нужной точности, используй `text` в asserts (см. [Шаг 3.6](#36-когда-text_rules-не-хватает)). ## Шаг 2: Первый запуск без asserts Запусти тест с `-v` (verbose), чтобы увидеть каждую модель и её ответ: ```bash python3 test_see_models.py --host 10.101.60.25 --port 6184 -v ``` Вывод будет примерно таким: ``` ── PRETRAINED ────────────────────────────────────────────── ✓ date → 2026-06-24 ✓ number → 523 ✓ fio → Ф:"Иванов" И:"Иван" О:"Иванович" ─── 6 passed, 0 empty ── CUSTOM ────────────────────────────────────────────── ✓ zags → отдел_центрального_о_новосибирска ✓ weekday → пятница ✗ fio2 → empty ✗ Начало диалога → empty ─── 200 passed, 9 empty ``` Из вывода ты видишь: - Какие модели работают (`✓ модель → результат`) - Какие всегда пустые (`✗ модель → empty`) - Какие есть хеш-варианты и бэкапы (у них тот же префикс, но с `~hash` или `-backup`) ## Шаг 3: Создание asserts ### 3.1 Правило `min_found` | Тип модели | `min_found` | Почему | |------------|-------------|--------| | Базовая (без суффикса) | `1` | Должна всегда отвечать | | Хеш-вариант (`~4f2d37c9...`) | `0` | Копия базы для тестирования, может не работать | | `-backup` | `0` | Бэкап, может не работать | | `-planned` | `0` | Запланированная, ещё не готова | | `-new` | `0` | Экспериментальная, может не работать | ### 3.2 Формат для SEE (entity-модели) SEE возвращает сущности с полями `text`, `calculated`, `confidence`: ```json { "asserts": [ { "model": "fio", "text": "Иванов Иван Иванович", "checks": { "min_found": 1, "text_contains": "иван", "calculated_contains": "Ф:", "confidence_min": 0.3 } } ] } ``` | Поле checks | Что проверяет | Пример | |-------------|--------------|--------| | `min_found` | Минимум сущностей в ответе | `1` | | `text_contains` | Подстрока в `text` первой сущности | `"иван"` | | `calculated_contains` | Подстрока в `calculated` первой сущности | `"Ф:"` | | `confidence_min` | Минимальный confidence первой сущности | `0.3` | **Где брать значения**: из вывода `-v`. Например: ``` ✓ fio → Ф:"Иванов" И:"Иван" О:"Иванович" ↑ берём отсюда подстроки для text_contains и calculated_contains ``` ### 3.3 Формат для SMC (classifier-модели) SMC возвращает классы с полями `class` и `confidence`: ```json { "asserts": [ { "model": "МФЦ НСК", "text": "записаться в мфц", "checks": { "min_found": 1, "text_contains": "Запись", "confidence_min": 0.5 } } ] } ``` | Поле checks | Что проверяет | Пример | |-------------|--------------|--------| | `min_found` | Минимум классов в ответе | `1` | | `text_contains` | Подстрока в `class` первого класса | `"Запись"` | | `confidence_min` | Минимальный confidence первого класса | `0.5` | **Где брать значения**: из вывода `-v`: ``` ✓ МФЦ НСК → Предварительная запись (1.000) └── text_contains: "Запись" └── confidence_min: 0.5 ``` ### 3.4 Полный assert для каждой строки `-v` Проходишь вывод `-v` и для каждой строки создаёшь запись: | Вывод `-v` | Запись в asserts | |-------------|-----------------| | `✓ fio → Ф:"Иванов" И:"Иван" О:"Иванович"` | `{"model": "fio", "text": "Иванов Иван Иванович", "checks": {"min_found": 1, "calculated_contains": "Ф:", "confidence_min": 0.3}}` | | `✓ weekday → пятница` | `{"model": "weekday", "checks": {"min_found": 1, "text_contains": "пятница"}}` | | `✓ zags~ffb403 → ...` (хеш) | `{"model": "zags~ffb403", "checks": {"min_found": 0}}` | | `✗ fio2 → empty` | `{"model": "fio2", "checks": {"min_found": 0}}` | ### 3.5 Поле `text` в assert Поле `text` в assert — опционально. Если оно есть, оно **переопределяет** `pick_text()`. **Когда указывать `text`:** - Модель работает с конкретным текстом, отличным от того что подбирает `pick_text` - Например: модель `service_mfc` возвращает услуги, но `pick_text` даёт "записаться в мфц" — лучше явно указать "хочу получить водительское удостоверение" **Когда НЕ указывать `text`:** - `pick_text()` уже подбирает правильный текст - Для хеш-вариантов и бэкапов — используй тот же текст что у базовой модели (укажи явно, чтобы не зависеть от порядка `pick_text`) **Важно**: если указываешь `text` для хеш-варианта или бэкапа, используй тот же текст, что для базовой модели. Разные тексты могут не работать для копий. Пример: ```json // Базовая модель {"model": "zags", "text": "загс центрального района", "checks": {"min_found": 1}} // Хеш-вариант — тот же текст! {"model": "zags~ffb403fff9f5802d53aac8a5c844af26", "text": "загс центрального района", "checks": {"min_found": 0}} ``` ### 3.6 Когда TEXT_RULES не хватает **Философия тестов — два уровня проверки:** | Уровень | Инструмент | Вопрос | |---------|-----------|--------| | 1 — санитарный | TEXT_RULES | Модель живая? Отвечает хоть что-то? | | 2 — проверочный | asserts | Модель отвечает **правильно**? | TEXT_RULES даёт быстрый ответ «живая/мёртвая» для типовых моделей. Но есть модели, которым нужен специфичный текст, недоступный для угадывания по ключевому слову. **Пример:** модель `Конкретика по УК~ffb403...` — TEXT_RULES по ключевому слову `"конкретик"` возвращает `"конкретный вопрос"`. Текст слишком размытый, модель не находит сущностей и возвращает пустой ответ. **Решение:** добавить `text` в assert с точной формулировкой: ```json { "model": "Конкретика по УК~ffb403fff9f5802d53aac8a5c844af26", "text": "Найти свою УК", "checks": {"min_found": 0} } ``` **Это не баг, а дизайн:** - TEXT_RULES универсален и переносим между проектами (скопировал тесты — работает) - asserts пишется под конкретный проект и покрывает специфичные модели - Без asserts — быстрый санитарный прогон, с asserts — полная валидация ## Шаг 4: Запуск с asserts ```bash python3 test_see_models.py --host 10.101.60.25 --port 6184 --asserts see_asserts.json -v ``` Теперь для каждой модели: - `✓` — ответ непустой, проверки asserts пройдены - `✗ → empty` — ответ пустой (но `min_found: 0`, тест проходит) - `✗ → assert fail` — ответ есть, но не прошёл проверки (тест падает) ## Шаг 5: Исправление проблем ### Модель возвращает пустой ответ, а должна работать 1. Проверь текст в assert — попробуй другой текст напрямую через curl: ```bash curl "http://HOST:PORT/see/entities/MODEL?text=ТЕКСТ&similarity=70" ``` 2. Если другой текст работает — обнови `text` в assert 3. Если никакой текст не работает — модель действительно пустая, оставь `min_found: 0` ### Модель проходит без asserts, но падает с asserts Проверь значения в `checks`: - `text_contains` — проверь, что подстрока действительно есть в ответе - `calculated_contains` — только для SEE entity-моделей - `confidence_min` — возможно завышен, поставь 0.1 для ненадёжных моделей ### Хеш-вариант работает без asserts, но с asserts — пустой Это значит assert для хеш-варианта переопределяет `text` на неправильный. Убедись, что `text` совпадает с базовой моделью. ## Полный пример: с нуля до готового набора Допустим, у тебя новый проект с SEE на `10.10.10.10:6184`. ```bash # 1. Первый запуск — смотрим что есть python3 test_see_models.py --host 10.10.10.10 --port 6184 -v # 2. Вывод показывает 50 моделей. Создаём see_asserts.json: # - Для каждой базовой модели → min_found: 1 + проверки из ответа # - Для хеш-вариантов и бэкапов → min_found: 0 # - Для пустых → min_found: 0 # 3. Запускаем с asserts python3 test_see_models.py --host 10.10.10.10 --port 6184 --asserts see_asserts.json # 4. Если есть ✗ assert fail — правим checks в asserts # 5. Повторяем 3-4 пока все не пройдут ``` ## Структура репозитория после настройки ``` мой-проект/ ├── test_see_models.py # тест SEE (скопирован, изменён DEFAULT_HOST/PORT и TEXT_RULES) ├── test_smc_models.py # тест SMC (опционально) ├── see_asserts.json # проверки для SEE ├── smc_asserts.json # проверки для SMC (опционально) └── README.md # краткое описание ``` ## Важные замечания 1. **Порядок правил в TEXT_RULES важен** — специфичные ключевые слова (`"fio"`, `"phone"`) должны быть выше общих (`"mfc"`, `"test"`). 2. **Не указывай `text` в assert без необходимости** — если `pick_text()` уже подбирает правильный текст, не переопределяй. Каждый лишний `text` в assert — потенциальный источник расхождений. 3. **Хеш-варианты и бэкапы** — всегда `min_found: 0`. Они могут не работать, и это нормально. 4. **confidence_min для ненадёжных моделей** — ставь `0.1`, не `0.5`. Даже минимальный confidence лучше чем пустой ответ. 5. **`_find_assert` ищет сначала точное совпадение** — модель `ekc_smc-backup` не перепутается с `ekc_smc`. Порядок записей в asserts не критичен. 6. **Проверяй на всех серверах** — если у тебя несколько инсталляций (dev/stage/prod), гоняй тесты на каждой: ```bash for host in 10.101.60.25 10.101.60.26 10.101.60.27; do python3 test_see_models.py --host $host --port 6184 --asserts see_asserts.json done ```