Документирован 3-й аудит, трёхсостояночный lock_check, созданные тесты и план эмулятора API

This commit is contained in:
2026-07-31 18:28:35 +04:00
parent 09d663d6d9
commit acf6a575b4
+87
View File
@@ -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` для переключения эндпоинта