# План тестирования стенда (Smoke + Full)

Документ описывает два контура проверки системы:
- быстрый `smoke` для регрессий после изменений;
- полный `full` для финальной валидации end-to-end.

## Цель

Проверить, что стек `Kafka + ClickHouse + Airflow + Superset + Prometheus/Grafana`:
- стабильно поднимается;
- загружает данные по пути `startup-history/backfill -> Kafka -> STG -> ODS/DDS/DM`;
- корректно обрабатывает «грязные» записи (ошибки фиксируются в ODS, пайплайн не падает);
- отдает метрики и дашборды мониторинга.

## Общие принципы

- Для smoke и CI явно задаём служебный профиль `ci`.
- Полный прогон выполняем отдельно через `PROFILE=daily-wave`.
- Основной ручной путь запуска — через Airflow DAG `world_init` с пустой формой.
- Консольный чистый прогон `make generated-history-analytics` остаётся коротким
  повторяемым сценарием для smoke и CI.
- Критерий успеха: не только `Success` DAG, но и проверки данных/ошибок/мониторинга.

---

## Контур A: Smoke (быстрый)

Ожидаемая длительность: ~10-20 минут.

### A.1 Подготовка окружения

```bash
# Чистый быстрый прогон: backfill -> Kafka -> STG -> ODS -> DDS -> DM -> Superset
PROFILE=ci make generated-history-analytics

# Поднять остальные UI-сервисы после чистого прогона
make up

# Проверка контейнеров
docker compose ps
```

Ожидаем:
- `airflow-init` в `Exited (0)`;
- остальные сервисы в `Up` (включая `superset`, `prometheus`, `grafana`, `kafka-exporter`, `statsd-exporter`).

### A.2 Проверка стартовой истории в STG

Проверки:

```bash
# STG не пустой

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "
SELECT 'browser_raw' AS table, count() AS cnt FROM stg.browser_raw
UNION ALL
SELECT 'location_raw', count() FROM stg.location_raw
UNION ALL
SELECT 'device_raw', count() FROM stg.device_raw
UNION ALL
SELECT 'geo_raw', count() FROM stg.geo_raw
"
```

Ожидаем: во всех 4 таблицах `cnt > 0`.

### A.3 Проверки слоев

Проверки:

```bash
# ODS / DDS / DM
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "
SELECT 'ods.browser_event' AS table, count() AS cnt FROM ods.browser_event
UNION ALL
SELECT 'dds.event', count() FROM dds.event
UNION ALL
SELECT 'dds.click', count() FROM dds.click
UNION ALL
SELECT 'dm.dq_summary', count() FROM dm.dq_summary
"

# Базовая целостность DDS (ожидаем 0 orphan-событий)
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "
SELECT countIf(click_id IS NOT NULL AND click_id NOT IN (SELECT click_id FROM dds.click)) AS orphan_events
FROM dds.event
"
```

Ожидаем:
- `ods.browser_event`, `dds.event`, `dm.dq_summary` > 0;
- `orphan_events = 0`.

### A.4 Smoke мониторинга

```bash
# Prometheus targets
curl -s http://localhost:9090/api/v1/targets | grep -o '"health":"[^"]*"'

# Grafana health
curl -s -u admin:admin http://localhost:3000/api/health

# Дашборды по UID
curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/clickhouse-overview" | grep -o '"title":"[^"]*"'
curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/kafka-overview" | grep -o '"title":"[^"]*"'
curl -s -u admin:admin "http://localhost:3000/api/dashboards/uid/airflow-overview" | grep -o '"title":"[^"]*"'
```

Критерий успеха smoke:
- сервисы подняты;
- стартовая история доведена до DM;
- данные проходят до DM;
- мониторинг и дашборды доступны.

---

## Контур B: Full (полный)

Ожидаемая длительность: ~30-60 минут.

### B.1 Полная стартовая история и ETL

```bash
# История на 3 суток с видимой суточной волной
PROFILE=daily-wave make generated-history-analytics
make up
```

### B.2 Проверка объемов и DQ

```bash
# Диапазон модельного времени в витрине
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "
SELECT min(event_ts) AS min_event_ts, max(event_ts) AS max_event_ts, count() AS events
FROM dm.v_events_enriched
"

# Сводка по слоям
docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 --query "
SELECT 'STG' as layer, sum(rows) as total_rows FROM (
    SELECT count() as rows FROM stg.browser_raw UNION ALL
    SELECT count() FROM stg.location_raw UNION ALL
    SELECT count() FROM stg.device_raw UNION ALL
    SELECT count() FROM stg.geo_raw
) UNION ALL
SELECT 'ODS', sum(rows) FROM (
    SELECT count() FROM ods.browser_event UNION ALL
    SELECT count() FROM ods.location_event UNION ALL
    SELECT count() FROM ods.device_by_click UNION ALL
    SELECT count() FROM ods.geo_by_click
) UNION ALL
SELECT 'DDS', sum(rows) FROM (
    SELECT count() FROM dds.event UNION ALL
    SELECT count() FROM dds.click
)"

# DQ summary

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "SELECT * FROM dm.dq_summary ORDER BY layer, table_name, check_name"
```

Критерии успеха full:
- STG заполнен по всем 4 потокам;
- ODS/DDS/DM заполнены;
- диапазон `event_ts` соответствует созданной стартовой истории;
- `dm.dq_summary` не пуста и содержит метрики всех слоев.

### B.3 Тест восстановления stop/start

```bash
# Остановить без удаления данных
docker compose stop

# Проверить volumes

docker volume ls | grep -E 'clickhouse-data|kafka-data|pgmeta|grafana_lib|superset_data|superset_config'

# Поднять обратно
docker compose start

# Проверить, что данные сохранились

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "SELECT count() FROM dds.event"
```

---

## Сценарий C: «Грязные» данные не валят пайплайн

Цель: доказать, что невалидные записи фиксируются в `ods.*_errors`, а ETL завершается успешно.

Предусловие: выполнен `Контур A` или `Контур B` (в STG уже есть валидные данные).

### C.1 Инъекция невалидных записей в STG

```bash
# browser: невалидные UUID и timestamp

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "INSERT INTO stg.browser_raw (raw) VALUES ('{\"event_id\":\"bad-uuid\",\"event_timestamp\":\"bad-ts\",\"click_id\":\"bad-click\",\"event_type\":\"pageview\"}')"

# location: невалидный event_id

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "INSERT INTO stg.location_raw (raw) VALUES ('{\"event_id\":\"bad-uuid\",\"page_url\":\"https://example.com\"}')"

# device: невалидные click_id и user_domain_id

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "INSERT INTO stg.device_raw (raw) VALUES ('{\"click_id\":\"bad-uuid\",\"user_domain_id\":\"bad-uuid\",\"device_type\":\"Mobile\"}')"

# geo: невалидные click_id и координаты

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "INSERT INTO stg.geo_raw (raw) VALUES ('{\"click_id\":\"bad-uuid\",\"geo_latitude\":\"abc\",\"geo_longitude\":\"def\"}')"
```

### C.2 Повторный ETL

```bash
docker compose exec -T airflow-webserver airflow dags trigger etl_pipeline \
  --conf '{"full_refresh": true}'
```

Ожидаем: DAG `etl_pipeline` завершен в `Success`.

### C.3 Ассерты по ошибкам

```bash
# Ошибки должны попасть в *_errors таблицы

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "
SELECT 'browser_event_errors' AS table, count() AS cnt FROM ods.browser_event_errors
UNION ALL
SELECT 'location_event_errors', count() FROM ods.location_event_errors
UNION ALL
SELECT 'device_by_click_errors', count() FROM ods.device_by_click_errors
UNION ALL
SELECT 'geo_by_click_errors', count() FROM ods.geo_by_click_errors
"

# При этом пайплайн продолжает давать бизнес-слой

docker compose exec -T clickhouse clickhouse-client --user=default --password=123456 \
  --query "SELECT count() FROM dds.event"
```

Критерии успеха сценария C:
- `etl_pipeline` не падает;
- хотя бы одна `*_errors` таблица увеличилась;
- `dds.event` остается непустой (валидные данные продолжают обрабатываться).

---

## Проверка алертов (фактический набор)

Проверяем, что в Grafana загружены именно текущие provisioned-правила:

```bash
curl -s -u admin:admin http://localhost:3000/api/v1/provisioning/alert-rules | grep -o '"title":"[^"]*"'
```

Ожидаемые правила:

- ClickHouse:
  - `ClickHouse Failed Queries Rate`
  - `ClickHouse Memory Resident High`
  - `ClickHouse Parts Active High`
- Airflow:
  - `Airflow Scheduler Down`
  - `Airflow Queue Backlog`
  - `High Task Failure Rate`
  - `High DAG Parse Time`
- Kafka:
  - `Kafka Broker Down`
  - `Kafka Consumer Lag High`
  - `Kafka No Messages Produced`

---

## Финальный чек-лист приемки

- `Smoke` проходит стабильно после изменений в коде.
- `Full` проходит перед демонстрацией/релизом.
- Сценарий `Грязные данные` подтверждает, что ошибки локализуются в ODS и не ломают ETL.
- Метрики и дашборды доступны, алерты совпадают с текущей конфигурацией provisioning.
