diff --git a/HISTORY/2026-07-31-session.md b/HISTORY/2026-07-31-session.md index 843fa9e..7d21c53 100644 --- a/HISTORY/2026-07-31-session.md +++ b/HISTORY/2026-07-31-session.md @@ -241,3 +241,90 @@ CREATE UNIQUE INDEX idx_one_running | escName `"` | JS | ❌ Неполный → ✅ добавлен `"` | | instances.js onclick | JS | ⚠️ Низкий риск, UUID безопасен | | `lock_check` fallback | Python | ✅ `True`→`False` при ошибке БД | + +--- + +## Третий аудит Codex + трёхсостояночный lock_check (v1.2.22-v1.2.23) + +Codex проверил v1.2.21 и нашёл 3 проблемы. Две исправлены, одну — обсудили и +пришли к правильному решению: + +### Исправлено + +1. **UniqueViolation → 500 (не 409)** — `api_scenario_run.py:78`. + INSERT ловился общим `except` → 500. Теперь: `e.pgcode == '23505'` → 409. (v1.2.22) + +2. **escName без `&`** — `scenario-list.js:128`. + `'` декодируется браузером в `'` до JS → разрыв строки. + Добавлен `.replace(/&/g,'&')` ПЕРВЫМ шагом. (v1.2.22) + +### Обсуждено и исправлено правильно + +3. **`lock_check` fallback — трёхсостояночный подход** (v1.2.23): + + Исходно Codex предложил `True`→`False` при no-db. AI слепо сделал. + Пользователь возразил: `False` ломает запуск при деградации БД. + + Codex согласился и предложил трёхсостояночный возврат: + - `True` — можно запускать (нет RUNNING) + - `False` — нельзя (есть RUNNING) → 409 + - `None` — БД недоступна → 503 + + `api_scenario_run.py` обрабатывает `None` как 503 DB unavailable. + +### Созданы тесты (Codex, только сохранены, не запущены) + +`tests/` — 4 файла, покрывают критические фиксы: + +| Файл | Что тестирует | +|------|---------------| +| `conftest.py` | Flask test client + sys.path | +| `test_api_scenario_run.py` | 503 при `lock_check=None`, 409 при `False`, 409 при `UniqueViolation(pgcode=23505)` | +| `test_db_scenario_defs.py` | `lock_check → None` при no-db и ошибке БД | +| `test_static_regressions.py` | Статическая проверка `idx_one_running` в init_db.py и цепочки `escName` в scenario-list.js | + +--- + +## План: эмулятор Nubes API для интеграционных тестов + +### Зачем +Реальные тесты медленные (поллинг до 30 минут) и жрут ресурсы облака. +Эмулятор даст: быстрые тесты (< 1 сек), детерминизм, краевые случаи, CI/CD. + +### Архитектура +``` +site/ +├── app.py # основное приложение +└── mock/ + └── nubes_mock.py # эмулятор API Nubes (Flask, порт 5001) +``` + +В `app.py` — переключение по `NUBES_MOCK=1` → эндпоинт `http://localhost:5001/api/v1/svc`. + +### Эндпоинты для эмуляции (Болванка, сервис 1) + +| Метод | Путь | Ответ | +|-------|------|-------| +| POST | `/instances` | 201 + `{instanceUid}` | +| GET | `/instances?pageSize=200` | `{results: [...]}` | +| GET | `/instances/{uid}` | `{instance: {state: {params: {...}}}}` | +| GET | `/services` | `{results: [{svcId: 1, svc: "dummy"}]}` | +| GET | `/services/1` | `{svc: {operations: [...]}}` | +| POST | `/instanceOperations` | `{instanceOperationUid}` | +| POST | `/instanceOperationCfsParams` | `{}` | +| POST | `/instanceOperations/{uid}/run` | `{}` | +| GET | `/instanceOperations/{uid}?fields=...` | `{instanceOperation: {dtFinish, isSuccessful, ...}}` | +| GET | `/instanceOperations/default/{id}` | `{svcOperation: {cfsParams: [...]}}` | +| GET | `/instanceOperations/{uid}/validate-cfs` | `{}` (200 OK) | + +### Что сложнее + +- `cfsParams` — у каждого сервиса своя структура, придётся хардкодить под Болванку +- `refSvcId` — ссылки на другие сервисы (External IP и т.д.) — отложить +- `stages` — этапы выполнения (plan→apply→...) — отдавать фейковые + +### Порядок создания + +1. `mock/nubes_mock.py` — Flask-заглушка (~150 строк) +2. Интеграционный тест: сценарий `create→modify→delete` через `app_client` +3. `NUBES_MOCK=1` в `app.py` для переключения эндпоинта