Files
autotest/TASKS/mock-architecture-prompt.md
T

8.6 KiB
Raw Blame History

Задача: архитектура эмулятора Nubes API для интеграционных тестов

Контекст

app-autotest — Flask-приложение для тестирования сервисов облачной платформы Nubes. Позволяет вручную и через сценарии запускать операции (create/modify/delete/suspend/resume/redeploy) над сервисами через REST API.

Проблема: реальные тесты идут 2-30 минут на операцию, жрут ресурсы облака, недетерминированы. Нужен эмулятор Nubes API чтобы гонять интеграционные тесты мгновенно и бесплатно.

Цель: спроектировать архитектуру эмулятора, который притворяется Nubes API для ЛЮБОГО сервиса, описанного в YAML-конфиге. Не хардкод под каждый сервис, а data-driven подход.

Что почитать Опусу (обязательно)

  1. DOCS/ARCHITECTURE.md — полная архитектура приложения (раздел 8 про безопасность пропустить)
  2. app-autotest/site/api/http_client.py — как приложение общается с Nubes API (GET/POST, Location, автостенд)
  3. app-autotest/site/operations/terraform.py — логика параметров: cfsParams, normalize_value, resolve_ref_svc, send_params_terraform
  4. app-autotest/site/operations/get_params.py — как достаются state.params и state.out из инстанса
  5. app-autotest/site/operations/executor.py — единый шлюз запуска операций (это то что будет вызывать эмулятор)
  6. app-autotest/site/operations/poll.py — поллинг до dtFinish (эмулятор должен отдавать dtFinish)

НЕ читать: HISTORY, DOCS/* кроме ARCHITECTURE.md, JS-файлы, routes, db. Только backend-ядро.

Что должен делать эмулятор

Принимать те же HTTP-запросы что и реальный Nubes API и отдавать валидные ответы. Ниже — обязательные эндпоинты.

1. Сервисы

GET /services → {results: [{svcId, svc, svcExtendedName, operations}]}
GET /services/{id} → {svc: {svc, svcShort, operations: [{svcOperationId, operation, isCreate}]}}

2. Инстансы

POST /instances → 201 + Location: ./uuid
  body: {serviceId, displayName, descr}
  ответ: {instanceUid}

GET /instances?pageSize=N&page=P → {results: [{instanceUid, displayName, serviceId, svc, explainedStatus, ...}]}
  пагинация: pageSize=200 макс, остановка по len(batch) < pageSize

GET /instances/{uid} → {instance: {instanceUid, displayName, serviceId, svc, explainedStatus, state: {params: {...}, out: {...}}}}
  state.params — ТЕКУЩИЕ значения параметров (ключ = код, напр. "durationMs": "0")
  state.out — доп. данные: {users: {pgadmin: {}}, databases: {mydb: {}}}

3. Операции

POST /instanceOperations → {instanceOperationUid}
  body: {instanceUid, svcOperationId, operation}

POST /instanceOperationCfsParams → {}
  body: {instanceOperationUid, svcOperationCfsParamId, paramValue}

GET /instanceOperations/{uid}?fields=dtFinish,isSuccessful,errorLog,duration,stages,svc
  → {instanceOperation: {dtFinish, isSuccessful, errorLog, duration, stages, svc}}
  dtFinish — ключевое: если есть → операция завершена
  stages: [{stage, dtStart, dtFinish, isSuccessful, duration}, ...]

POST /instanceOperations/{uid}/run → {}
  После этого операция считается запущенной, через N секунд появляется dtFinish

GET /instanceOperations/default/{id} → {svcOperation: {cfsParams: [{svcOperationCfsParamId, svcOperationCfsParam, dataType, isRequired, defaultValue, valueList, refSvcId, dataDescriptor}]}}

GET /instanceOperations/{uid}/validate-cfs → {} (200 OK, пустое тело = успех)

Что эмулятор НЕ должен делать

  • Реально выполнять операции (не Terraform, не Ansible, не K8s)
  • Проверять валидность параметров (всегда validate-cfs = OK)
  • Хранить данные в БД (всё в памяти процесса)
  • Работать с реальными токенами (принимать любой)

Ключевые вопросы для проектирования

Q1. Конфигурация сервисов

Формат YAML для описания сервиса должен покрывать:

  • Базовые поля: svcId, svc (имя), svcExtendedName
  • Операции: create, modify, delete, suspend, resume, redeploy (у каждого свой svcOperationId)
  • cfsParams: для КАЖДОГО параметра — id, код, тип, default, valueList, isRequired, refSvcId, dataDescriptor
  • stateParams: значения по умолчанию после create
  • stateOut: структура для users/databases (PG) и подобного

Вопрос: как компактно описать cfsParams для сервисов с 20+ параметрами? Может ли эмулятор сам сгенерировать разумные defaults по типам?

Q2. Состояние инстансов

Эмулятор должен отслеживать:

  • Какие инстансы созданы (instanceUid → {serviceId, displayName, params, status})
  • Статус: creating → running (после create), suspending → suspended, modifying → running, deleting → deleted
  • После delete — инстанс исчезает из GET /instances

Вопрос: как моделировать explainedStatus? Простая машина состояний или хардкод?

Q3. Поллинг и время

После POST /run операция должна «выполняться» N секунд, потом появляется dtFinish. N можно настраивать (для тестов — 0.1с, для демо — 2с).

Вопрос: как эмулятор понимает что операция «завершена»? Таймер? Или сразу при запросе проверять время?

Q4. Интеграция с app-autotest

Приложение сейчас использует NUBES_API_ENDPOINT из env. При NUBES_MOCK=1 подставляется http://localhost:5001/api/v1/svc.

Вопрос: нужно ли чтобы эмулятор запускался как отдельный процесс (порт 5001), или можно встроить как Flask blueprint в то же приложение?

Q5. Тестовые сценарии

После создания эмулятора — интеграционные тесты. Минимальный набор:

  1. create Болванку → проверить instanceUid
  2. modify параметр → проверить изменение state.params
  3. delete → проверить исчезновение из GET /instances
  4. Сценарий: create→modify→delete через run_scenario
  5. Для PG: create → проверить state.out.users и state.out.databases

Вопрос: должны ли тесты использовать реальный app_client (Flask test client) или напрямую дёргать эмулятор?

Ожидаемый ответ

Структурированный план:

  1. Архитектура эмулятора (файлы, классы, модули)
  2. Формат services.yaml с примерами для Болванки и PostgreSQL
  3. Машина состояний инстансов
  4. Механизм поллинга (как эмулировать dtFinish)
  5. Точки интеграции с app-autotest
  6. План тестов
  7. Порядок реализации (MVP → полная версия)

Ограничения

  • Только Python (Flask), никаких новых зависимостей кроме PyYAML
  • Без БД, всё в памяти
  • Без многопоточности (один процесс, один пользователь)
  • Код должен быть ПРОСТЫМ — эмулятор не должен быть сложнее тестируемого приложения