210 lines
9.7 KiB
Markdown
210 lines
9.7 KiB
Markdown
---
|
||
type: test-report
|
||
work_item_id: ET-008
|
||
title: "Test Report: GPS-треки с публичных платформ на карте"
|
||
version: 3
|
||
status: pass
|
||
created_at: 2026-06-01
|
||
updated_at: 2026-06-01
|
||
authors:
|
||
- "agent:tester"
|
||
tested_branch: feature/ET-008-gps
|
||
tested_commits:
|
||
- 1ffa178 "fix(gps-tracks): aggregate last_pipeline_run in health endpoint (REQ-F-12)"
|
||
- ba356ae "fix(gps-tracks): rename health fields and fix layer insert priority (F-04, F-05)"
|
||
- edbe9a3 "fix(gps-tracks): normalise GeoJSON props, add health fields, OSM meta fetch, z-order fix"
|
||
- 3734b98 "feat(ET-008): GPS tracks pipeline, API, frontend layer"
|
||
verdict: stage:ready-to-deploy
|
||
---
|
||
|
||
# Test Report — ET-008: GPS-треки с публичных платформ на карте (v3)
|
||
|
||
## Вердикт: **stage:ready-to-deploy**
|
||
|
||
Коммит `1ffa178` закрывает последний P1-дефект (F-04 — структура
|
||
`last_pipeline_run`). Все 141 pytest и 22 JS unit-теста зелёные. Все
|
||
P0/P1 находки из code-review v2 устранены. E2E и UI-тесты пропущены по
|
||
инфраструктурным причинам (бэкенд ET-008 не задеплоен на тест-стенд,
|
||
UI-раннер `/home/slin/tools/ui-test/run_tests.js` недоступен) — это не
|
||
дефект кода; рекомендуется выполнить E2E-прогон сразу после деплоя.
|
||
|
||
---
|
||
|
||
## Шаг 1 — Проверка окружения
|
||
|
||
| Endpoint | Статус | Детали |
|
||
|---|---|---|
|
||
| `GET /enduro/api/health` | ✅ 200 OK | `{"status":"ok","db_exists":true}` |
|
||
| `GET /enduro/api/gps-tracks/health` | ❌ 404 | ET-008 не задеплоен на стенд |
|
||
| `GET /enduro/api/gps-tracks?bbox=…` | ❌ 404 | ET-008 не задеплоен на стенд |
|
||
| `GET /enduro/api/gps-tracks/tiles/…mvt` | ❌ 404 | ET-008 не задеплоен на стенд |
|
||
| Фронтенд (HTML) | ✅ | `#public-tracks-cb`, `#sheet-gps-filters`, `gps_tracks.js` в разметке |
|
||
|
||
Бэкенд-роуты `/api/gps-tracks/*` возвращают 404 — статика ET-008
|
||
задеплоена, сервис не поднят. E2E и UI тесты выполнить невозможно до
|
||
деплоя.
|
||
|
||
---
|
||
|
||
## Шаг 2 — Функциональные тесты (`python -m pytest tests/ -v`)
|
||
|
||
```
|
||
cd /repos/enduro-trails/src/api
|
||
python -m pytest ../../tests/ -v --tb=short
|
||
```
|
||
|
||
**Результат: 141 passed, 0 failed, 7 warnings**
|
||
|
||
| Сюита | Тестов | Результат |
|
||
|---|---|---|
|
||
| `tests/api/test_gps_tracks_dedup.py` | 8 | ✅ PASS |
|
||
| `tests/api/test_gps_tracks_endpoint.py` | 15 | ✅ PASS |
|
||
| `tests/api/test_gps_tracks_mvt.py` | 9 | ✅ PASS |
|
||
| `tests/api/test_gps_tracks_sources_osm.py` | 21 | ✅ PASS |
|
||
| `tests/integration/test_routing_barriers.py` | 7 | ✅ PASS |
|
||
| `tests/unit/test_base_layer.py` | 22 | ✅ PASS |
|
||
| `tests/unit/test_gpx_upload.py` | 21 | ✅ PASS |
|
||
| `tests/unit/test_health.py` | 1 | ✅ PASS |
|
||
| `tests/unit/test_poi_toggle.py` | 10 | ✅ PASS |
|
||
| `tests/unit/test_unit_toggle.py` | 18 | ✅ PASS |
|
||
| `tests/web/test_gps_tracks.py` | 9 | ✅ PASS |
|
||
|
||
Предупреждения (7 шт.) — `DeprecationWarning` в `mapbox_vector_tile.encode`
|
||
(внешняя библиотека, некритично).
|
||
|
||
---
|
||
|
||
## Шаг 3 — E2E тесты (Playwright)
|
||
|
||
**SKIP** — бэкенд ET-008 не задеплоен на тест-стенд; Playwright-сценарии
|
||
E-01, E-02 (pipeline smoke) и E-10…E-12 (UI-фильтры) выполнить
|
||
невозможно. Рекомендуется запустить после `make deploy-test`.
|
||
|
||
---
|
||
|
||
## Шаг 4 — JS Unit-тесты (`node --test`)
|
||
|
||
```
|
||
node --test tests/web/gps_tracks.test.js
|
||
```
|
||
|
||
**Результат: 22 passed, 0 failed**
|
||
|
||
| Группа | Тестов | Результат |
|
||
|---|---|---|
|
||
| F-05: `_findGpsInsertPosition` — приоритет слоёв | 9 | ✅ PASS |
|
||
| Filters: начальное состояние `window.gpsTracksLayer` | 5 | ✅ PASS |
|
||
| Colors: палитра источников, активностей, fallback | 8 | ✅ PASS |
|
||
|
||
---
|
||
|
||
## Шаг 5 — UI / Visual тесты (TC-UI-01…TC-UI-20)
|
||
|
||
**SKIP** — `/home/slin/tools/ui-test/run_tests.js` недоступен; бэкенд
|
||
ET-008 не отвечает на тест-стенде. Скриншоты TC-UI-01…TC-UI-20 не
|
||
сделаны.
|
||
|
||
---
|
||
|
||
## Верификация фиксов из `12-review.md`
|
||
|
||
### Итоговая таблица
|
||
|
||
| Finding | Severity | v2 | v3 | Вердикт |
|
||
|---|---|---|---|---|
|
||
| F-01: GeoJSON props несовместимы с MVT | P0 | PASS | PASS | ✅ PASS |
|
||
| F-02: `length_m` вместо `length_km` в GeoJSON | P1 | PASS | PASS | ✅ PASS |
|
||
| F-03: OSM batch-fetch и activity_type не реализованы | P1 | PASS | PASS | ✅ PASS |
|
||
| F-04: Health endpoint несовместим с REQ-F-12 | P1 | ⚠️ WARN | PASS | ✅ PASS |
|
||
| F-05: Z-order vs `gpx-layer-*` | P1 | PASS | PASS | ✅ PASS |
|
||
| `tests/web/gps_tracks.test.js` отсутствует | — | PASS | PASS | ✅ PASS |
|
||
| F-06: Нет валидации площади bbox | P2 | follow-up | follow-up | ⏭ follow-up |
|
||
| F-07: Дефолт sources включает disabled | P2 | follow-up | follow-up | ⏭ follow-up |
|
||
| F-08: LRU-кэш — на самом деле FIFO | P2 | follow-up | follow-up | ⏭ follow-up |
|
||
| F-09…F-12: P3-находки | P3 | follow-up | follow-up | ⏭ follow-up |
|
||
|
||
---
|
||
|
||
### F-04 [P1] → ✅ **PASS** (v2: ⚠️ WARN)
|
||
|
||
Коммит `1ffa178` реализует агрегацию строк `pipeline_runs` по
|
||
`MAX(started_at)` в полный контракт REQ-F-12:
|
||
|
||
```python
|
||
# endpoint.py, gps_health()
|
||
cur.execute("""
|
||
SELECT started_at, finished_at, region_id, source_id,
|
||
status, tracks_new, errors_json
|
||
FROM pipeline_runs
|
||
WHERE started_at = (SELECT MAX(started_at) FROM pipeline_runs)
|
||
ORDER BY region_id, source_id
|
||
""")
|
||
# → агрегация в:
|
||
{
|
||
"started_at": "...", "finished_at": "...",
|
||
"regions": ["tsfo_plus_chuvashia"],
|
||
"sources_ok": ["osm", "enduro_russia"],
|
||
"sources_error": [{"source": "ttrails", ...}],
|
||
"tracks_added": 100
|
||
}
|
||
```
|
||
|
||
Тест `test_i40_health_endpoint` (обновлён) проверяет:
|
||
- наличие всех 6 обязательных полей (`started_at`, `finished_at`,
|
||
`regions`, `sources_ok`, `sources_error`, `tracks_added`);
|
||
- типы (`list`, `int`);
|
||
- отсутствие сырых полей БД (`region_id`, `source_id`);
|
||
- конкретные агрегированные значения из фикстуры (2 региона,
|
||
2 ok-источника).
|
||
|
||
`test_i40_health_empty_db` подтверждает: при пустой БД — `last_pipeline_run: null`.
|
||
|
||
---
|
||
|
||
### Детали: что проверяют ключевые тесты ET-008
|
||
|
||
| Тест-ID | Связанный AC / REQ | Что проверяется |
|
||
|---|---|---|
|
||
| `test_f01_f02_geojson_normalised_properties` | AC-04, REQ-F-10 | GeoJSON `activity`, `source`, `length_km`, `activity_type` |
|
||
| `test_i20_filter_by_activity` | AC-04 | фильтр `?activity=enduro` возвращает только enduro |
|
||
| `test_i20_filter_by_source` | AC-04 | фильтр `?source=osm` возвращает только OSM |
|
||
| `test_i21_truncation` | AC-04 | `truncated=true`, `returned=500`, `total_in_bbox=1500` |
|
||
| `test_i22_invalid_bbox_returns_400` (7 param) | AC-04 | 400 на невалидные bbox |
|
||
| `test_i30_mvt_tile_returns` | AC-05 | `200 application/x-protobuf`, layer `gps_tracks` |
|
||
| `test_i31_cache_hit` | AC-05, REQ-NF-04 | `X-Cache: HIT` на повторный запрос |
|
||
| `test_i40_health_endpoint` | AC-06, REQ-F-12 | все поля health, агрегированный `last_pipeline_run` |
|
||
| `test_u13_merge_sources_on_upsert` | AC-03, REQ-F-08 | дедупликация: union sources |
|
||
| `test_u44_xxe_protection` | REQ-NF-01 | defusedxml блокирует XXE |
|
||
| `test_u45_meta_response_with_known_tag` | REQ-F-04, REQ-F-07 | OSM tag → `activity_type` |
|
||
| `test_gps_tracks_find_insert_position_priority_gpx_first` | AC-10, §7.1 | gpx-layer-* > route-* |
|
||
|
||
---
|
||
|
||
## Открытые P2/P3 — follow-up (не меняют вердикт)
|
||
|
||
| Finding | Severity | Описание | Рекомендация |
|
||
|---|---|---|---|
|
||
| F-06 | P2 | Нет проверки площади bbox | Добавить max area ≤ 100°² в `_parse_bbox()` |
|
||
| F-07 | P2 | Дефолт sources содержит disabled-источники | Инициализировать из `/api/gps-tracks/health.tracks_by_source` |
|
||
| F-08 | P2 | LRU-кэш — на самом деле FIFO | `OrderedDict` с `move_to_end` при чтении |
|
||
| F-09 | P3 | `save_user_field` в YAML не читается кодом | Обработать в upsert |
|
||
| F-10 | P3 | Лишний `import pytest_asyncio` | Убрать |
|
||
| F-11 | P3 | `MockRow(dict)` вместо `sqlite3.Row` | Рефактор тестов |
|
||
| F-12 | P3 | Лишняя проверка `"source_priority" in existing.keys()` | Упростить |
|
||
|
||
---
|
||
|
||
## Рекомендации для деплоя
|
||
|
||
После выполнения `make deploy-test` или `docker compose up -d` на тест-стенде
|
||
с веткой `feature/ET-008-gps`:
|
||
|
||
1. **Smoke API:**
|
||
```bash
|
||
curl https://openclaw.mva154.duckdns.org/enduro/api/gps-tracks/health
|
||
curl "https://openclaw.mva154.duckdns.org/enduro/api/gps-tracks?bbox=37.0,55.0,38.0,56.0&limit=5"
|
||
```
|
||
2. **E2E Playwright:** E-01 (pipeline smoke), E-02 (dedup), E-10…E-12 (filters).
|
||
3. **UI тесты:** TC-UI-01…TC-UI-20 через `run_tests.js` (при наличии раннера).
|
||
4. **P2 follow-up** можно закрыть отдельным PR после приёмки основного.
|