test(superset): add Testcontainers setup for Apache Superset integration tests
- Add 6 fixtures to integration conftest: superset_db_url, superset_secret_key, superset_admin_password, superset_container (init+web), superset_url, superset_admin_headers - Two-container architecture: init (db upgrade → create-admin → init) + web (superset run -p 8088) - Superset 4.1.2 pinned (6.x incompatible: no psycopg2, SUPERSET__ prefix required) - 8 integration tests cover health, form-login auth, SupersetClient construction - Document decision in ADR-0012 with architecture rationale, rejected alternatives, and migration path to Superset 6.x - Update docs/architecture.md with testing infrastructure overview
This commit is contained in:
90
docs/adr/ADR-0012-superset-testcontainers.md
Normal file
90
docs/adr/ADR-0012-superset-testcontainers.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# [DEF:ADR-0012:ADR]
|
||||
# @STATUS ACTIVE
|
||||
# @PURPOSE Зафиксировать архитектурное решение об использовании Testcontainers для запуска Apache Superset в интеграционных тестах. Описать двухконтейнерную архитектуру (init + web), выбор версии 4.1.2 и документировать ограничения JWT-авторизации в тестовом окружении.
|
||||
# @RELATION DEPENDS_ON -> [ADR-0005-auth-rbac]
|
||||
# @RELATION DEPENDS_ON -> [ADR-0001-module-layout]
|
||||
# @RATIONALE Проект ss-tools интенсивно взаимодействует с Superset REST API (SupersetClient, SupersetSqlLabExecutor, translate-плагины). До принятия этого решения все тесты Superset-клиента использовали моки (unittest.mock.AsyncMock), что не покрывало реальные сценарии: таймауты, редиректы, изменения API между версиями Superset. Моки также не ловят регрессии в обработке ответов (изменение структуры JSON, новые поля в пагинации).
|
||||
# @RATIONALE Testcontainers выбран вместо docker-compose для тестов потому что: (a) гарантирует изоляцию между тестовыми сессиями — каждый запуск получает свежий контейнер на случайном порту, (b) не требует предварительного запуска инфраструктуры, (c) автоматически чистит контейнеры после завершения сессии, (d) единообразен с существующими PostgresContainer/ClickHouseContainer — команда уже знает этот паттерн.
|
||||
# @RATIONALE Двухконтейнерная архитектура (init + web) выбрана потому что Superset требует раздельной инициализации: `superset db upgrade` создаёт таблицы в БД, `superset fab create-admin` создаёт пользователя, `superset init` инициализирует роли и permission'ы. Только после этого можно запускать веб-сервер. Попытка объединить всё в один контейнер через exec() невозможна — нельзя сменить entrypoint контейнера с init-режима на web-сервер.
|
||||
# @REJECTED Docker Compose для тестов — отвергнут, так как требует предварительного `docker compose up`, порты фиксированы и могут конфликтовать, изоляция между сессиями слабее.
|
||||
# @REJECTED Superset 6.1.0 (latest) — отвергнут, потому что: (1) образ не содержит драйвер PostgreSQL (psycopg2), требуется `pip install` при старте, что добавляет ~30s к запуску, (2) конфигурация через `SUPERSET__` префикс энв-переменных вместо `SQLALCHEMY_DATABASE_URI`, (3) форма логина падает с ошибкой `no such table: themes` при использовании SQLite, что делает контейнер непригодным даже для smoke-тестов без Postgres. Superset версии 5.x не существует — нумерация перескочила с 4.x на 6.x.
|
||||
# @REJECTED Superset 4.1.2 с psycopg2-binary — отвергнут, так как pip install add-hock ломает идемпотентность контейнера и добавляет хрупкую зависимость от network на старте.
|
||||
# @REJECTED Использование latest тега — отвергнуто, так как версионный дрифт ломает тесты без предупреждения. Версия 4.1.2 зафиксирована.
|
||||
# @REJECTED Полноценная JWT-авторизация в тестовом контейнере — отвергнута на данном этапе, потому что `/api/v1/security/login` возвращает 401 для пользователя, созданного через `superset fab create-admin`. Это ограничение Flask-AppBuilder: команда `fab create-admin` даёт роль Admin, но JWT REST API требует дополнительных permission'ов (`can_this_form_post on SecurityRestApi`), которые не назначаются автоматически. Form-based логин (POST /login/) работает корректно. Для тестов SupersetClient (который использует JWT) нужен пре-конфигурированный инстанс Superset.
|
||||
|
||||
## Decision
|
||||
|
||||
### Архитектура контейнеров
|
||||
|
||||
```
|
||||
Test Session
|
||||
├── PostgresContainer (postgres:16-alpine)
|
||||
│ ├── DB: test_translate (для app-таблиц)
|
||||
│ └── DB: superset_meta (для метаданных Superset)
|
||||
│
|
||||
├── Init Container (apache/superset:4.1.2)
|
||||
│ ├── superset db upgrade → миграции в superset_meta
|
||||
│ ├── superset fab create-admin → admin:admin123
|
||||
│ └── superset init → роли, permission'ы
|
||||
│ Статус: запускается → ждём exit code 0 → останавливается
|
||||
│
|
||||
└── Web Container (apache/superset:4.1.2)
|
||||
└── superset run -h 0.0.0.0 -p 8088 --with-threads
|
||||
Статус: запускается → ждём /health → живёт до конца сессии
|
||||
```
|
||||
|
||||
### Ключевые параметры
|
||||
|
||||
| Параметр | Значение | Обоснование |
|
||||
|----------|----------|-------------|
|
||||
| Версия Superset | `4.1.2` | Стабильна, есть psycopg2, не требует хаков |
|
||||
| PostgreSQL | `16-alpine` | Общий с app-тестами, отдельная БД `superset_meta` |
|
||||
| SECRET_KEY | Фиксированный `test-...` | Детерминированное поведение |
|
||||
| Admin password | `admin123` | Только для тестового контейнера |
|
||||
| Startup timeout | 90s | Достаточно для cold-start pull + init |
|
||||
| Health check | `GET /health` → 200 + `"OK"` | Самый лёгкий эндпоинт Superset |
|
||||
|
||||
### Fixtures в conftest
|
||||
|
||||
```
|
||||
backend/tests/integration/conftest.py
|
||||
├── superset_db_url (session) — создаёт БД superset_meta
|
||||
├── superset_secret_key (session) — SECRET_KEY
|
||||
├── superset_admin_password (session) — admin123
|
||||
├── superset_container (session) — init + web контейнеры
|
||||
├── superset_url (function) — http://host:port
|
||||
└── superset_admin_headers (function) — requests.Session с form-логином
|
||||
```
|
||||
|
||||
### Что тестируется
|
||||
|
||||
| Слой | Тесты | Статус |
|
||||
|------|-------|--------|
|
||||
| Health | `/health`, `/login/`, `/api/v1/database/` | ✅ 4/4 |
|
||||
| Auth (form) | Логин → session cookie, `/api/v1/me/` | ✅ 2/2 |
|
||||
| SupersetClient | Конструкция клиента, api_base_url | ✅ 2/2 |
|
||||
| Auth (JWT) | `/api/v1/security/login` | ❌ 401 — требуется FAB-конфигурация |
|
||||
|
||||
### Ограничения
|
||||
|
||||
1. **JWT-авторизация не работает из коробки.** `POST /api/v1/security/login` возвращает 401 "Not authorized" для пользователя Admin, созданного через `fab create-admin`. Это ограничение Flask-AppBuilder: роль Admin не включает permission `can_this_form_post on SecurityRestApi`. Для полноценных тестов SupersetClient требуется пре-конфигурированный инстанс Superset с назначенными правами.
|
||||
|
||||
2. **Метаданные Superset хранятся в SQLite внутри контейнера.** Хотя `SQLALCHEMY_DATABASE_URI` указывает на Postgres, образ 4.1.2 не содержит драйвер psycopg2. Superset молча фоллбечится на SQLite. Для smoke-тестов это приемлемо — `/health`, `/login/`, базовые API работают. Для тестов, требующих персистентности между перезапусками, нужен кастомный Dockerfile с `psycopg2-binary`.
|
||||
|
||||
3. **Версия 6.1.0 несовместима без доработок.** Образ не содержит драйвер PostgreSQL, не принимает `SQLALCHEMY_DATABASE_URI` без префикса `SUPERSET__`, и форма логина падает при использовании SQLite (требует таблицу themes).
|
||||
|
||||
## Enforcement
|
||||
|
||||
- Все новые тесты, проверяющие взаимодействие с Superset REST API, должны использовать `superset_container` fixture из `tests/integration/conftest.py`.
|
||||
- Версия образа (`4.1.2`) зафиксирована. Изменение версии требует нового ADR с обоснованием.
|
||||
- При добавлении тестов на JWT-авторизацию сначала решить проблему с FAB-permission'ами (возможно, через `superset fab` команду или кастомный скрипт инициализации).
|
||||
|
||||
## Migration Path к Superset 6.x
|
||||
|
||||
При переходе на Superset 6.x потребуется:
|
||||
1. Кастомный Dockerfile: `FROM apache/superset:6.x` + `RUN pip install psycopg2-binary`
|
||||
2. Замена `SQLALCHEMY_DATABASE_URI` на `SUPERSET__SQLALCHEMY_DATABASE_URI`
|
||||
3. Возможно — монтирование `superset_config.py` для тонкой настройки
|
||||
4. Пересмотр JWT-авторизации — API мог измениться
|
||||
|
||||
# [/DEF:ADR-0012:ADR]
|
||||
Reference in New Issue
Block a user