# История разработки app-autotest ## v1.0.89 (27.07.2026) — Sonnet Round 3: практические вопросы ### 1. PostgreSQL connection string - `os.getenv("DATABASE_URL")` — никакого service discovery - Инжектится через `jsonEnv` параметры pythonk8s-сервиса (как NUBES_API_TOKEN) - Порядок: создать PostgreSQL-инстанс в Nubes → взять host/port/user/pass из state_out → собрать DSN → jsonEnv → деплой ### 2. Миграции при деплое - `init_db()` на уровне модуля в app.py, до register_blueprint - `CREATE TABLE/INDEX IF NOT EXISTS` — идемпотентно, каждый воркер выполнит независимо - PostgreSQL обрабатывает конкурентный DDL корректно - Никаких ручных шагов при деплое - `DROP COLUMN` — не идемпотентно, осторожно ### 3. Multi-service UI (map-fixed) - `dataDescriptor` уже в API-ответе — инфраструктура готова - `map` (свободный): textarea + JSON-валидация (как сейчас) - `map-fixed`: таблица подполей с отдельными input/select из dataDescriptor - Бэкенд не меняется — paramValue всегда строка - Изменения только во фронте: showParams() + сбор подполей в JSON перед отправкой ### 4. Тесты - pytest для чистых функций без моков: `_find_uid`, `_uid_from_location`, `_with_prefix`, слияние params, tracker - Структура: `tests/conftest.py`, `test_utils.py`, `test_tracker.py`, `test_get_params.py` - Роуты с Flask test client + mock HttpClient — отложить (высокая стоимость мокирования) ## v1.0.89 (27.07.2026) — Sonnet Round 2: PostgreSQL, auth.py, детальный план ### Результаты анализа Sonnet дал конкретные ответы на все 7 вопросов Round 2: **1. PostgreSQL схема:** - Таблица `runs` с JSONB-полями `params` и `stages` - 3 индекса: `(client_id, stand, created_at DESC)`, `(instance_uid)`, `(created_at)` - Stages в JSONB (не отдельная таблица), instances не нужны (API — source of truth) - Чувствительные params маскировать при сохранении **2. Connection pool:** - `psycopg2.pool.ThreadedConnectionPool` — минимум зависимостей - Lazy-init в `db/pool.py` — безопасно для fork-модели gunicorn - `flask.g` + `@app.teardown_appcontext` для возврата соединений **3. Миграции:** - `db/init_db.py` с `IF NOT EXISTS` — идемпотентно, авто-применение при старте - Alembic — когда 3+ таблицы или нужен rollback (сейчас не нужно) **4. Трекер vs PostgreSQL:** - **НЕ заменять** трекер на БД — это разные слои - Трекер = кеш (секунды), PostgreSQL = история (недели) - Сетевое обращение к БД на критическом пути CREATE недопустимо **5. auth.py:** - Явные функции вместо `before_request` — проще, нет скрытых зависимостей - Одна реализация `get_client_id()` вместо двух копий - Кеширование `token → endpoint` (опционально, TTL 5 мин) **6. Динамические сервисы:** - `/api/services` из API → фильтр по `enabled: true` из config.yaml - UI: заменить хардкод `SVC_ID=1` на динамическую загрузку в `selectService()` **7. JS в файл:** - Классический скрипт, один файл `app.js` (300 строк — не нужно ES modules) - `window.APP = {...}` через `tojson` фильтр Jinja2 для передачи переменных ### Итоговый порядок реализации (9 шагов) 1. `api/auth.py` — вынести get_token/get_client/get_client_id/get_stand 2. Заменить дублирующиеся функции в main.py и api_test.py 3. Cookie `httponly=True, samesite='Strict'` 4. `_op_results` TTL/очистка 5. JS вынести в `/static/app.js` + `window.APP` 6. Динамические сервисы из `/api/services` 7. `db/pool.py` + `db/init_db.py` (PostgreSQL) 8. Запись истории в runs при завершении операции 9. `/api/history` эндпоинт + UI ## v1.0.89 (27.07.2026) — Sonnet Round 1: архитектурный аудит - Cloud-first подход с tracker-fallback - HTML-экранирование в JS **🔴 Критические находки:** 1. `_op_results` dict растёт бесконечно — утечка памяти, нужен TTL/очистка 2. Cookie токена без `httponly` и `samesite` 3. `/api/log` без проверки авторизации **🟡 Дублирование:** 1. `_client_id()` — идентичный код в main.py и api_test.py 2. `_client()` — разное поведение в main.py и api_test.py 3. Инлайн `