analyst(ET): auto-commit from analyst run_id=72
This commit is contained in:
216
docs/work-items/ET-012/01-brd.md
Normal file
216
docs/work-items/ET-012/01-brd.md
Normal file
@@ -0,0 +1,216 @@
|
||||
---
|
||||
type: brd
|
||||
work_item_id: ET-012
|
||||
title: "BRD: Показывать пользовательские треки с зума z5 (сейчас с z8)"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-06-04
|
||||
updated_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
related:
|
||||
- "ET-008"
|
||||
- "ET-009"
|
||||
---
|
||||
|
||||
# BRD — ET-012: Показывать пользовательские треки с зума z5
|
||||
|
||||
## 1. Цель
|
||||
|
||||
Снизить нижний порог видимости слоя публичных GPS-треков
|
||||
(`gps-tracks-layer-mvt`) с **z8** до **z5**, чтобы пользователь
|
||||
видел общее покрытие сети треков на средних/мелких масштабах
|
||||
(z5 ≈ ¼ Европы в кадре, z7 ≈ область размером с ЦФО) и мог
|
||||
«с высоты птичьего полёта» искать интересные треки.
|
||||
|
||||
На сегодня (после ET-008/ET-009) слой публичных треков физически
|
||||
скрыт ниже z8 двумя механизмами:
|
||||
|
||||
- vector-source задаёт `minzoom: 8` (тайлы не запрашиваются);
|
||||
- клиентский visibility-фильтр `zoom >= GPS_TRACKS_MIN_ZOOM` (8)
|
||||
в `_syncGpsLayersVisibility` и `applyGpsHaloVisibility`;
|
||||
- UI-hint «Зум 8+» (`#public-tracks-zoom-hint`) висит как
|
||||
обоснование «почему пусто».
|
||||
|
||||
ET-012 = **снизить порог + сохранить читаемость и
|
||||
производительность** на новых зумах z5-z7.
|
||||
|
||||
## 2. Контекст
|
||||
|
||||
### 2.1 Текущее поведение (после ET-009)
|
||||
|
||||
- Источник `gps-tracks-tiles` (MVT):
|
||||
`tiles: /api/gps-tracks/tiles/{z}/{x}/{y}.mvt`,
|
||||
`minzoom: 8`, `maxzoom: 11`.
|
||||
- Источник `gps-tracks-geo` (GeoJSON, через `/api/gps-tracks?bbox=…`)
|
||||
включается при `zoom >= GPS_TRACKS_ZOOM_CUTOFF = 12` —
|
||||
ET-012 в этом регионе ничего не меняет.
|
||||
- `build_gps_mvt` (`src/api/gps_tracks/mvt.py`) уже содержит
|
||||
zoom-aware упрощение и пороги:
|
||||
- `_simplify_coords`: tolerance `0.008°` (~800м) на z≤7,
|
||||
`0.002°` (~200м) на z8-9, `0.0005°` (~50м) на z10-11,
|
||||
без упрощения на z≥12.
|
||||
- В `build_gps_mvt`: при z≤7 — `min_length_m=2000`, `limit=3000`
|
||||
features на тайл; на больших зумах limit/min_length мягче.
|
||||
- Endpoint `/api/gps-tracks/tiles/{z}/{x}/{y}.mvt` принимает
|
||||
любой `0 ≤ z ≤ 22`; никакой пре-нарезки тайлов нет —
|
||||
каждый тайл строится из БД on-demand и кэшируется в FIFO
|
||||
размером 1024.
|
||||
- На клиенте используется LRU-кэш MapLibre и сетевой кэш браузера.
|
||||
- Текущая БД (test-среда) содержит порядка нескольких сотен
|
||||
треков (ожидаемо ≤ 5000 в горизонте года), геометрия каждого
|
||||
трека — десятки-тысячи точек.
|
||||
|
||||
### 2.2 Почему это бизнес-важно
|
||||
|
||||
- На малых масштабах (z5-z7) пользователю **сейчас негде искать
|
||||
треки**: при первом открытии карта по умолчанию показывает
|
||||
обзор региона; чтобы увидеть хоть что-то из публичных треков,
|
||||
нужно сразу зумить до z8 — это лишний шаг и плохой UX.
|
||||
- Видимость на z5-z7 = понимание «где вообще катаются» в
|
||||
масштабах целого региона/страны, что помогает планировать
|
||||
выезды и оценивать покрытие.
|
||||
- Конкуренты (Wikiloc, Komoot) показывают clustered/density
|
||||
слои с z3-z4; для нас достаточно начать с z5.
|
||||
|
||||
### 2.3 Открытые вопросы из бизнес-запроса — ответы по результатам анализа
|
||||
|
||||
| Вопрос | Ответ |
|
||||
| ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Где задаётся minzoom слоя? | Клиент: `src/web/gps_tracks.js`, константа `GPS_TRACKS_MIN_ZOOM = 8` (используется в source.minzoom, visibility, halo, hint). |
|
||||
| Тайлы уже нарезаны до z5 или нужно догенерить? | Нарезки нет вообще — тайлы строятся on-demand из SQLite по bbox. Никакой генерации/инвалидации делать не нужно. |
|
||||
| Нужна ли генерализация линий на малых зумах? | Базовая уже есть в `_simplify_coords` (DP-tolerance 800м при z≤7). Для z5-z6 нужно ужесточить пороги (min_length, limit, tolerance) — F-04..F-06. |
|
||||
|
||||
## 3. Scope
|
||||
|
||||
### In scope
|
||||
|
||||
| # | Функция |
|
||||
| ----- | -------------------------------------------------------------------------------------------------------------------- |
|
||||
| F-01 | Снизить клиентскую константу `GPS_TRACKS_MIN_ZOOM` с 8 до 5 в `src/web/gps_tracks.js`. |
|
||||
| F-02 | Уменьшить `minzoom` vector-source `gps-tracks-tiles` с 8 до 5 (использует ту же константу). |
|
||||
| F-03 | На бэкенде в `build_gps_mvt` расширить tier-структуру: добавить уровни z5, z6 с более жёсткими min_length_m и limit. |
|
||||
| F-04 | В `_simplify_coords` добавить tier для z5-z6: tolerance ~0.02° (~2 км) на z5, ~0.01° (~1 км) на z6. |
|
||||
| F-05 | Расширить `line-width` (основной слой) и `line-width` (halo) для z5: явные stops чтобы линия читалась. |
|
||||
| F-06 | UI-hint `#public-tracks-zoom-hint`: либо обновить текст до «Зум 5+», либо скрывать всегда (после снижения порога порог фактически недостижим в обычных сценариях). |
|
||||
| F-07 | Halo на спутнике активируется на z5-z11 (как и основной MVT-слой). |
|
||||
| F-08 | Производительность: p95 generation одного MVT-тайла z5 ≤ 500 мс при размере БД ≤ 5000 треков; p95 endpoint не выше +50 мс относительно baseline ET-009. |
|
||||
| F-09 | Читаемость: на z5 с включённым слоем при ≥ 200 треках по ЦФО карта остаётся «читаемой» — линии не сливаются в сплошную кашу. Критерий приёмки качественный, см. AC-08. |
|
||||
| F-10 | Halo на спутнике на z5-z7: не «глушит» подложку. Halo-width на z5 ≤ 2 px. |
|
||||
| F-11 | Регрессия: поведение на z8-z11 (MVT) и z12+ (GeoJSON) не меняется. |
|
||||
| F-12 | Регрессия: фильтры по `activity` / `source` работают на z5-z7 так же, как на z8+. |
|
||||
| F-13 | Регрессия: popup трека и кнопка «Скачать GPX» (ET-011) работают при клике на трек на z5-z7. (Замечание: на низких зумах кликнуть в линию пальцем сложнее — оставляем как есть, hit-area не расширяем.) |
|
||||
|
||||
### Out of scope
|
||||
|
||||
- **Clustering / heat-map на z3-z4.** Идея здравая, но требует
|
||||
отдельной серверной агрегации (например, h3-cell counts) и
|
||||
нового UI-слоя. Делаем отдельным work item.
|
||||
- **Пре-нарезка тайлов на диск.** Не требуется при текущем
|
||||
размере БД; on-demand + LRU справляются.
|
||||
- **Изменение поведения GeoJSON-слоя на z12+.** Не трогаем.
|
||||
- **Изменение фильтров по activity/source.** Не трогаем.
|
||||
- **Изменения popup'а трека.** Не трогаем.
|
||||
- **Расширение `gps_tracks_minzoom` в админ-конфиг.** Константа
|
||||
остаётся хардкодом — менять через релиз. Если в будущем
|
||||
появится потребность в feature-flag — отдельный work item.
|
||||
- **Изменения схемы БД и dedup-алгоритма.** Не трогаем.
|
||||
- **Изменения OSRM / routing.** Не трогаем.
|
||||
|
||||
## 4. Метрики успеха
|
||||
|
||||
| # | Метрика | Критерий |
|
||||
| --- | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| M-1 | Видимость на z5 | При включённом чекбоксе «Публичные треки» и `zoom = 5` слой `gps-tracks-layer-mvt` имеет `visibility: visible`. На карте отображаются линии треков. |
|
||||
| M-2 | Видимость на z6, z7 | Аналогично M-1 для z6 и z7. |
|
||||
| M-3 | Поведение на z8-z11 не изменилось | Регрессия: на z8-z11 виден тот же набор треков, что и до ET-012 (с поправкой на улучшенную z5-z7 генерализацию — не считается регрессией). |
|
||||
| M-4 | Поведение на z12+ не изменилось | Регрессия: GeoJSON-слой включается ровно на z=12 как раньше; MVT слой скрывается ровно на z=12. |
|
||||
| M-5 | Hint «Зум 5+» / «Зум 8+» | После ET-012: при включённом слое и zoom ≥ 5 hint скрыт. (До ET-012 hint показывал «Зум 8+» при zoom < 8.) |
|
||||
| M-6 | p95 MVT tile generation на z5 | ≤ 500 мс на test-среде при размере БД до 5000 треков; ≤ 1 с при размере до 20 000 треков (запас). |
|
||||
| M-7 | p95 endpoint `/api/gps-tracks/tiles/5/x/y.mvt` | cold ≤ 700 мс, hit ≤ 50 мс (кэш). |
|
||||
| M-8 | Размер MVT тайла z5 | ≤ 200 KB после генерализации и фильтра min_length (защита от мобильного трафика). Если больше — F-03/F-04 переусиливают (ужесточить limit). |
|
||||
| M-9 | Читаемость z5 | На скриншоте z5 с ≥ 200 треков по ЦФО видны минимум 3 разных линии в разных частях кадра; нет «сплошной заливки» одной зоны. Качественная проверка по TC-UI-12-Z5-Q. |
|
||||
| M-10 | Регрессия фильтров | Снятие галки «EnduroRussia» в `#sheet-gps-filters` на z=6 убирает соответствующие линии (как и на z=10). |
|
||||
| M-11 | LRU-кэш не переполняется ненужно | После панорамирования по миру на z5-z6 (≈ 50 уникальных тайлов) кэш-хит на повторных тайлах ≥ 80 %. |
|
||||
|
||||
## 5. Риски
|
||||
|
||||
| # | Риск | Вероятность | Влияние | Митигация |
|
||||
| ---- | ------------------------------------------------------------------------------------------ | ----------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| R-1 | На z5 слишком много фич в одном тайле → MVT > 1 MB, тормоза рендера на мобильном. | Средняя | Высокое | F-03: жёсткий `min_length_m` и `limit` для z=5. Метрика M-8 (≤ 200 KB) — гейт. При нарушении — ужесточить limit/min_length. |
|
||||
| R-2 | На z5 линии после Douglas-Peucker превращаются в «обрубки» (трек из 1000 точек → 3 точки). | Средняя | Низкое | Качественная проверка по TC-UI-12-Z5-Q. Tolerance подобрана так, чтобы трек ≤ 5 км превращался в прямую — это норма на z5. |
|
||||
| R-3 | Линия `line-width: 0.5 px` на z5 невидима на retina-дисплеях. | Низкая | Низкое | F-05: явные stops `interpolate linear zoom 5 0.8 8 1.0 12 2.0 16 3.0`. Проверка по TC-UI-01-Z5. |
|
||||
| R-4 | Бэкенд-запрос к БД с огромным bbox (z5 тайл ~1250×1250 км) тянет ВСЕ треки региона. | Средняя | Среднее | Запрос уже идёт через индекс по min_lon/max_lon/min_lat/max_lat в SQLite; при ≤ 5000 строк это < 100 мс. M-7 — гейт. При деградации — добавить индекс `length_m`. |
|
||||
| R-5 | На z5 buffer 10 % bbox в endpoint раздувает запрос до 130 % площади. | Низкая | Низкое | На z5 это уже не имеет смысла (соседний тайл всё равно отрисует пограничные фичи). Опционально — снизить buffer до 5 % для z≤6. См. TRZ §3.10. |
|
||||
| R-6 | LRU-кэш в 1024 тайла на z5 (всего 32×32 = 1024 тайла в мире) — теоретически переполняется на «walk through world». | Низкая | Низкое | На практике пользователь видит ~10-20 тайлов одновременно на z5; ротация работает. Опционально — увеличить `_GPS_TILE_CACHE_MAX` до 2048. См. TRZ §3.11. |
|
||||
| R-7 | Hint «Зум 8+» забыли удалить → пользователь видит и линии, и подсказку «увеличь зум». | Средняя | Низкое | F-06 явно: либо hide-always при `GPS_TRACKS_MIN_ZOOM = 5`, либо текст «Зум 5+». См. AC-05. |
|
||||
| R-8 | Регрессия halo на спутнике: halo на z5 «закрывает» линию. | Низкая | Низкое | F-10: halo-width ≤ 2 px на z5; проверка по TC-UI-09-Z5-SAT. |
|
||||
| R-9 | Пользователи на мобильном с медленным интернетом получают раздутые тайлы z5-z6 при первом открытии. | Средняя | Среднее | Размер ≤ 200 KB (M-8) + gzip на nginx + браузерный кэш. Опционально — отсрочить включение слоя до первого panMove (не в scope ET-012). |
|
||||
| R-10 | Конфликт с поведением другого слоя `gps-tracks-halo-mvt-satellite`: оба используют те же фичи MVT — на z5 halo и линия должны быть согласованы. | Низкая | Низкое | Используют тот же source/source-layer; видимость синхронизируется через `_syncGpsLayersVisibility` + `applyGpsHaloVisibility`. Регрессионная проверка TC-UI-09-Z5-SAT. |
|
||||
|
||||
## 6. Зависимости
|
||||
|
||||
### Backend
|
||||
|
||||
- `src/api/gps_tracks/mvt.py:build_gps_mvt` — расширить tier-таблицу
|
||||
для z5, z6 (F-03).
|
||||
- `src/api/gps_tracks/mvt.py:_simplify_coords` — добавить tier для z5-z6 (F-04).
|
||||
- `src/api/gps_tracks/endpoint.py` — без изменений логики, опциональная
|
||||
правка buffer для z≤6 (R-5). По умолчанию не меняем.
|
||||
- Endpoint `/api/gps-tracks/tiles/{z}/{x}/{y}.mvt` уже принимает z 0..22 — не трогаем.
|
||||
|
||||
### Frontend
|
||||
|
||||
- `src/web/gps_tracks.js`:
|
||||
- константа `GPS_TRACKS_MIN_ZOOM = 5` (F-01, F-02).
|
||||
- `_gpsLayerDef` paint.line-width — расширить interpolate-выражение
|
||||
для z5 (F-05).
|
||||
- `_gpsHaloDef` paint.line-width — то же (F-05, F-10).
|
||||
- `src/web/index.html`:
|
||||
- `#public-tracks-zoom-hint` — обновить текст или скрыть навсегда (F-06).
|
||||
- Стили `style.json` / `style-dark.json` — без изменений
|
||||
(минзум слоя в стилях не задаётся; он живёт в коде клиента).
|
||||
|
||||
### Тесты
|
||||
|
||||
- Новые unit-тесты `tests/unit/test_gps_mvt_zoom_tiers.py` (новый файл):
|
||||
тиры min_length и limit для z=5..z=12.
|
||||
- Новые unit-тесты `tests/unit/test_gps_mvt_simplify.py` или расширение
|
||||
существующих: tolerance для z5-z6.
|
||||
- Новые integration-тесты `tests/integration/test_gps_tile_z5_z7.py`:
|
||||
endpoint отдаёт непустой MVT для z=5/6/7 на регионе с ≥ 10 треками.
|
||||
- UI-тесты см. `04b-ui-test-cases.md`.
|
||||
|
||||
### Документация
|
||||
|
||||
- `01-brd.md` (этот файл).
|
||||
- `02-trz.md`, `03-acceptance-criteria.md`, `04-test-plan.yaml`, `04b-ui-test-cases.md` — этот пакет.
|
||||
- Опциональный ADR не требуется: tile-pipeline уже спроектирован
|
||||
под динамические тиры в ET-008/ET-009; это calibration, а не
|
||||
архитектурное решение. Если разработчик в реализации обнаружит
|
||||
нужду в смене политики (например, переход к heat-map на z5) —
|
||||
добавляет ADR в `06-adr/`.
|
||||
|
||||
### Инфра / Данные
|
||||
|
||||
- Test-среда `https://openclaw.mva154.duckdns.org/enduro/` —
|
||||
существующий деплой.
|
||||
- БД `data/gps_tracks.sqlite` — без миграций.
|
||||
- nginx gzip уже включён.
|
||||
|
||||
### Связи с другими work items
|
||||
|
||||
- **ET-008** — родительский слой публичных GPS-треков.
|
||||
- **ET-009** — заполнил БД треками EnduroRussia/Wikiloc; без этих
|
||||
данных z5-z7 будет визуально пустым в test-среде.
|
||||
- **ET-011** — кнопка «Скачать GPX» в popup'е; регрессия покрывается.
|
||||
- **PH-3 Smart Route** — независимо.
|
||||
- Будущий work item «Heat-map / clustering на z3-z4» — отдельная задача.
|
||||
|
||||
## 7. План в одну строку
|
||||
|
||||
Снижаем константу `GPS_TRACKS_MIN_ZOOM` с 8 до 5, расширяем
|
||||
zoom-tier структуру в `build_gps_mvt` и `_simplify_coords` для z5-z6,
|
||||
добавляем явные line-width stops для z5, скрываем/обновляем hint,
|
||||
гарантируем читаемость и производительность тестами и
|
||||
скриншот-тестами.
|
||||
442
docs/work-items/ET-012/02-trz.md
Normal file
442
docs/work-items/ET-012/02-trz.md
Normal file
@@ -0,0 +1,442 @@
|
||||
---
|
||||
type: trz
|
||||
work_item_id: ET-012
|
||||
title: "ТЗ: Показывать пользовательские треки с зума z5"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-06-04
|
||||
updated_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
related:
|
||||
- "ET-008"
|
||||
- "ET-009"
|
||||
---
|
||||
|
||||
# ТЗ — ET-012: Показывать пользовательские треки с зума z5
|
||||
|
||||
## 1. Терминология
|
||||
|
||||
- **MVT-слой** — `gps-tracks-layer-mvt`, отрисовка треков из
|
||||
vector-source `gps-tracks-tiles` (тайлы `/api/gps-tracks/tiles/{z}/{x}/{y}.mvt`).
|
||||
Активен при `GPS_TRACKS_MIN_ZOOM ≤ zoom < GPS_TRACKS_ZOOM_CUTOFF`.
|
||||
- **GeoJSON-слой** — `gps-tracks-layer-geo`, отрисовка треков из
|
||||
GeoJSON-source (запрос `/api/gps-tracks?bbox=…`). Активен при
|
||||
`zoom ≥ GPS_TRACKS_ZOOM_CUTOFF = 12`. **ET-012 не трогает этот слой.**
|
||||
- **Halo** — белый ореол на спутниковой подложке
|
||||
(`gps-tracks-halo-mvt-satellite`, `gps-tracks-halo-geo-satellite`).
|
||||
- **Zoom-tier** — диапазон зумов (например, `z ≤ 5`, `6 ≤ z ≤ 7`),
|
||||
для которого `build_gps_mvt` применяет общий набор лимитов
|
||||
(`min_length_m`, `limit`) и порог упрощения (`tolerance`).
|
||||
- **Douglas-Peucker tolerance** — параметр `shapely.LineString.simplify`,
|
||||
в градусах WGS84. На широте 55°: 1° долготы ≈ 64 км, 1° широты ≈ 111 км.
|
||||
- **Zoom-hint** — UI-надпись «Зум 8+» (`#public-tracks-zoom-hint`),
|
||||
подсказывающая, что нужно увеличить зум, чтобы увидеть слой.
|
||||
|
||||
## 2. Архитектурные опоры
|
||||
|
||||
ET-012 не строит новых модулей. Используем существующее:
|
||||
|
||||
- `src/web/gps_tracks.js` — клиентский слой ET-008/ET-009/ET-011.
|
||||
Константы: `GPS_TRACKS_ZOOM_CUTOFF = 12`, `GPS_TRACKS_MIN_ZOOM = 8`.
|
||||
- `src/api/gps_tracks/mvt.py:build_gps_mvt` — серверная сборка MVT
|
||||
с tier-логикой `min_length_m` / `limit` и `_simplify_coords`.
|
||||
- `src/api/gps_tracks/endpoint.py:get_gps_tile` — обработчик
|
||||
`/api/gps-tracks/tiles/{z}/{x}/{y}.mvt`. Валидация `0 ≤ z ≤ 22`
|
||||
уже есть. LRU-кэш `_gps_tile_cache` размер 1024 — не меняем.
|
||||
- `src/api/gps_tracks/db.py:get_tracks_in_bbox` — bbox-запрос
|
||||
по индексам min_lon/max_lon/min_lat/max_lat. Не меняем.
|
||||
|
||||
ET-012 = **значения констант + одна функция-tier + одна функция-simplify + три CSS/MapLibre-выражения + один hint**.
|
||||
|
||||
## 3. Требования
|
||||
|
||||
### REQ-F-01 — Клиентская константа `GPS_TRACKS_MIN_ZOOM`
|
||||
|
||||
Файл `src/web/gps_tracks.js`, строка
|
||||
```js
|
||||
const GPS_TRACKS_MIN_ZOOM = 8; // ниже — слой скрыт
|
||||
```
|
||||
заменить на
|
||||
```js
|
||||
const GPS_TRACKS_MIN_ZOOM = 5; // ниже — слой скрыт
|
||||
```
|
||||
|
||||
**Acceptance check.**
|
||||
```bash
|
||||
grep -n "GPS_TRACKS_MIN_ZOOM" src/web/gps_tracks.js
|
||||
```
|
||||
Первое вхождение содержит `= 5`. Никаких других мест объявления этой
|
||||
константы в `src/web/` нет (`grep -R "GPS_TRACKS_MIN_ZOOM" src/web/`).
|
||||
|
||||
### REQ-F-02 — Vector-source minzoom использует ту же константу
|
||||
|
||||
В `_ensureGpsSources` (gps_tracks.js, около строки 178) запись
|
||||
```js
|
||||
minzoom: GPS_TRACKS_MIN_ZOOM,
|
||||
```
|
||||
**не меняется** — она автоматически примет новое значение 5.
|
||||
|
||||
**Acceptance check.** Через DevTools на test-среде:
|
||||
```js
|
||||
window._map.getSource('gps-tracks-tiles').minzoom === 5
|
||||
```
|
||||
|
||||
### REQ-F-03 — Backend: zoom-tier для z=5 и z=6 в `build_gps_mvt`
|
||||
|
||||
Файл `src/api/gps_tracks/mvt.py`, функция `build_gps_mvt`,
|
||||
блок «Min-length фильтр по зуму» (строки ~104-116) заменить на:
|
||||
|
||||
```python
|
||||
# Min-length фильтр и cap на число фич по зуму
|
||||
if z <= 5:
|
||||
min_length_m = 10000 # 10 км — только «магистральные» треки
|
||||
limit = 1500
|
||||
elif z == 6:
|
||||
min_length_m = 5000 # 5 км
|
||||
limit = 2000
|
||||
elif z == 7:
|
||||
min_length_m = 2000 # как было для z<=7
|
||||
limit = 3000
|
||||
elif z <= 9:
|
||||
min_length_m = 0
|
||||
limit = 8000
|
||||
elif z <= 11:
|
||||
min_length_m = 0
|
||||
limit = 15000
|
||||
else:
|
||||
min_length_m = 0
|
||||
limit = 25000
|
||||
```
|
||||
|
||||
Цифры подобраны под цели:
|
||||
- z5: лимит 1500 фич × ~200 байт после генерализации ≈ 300 KB MVT
|
||||
до gzip — близко к гейту M-8 (200 KB). Если на реальных данных
|
||||
получится > 200 KB — снизить `limit` до 1000 в дев-итерации.
|
||||
- min_length 10 км отсекает короткие тестовые трассы — они
|
||||
визуально не различимы на z5.
|
||||
|
||||
### REQ-F-04 — Backend: tier для tolerance в `_simplify_coords`
|
||||
|
||||
Файл `src/api/gps_tracks/mvt.py`, функция `_simplify_coords`,
|
||||
заменить блок выбора tolerance на:
|
||||
|
||||
```python
|
||||
if z >= 12:
|
||||
return coords
|
||||
elif z >= 10:
|
||||
tolerance = 0.0005 # ~50 м
|
||||
elif z >= 8:
|
||||
tolerance = 0.002 # ~200 м
|
||||
elif z == 7:
|
||||
tolerance = 0.008 # ~800 м (как сейчас для z<=7)
|
||||
elif z == 6:
|
||||
tolerance = 0.018 # ~2 км
|
||||
else:
|
||||
tolerance = 0.04 # ~4 км (z5 и ниже)
|
||||
```
|
||||
|
||||
Замечание. `tolerance` — в градусах долготы; на 55° с.ш. её
|
||||
эквивалент по расстоянию = `tolerance * 64 км`. Для z5 на пиксель
|
||||
карты приходится ≈ 5 км по долготе на 55° с.ш., так что 4 км
|
||||
tolerance даёт «1 точка на пиксель» — оптимум.
|
||||
|
||||
### REQ-F-05 — Frontend: line-width для основного MVT-слоя на z5
|
||||
|
||||
Файл `src/web/gps_tracks.js`, функция `_gpsLayerDef`, выражение
|
||||
`line-width`:
|
||||
```js
|
||||
'line-width': ['interpolate', ['linear'], ['zoom'], 8, 1.0, 12, 2.0, 16, 3.0],
|
||||
```
|
||||
заменить на
|
||||
```js
|
||||
'line-width': ['interpolate', ['linear'], ['zoom'],
|
||||
5, 0.8,
|
||||
8, 1.0,
|
||||
12, 2.0,
|
||||
16, 3.0],
|
||||
```
|
||||
|
||||
Stop на z5 = 0.8 px подобран так, чтобы на 1× и 2×-DPR дисплеях
|
||||
линия гарантированно занимала ≥ 1 физический пиксель (с округлением
|
||||
GPU). На retina (3×) — 2.4 пикселя, видимо.
|
||||
|
||||
### REQ-F-06 — Frontend: line-width для halo на z5
|
||||
|
||||
Файл `src/web/gps_tracks.js`, функция `_gpsHaloDef`, выражение
|
||||
`line-width`:
|
||||
```js
|
||||
'line-width': ['interpolate', ['linear'], ['zoom'], 8, 2.5, 12, 4.0, 16, 6.0],
|
||||
```
|
||||
заменить на
|
||||
```js
|
||||
'line-width': ['interpolate', ['linear'], ['zoom'],
|
||||
5, 1.8,
|
||||
8, 2.5,
|
||||
12, 4.0,
|
||||
16, 6.0],
|
||||
```
|
||||
|
||||
Halo на z5 = 1.8 px — белый ореол не должен «съедать» линию
|
||||
толщиной 0.8 px. Соотношение ~2.25× оставляет халобакс по 0.5 px с каждой стороны.
|
||||
|
||||
### REQ-F-07 — Frontend: zoom-hint «Зум 5+»
|
||||
|
||||
Файл `src/web/index.html`, строка
|
||||
```html
|
||||
<span class="terrain-hint" id="public-tracks-zoom-hint" style="display:none">Зум 8+</span>
|
||||
```
|
||||
заменить на
|
||||
```html
|
||||
<span class="terrain-hint" id="public-tracks-zoom-hint" style="display:none">Зум 5+</span>
|
||||
```
|
||||
|
||||
В `_syncGpsLayersVisibility` (gps_tracks.js, строка ~358-362) логика
|
||||
```js
|
||||
hint.style.display = (enabled && zoom < GPS_TRACKS_MIN_ZOOM) ? 'inline' : 'none';
|
||||
```
|
||||
**не меняется** — она автоматически подхватит новый порог.
|
||||
|
||||
**Замечание.** При z < 5 (фактически только z=0..4) hint всё ещё
|
||||
появится, что и желательно: у пользователя есть подсказка, в каких
|
||||
случаях линий нет «по дизайну».
|
||||
|
||||
### REQ-F-08 — Endpoint без изменений
|
||||
|
||||
`src/api/gps_tracks/endpoint.py:get_gps_tile` остаётся прежним:
|
||||
|
||||
- Валидация `0 ≤ z ≤ 22` уже корректно пропускает z=5..7.
|
||||
- Buffer 10 % bbox остаётся (для z≤6 это формально излишне,
|
||||
но не вредит — соседние тайлы кэшируются независимо).
|
||||
- LRU-кэш `_gps_tile_cache` размером 1024 остаётся.
|
||||
|
||||
Никаких новых query-параметров не вводится. Никаких изменений
|
||||
в `/api/gps-tracks?bbox=…` (GeoJSON endpoint) не делаем —
|
||||
z12+ не затрагивается.
|
||||
|
||||
### REQ-F-09 — Unit-тесты zoom-tier в `build_gps_mvt`
|
||||
|
||||
Файл `tests/unit/test_gps_mvt_zoom_tiers.py` (новый или расширение
|
||||
существующего `test_gps_mvt.py`):
|
||||
|
||||
- **UT-Z5-01.** При z=5 и 10 треках, из которых 3 короче 10 км, в
|
||||
итоговом MVT — ≤ 7 features.
|
||||
- **UT-Z5-02.** При z=5 и 2000 треках длиннее 10 км — в MVT не
|
||||
больше `limit=1500` features.
|
||||
- **UT-Z6-01.** При z=6 и треках 3 км и 6 км — в MVT попадает
|
||||
только трек 6 км.
|
||||
- **UT-Z6-02.** При z=6 и 2500 треках длиной ≥ 5 км — в MVT
|
||||
не больше 2000 features.
|
||||
- **UT-Z7-01.** При z=7 поведение совпадает с прежним
|
||||
(min_length=2000, limit=3000). Регрессия.
|
||||
- **UT-Z8-01.** При z=8 поведение совпадает с прежним
|
||||
(min_length=0, limit=8000). Регрессия.
|
||||
- **UT-Z12-01.** При z=12 поведение совпадает с прежним
|
||||
(limit=25000). Регрессия.
|
||||
|
||||
### REQ-F-10 — Unit-тесты `_simplify_coords` для новых тиров
|
||||
|
||||
Файл `tests/unit/test_gps_mvt_simplify.py` (новый или расширение):
|
||||
|
||||
- **UT-SIMP-Z5-01.** Прямой трек 100 точек, диапазон ≈ 0.1° по широте/долготе:
|
||||
при z=5 — возвращает ≤ 5 точек (DP с большим tolerance
|
||||
схлопывает почти прямую).
|
||||
- **UT-SIMP-Z5-02.** Зигзаг 100 точек, амплитуда зигзагов 0.01°
|
||||
(≈ 1 км): при z=5 (tolerance ~4 км) — возвращает 2 точки
|
||||
(зигзаги меньше tolerance, остаются только концы).
|
||||
- **UT-SIMP-Z6-01.** Тот же зигзаг 100 точек, амплитуда 0.05° (~5 км):
|
||||
при z=6 (tolerance ~2 км) — возвращает > 5 точек (видны
|
||||
крупные зигзаги).
|
||||
- **UT-SIMP-Z7-01.** Регрессия: при z=7 tolerance = 0.008,
|
||||
поведение прежнее.
|
||||
- **UT-SIMP-Z10-01.** Регрессия: при z=10 tolerance = 0.0005,
|
||||
поведение прежнее.
|
||||
- **UT-SIMP-Z12-01.** Регрессия: при z=12 функция возвращает
|
||||
оригинальный coords без изменений.
|
||||
|
||||
### REQ-F-11 — Integration-тесты endpoint z5-z7
|
||||
|
||||
Файл `tests/integration/test_gps_tile_z5_z7.py` (новый):
|
||||
|
||||
- **IT-Z5-01.** На тестовой БД с 50 треками ≥ 10 км по ЦФО
|
||||
запрос `GET /api/gps-tracks/tiles/5/19/9.mvt` (тайл, накрывающий
|
||||
Москву): возвращает 200, Content-Type `application/x-protobuf`,
|
||||
тело длиной > 0 и < 200 KB (M-8).
|
||||
- **IT-Z5-02.** Размер MVT для того же тайла на БД из 200 треков
|
||||
≥ 10 км — ≤ 200 KB.
|
||||
- **IT-Z5-03.** Тайл z=5 за пределами региона (например, центр
|
||||
Тихого океана `tiles/5/4/12.mvt`): тело пустое, ответ 200.
|
||||
- **IT-Z6-01.** Тайл z=6 над Москвой: размер < 200 KB,
|
||||
features > IT-Z5-01.
|
||||
- **IT-Z7-01.** Тайл z=7 над Москвой: features > IT-Z6-01 (более
|
||||
мелкие треки попадают в фильтр), но всё ещё < `limit=3000`.
|
||||
- **IT-CACHE-01.** Два подряд запроса одного тайла z=5: второй
|
||||
возвращает заголовок `X-Cache: HIT`.
|
||||
|
||||
### REQ-F-12 — Регрессионный тест: контракт endpoint не сломался
|
||||
|
||||
- **IT-REGRESS-Z8-01.** Endpoint `/api/gps-tracks/tiles/8/x/y.mvt`
|
||||
возвращает тот же набор треков, что и до ET-012 (sanity-check
|
||||
через сравнение `mapbox_vector_tile.decode(body)['gps_tracks']['features']`
|
||||
до и после; допустимо различие только в порядке).
|
||||
- **IT-REGRESS-Z10-01.** Аналогично для z=10.
|
||||
|
||||
### REQ-F-13 — Производительность: бенчмарк MVT z5
|
||||
|
||||
Файл `tests/performance/test_gps_mvt_z5_perf.py` (новый,
|
||||
помечается маркером `@pytest.mark.perf`):
|
||||
|
||||
- **PERF-Z5-01.** При тестовой БД из 500 треков по ЦФО и
|
||||
10 повторных вызовах `build_gps_mvt(rows, 5, 19, 9)`:
|
||||
- среднее время выполнения ≤ 200 мс на CI-runner.
|
||||
- 95-й перцентиль ≤ 500 мс (метрика M-6).
|
||||
|
||||
Запуск отдельный (`pytest -m perf`), не в основной CI-gate.
|
||||
Цель — раз-в-релиз проверять, что мы не уплыли.
|
||||
|
||||
### REQ-F-14 — UI-тесты (Playwright)
|
||||
|
||||
См. `04b-ui-test-cases.md`. Ключевые проверки:
|
||||
|
||||
- TC-UI-01-Z5: при `zoom = 5` слой виден.
|
||||
- TC-UI-02-Z6: при `zoom = 6` слой виден.
|
||||
- TC-UI-03-Z7: при `zoom = 7` слой виден.
|
||||
- TC-UI-04-HINT-OFF: hint «Зум 5+» **не** показывается при `zoom ≥ 5`.
|
||||
- TC-UI-05-HINT-ON: hint показывается при `zoom < 5`.
|
||||
- TC-UI-06-FILTER-Z6: фильтр источников работает на z6 (регрессия).
|
||||
- TC-UI-07-POPUP-Z6: клик по треку на z6 открывает popup.
|
||||
- TC-UI-08-Z11-REGRESS: на z11 слой по-прежнему виден (регрессия).
|
||||
- TC-UI-09-Z12-CUTOFF: на z12 MVT-слой скрыт, GeoJSON-слой виден.
|
||||
- TC-UI-10-Z5-MOBILE: на мобильном при z5 слой виден.
|
||||
- TC-UI-11-Z5-SAT: на z5 со спутниковой подложкой halo не «глушит» подложку.
|
||||
- TC-UI-12-Z5-Q: качественная проверка читаемости на z5.
|
||||
|
||||
### REQ-F-15 — Не менять контракт `/api/gps-tracks*`
|
||||
|
||||
Никаких новых query-параметров, заголовков, кодов ответа,
|
||||
полей в JSON. `/health` endpoint не меняется.
|
||||
|
||||
### REQ-F-16 — Не менять конфиги
|
||||
|
||||
`config/gps_sources.yaml`, `config/gps_regions.yaml`,
|
||||
миграции БД — без изменений.
|
||||
|
||||
### REQ-F-17 — Не менять стили карты
|
||||
|
||||
`src/web/style.json` и `src/web/style-dark.json` — без изменений.
|
||||
Color-by-source / color-by-activity match-expressions внутри
|
||||
`_buildColorExpression` в коде клиента — без изменений (треки
|
||||
на z5-z7 будут окрашены теми же цветами).
|
||||
|
||||
### REQ-F-18 — localStorage без миграции
|
||||
|
||||
Текущий слой использует ключи `gps-tracks-enabled`,
|
||||
`gps-tracks-activities`, `gps-tracks-sources`, `gps-tracks-color-mode`.
|
||||
ET-012 не вводит новых ключей и не меняет существующие. Существующие
|
||||
пользователи увидят треки на z5-z7 при следующей загрузке без потери
|
||||
выбранных фильтров.
|
||||
|
||||
### REQ-F-19 — Деплой и валидация
|
||||
|
||||
После merge в `main` и деплоя в test-среду:
|
||||
|
||||
1. Открыть `https://openclaw.mva154.duckdns.org/enduro/`,
|
||||
включить «Публичные треки», установить `zoom = 5`
|
||||
(через DevTools `window._map.setZoom(5)`), убедиться, что
|
||||
линии видны.
|
||||
2. Снять профайл DevTools Network: размер запроса
|
||||
`/api/gps-tracks/tiles/5/19/9.mvt` ≤ 200 KB.
|
||||
3. Проверить три тайла z=5 над разными регионами (Москва, Урал,
|
||||
Сибирь) — все ≤ 200 KB и тело > 0 для регионов с треками.
|
||||
4. Зафиксировать результаты в `14-deploy-log.md`.
|
||||
|
||||
### REQ-F-20 — Документация
|
||||
|
||||
В `docs/work-items/ET-012/` после Анализа существуют:
|
||||
- `00-business-request.md` (есть)
|
||||
- `01-brd.md`
|
||||
- `02-trz.md` (этот файл)
|
||||
- `03-acceptance-criteria.md`
|
||||
- `04-test-plan.yaml`
|
||||
- `04b-ui-test-cases.md`
|
||||
|
||||
После реализации добавляются: `10-tech-risks.md` (опционально),
|
||||
`12-review.md`, `13-test-report.md`, `14-deploy-log.md`.
|
||||
|
||||
## 4. Не-функциональные требования
|
||||
|
||||
### NFR-01 — Производительность сервера
|
||||
|
||||
- p95 `build_gps_mvt` на z=5 при БД 500 треков ≤ 500 мс на CI-runner
|
||||
(метрика M-6).
|
||||
- p95 endpoint `/api/gps-tracks/tiles/{5..7}/x/y.mvt` cold ≤ 700 мс,
|
||||
hit ≤ 50 мс (M-7).
|
||||
- Не более 10 SQLite-запросов на тайл (в идеале — 2: COUNT + SELECT).
|
||||
|
||||
### NFR-02 — Производительность клиента
|
||||
|
||||
- На z5 рендер слоя не дольше +30 мс по сравнению с состоянием
|
||||
слой-выключен (замер через MapLibre `map.on('render')` интервал).
|
||||
- Не вызывает frame-drop ниже 30 FPS на средне-мобильном устройстве
|
||||
(iPhone 12 / Pixel 5 эквивалент).
|
||||
|
||||
### NFR-03 — Сетевой трафик
|
||||
|
||||
- Размер одного MVT-тайла z=5 ≤ 200 KB до gzip (метрика M-8).
|
||||
- gzip-compression на nginx даёт обычно ×3-4 по тайлам — финальный
|
||||
трафик 50-70 KB на тайл.
|
||||
|
||||
### NFR-04 — Кэширование
|
||||
|
||||
- LRU размер `_GPS_TILE_CACHE_MAX = 1024` — не меняем.
|
||||
Опциональное увеличение до 2048 — на усмотрение разработчика,
|
||||
если в `PERF-Z5-01` обнаружится частая инвалидация.
|
||||
|
||||
### NFR-05 — Безопасность
|
||||
|
||||
Никаких изменений в auth / CSP / валидации входных данных
|
||||
ET-012 не вносит.
|
||||
|
||||
### NFR-06 — Совместимость
|
||||
|
||||
- API контракт `/api/gps-tracks*` не меняется → старые клиенты
|
||||
работают без обновления.
|
||||
- Существующие browser-tabs с открытой картой при следующей загрузке
|
||||
получат новые лимиты автоматически (никакой миграции
|
||||
localStorage не нужно).
|
||||
|
||||
### NFR-07 — Логирование
|
||||
|
||||
Никаких новых лог-сообщений. Существующее логирование
|
||||
endpoint `gps_tile` (через `uvicorn.access`) показывает зум, x, y, размер ответа — это достаточно.
|
||||
|
||||
## 5. План работ (для разработчика)
|
||||
|
||||
1. **Backend: расширить `build_gps_mvt` tier-таблицу** (REQ-F-03).
|
||||
2. **Backend: расширить `_simplify_coords` tier-таблицу** (REQ-F-04).
|
||||
3. **Unit-тесты zoom-tier и simplify** (REQ-F-09, F-10).
|
||||
4. **Integration-тесты endpoint z5-z7** (REQ-F-11, F-12).
|
||||
5. **Performance-тест PERF-Z5-01** (REQ-F-13). Если не проходит —
|
||||
ужесточить `limit` в REQ-F-03.
|
||||
6. **Frontend: понизить `GPS_TRACKS_MIN_ZOOM` до 5** (REQ-F-01).
|
||||
7. **Frontend: line-width stops для z5** в основном слое и halo
|
||||
(REQ-F-05, F-06).
|
||||
8. **Frontend: текст hint** (REQ-F-07).
|
||||
9. **Прогон `make lint`, `make test`.**
|
||||
10. **Code review → merge → deploy в test.**
|
||||
11. **Ручная проверка REQ-F-19.**
|
||||
12. **Прогон UI-тестов** по `04b-ui-test-cases.md`.
|
||||
13. **Запись результатов** в `13-test-report.md` и `14-deploy-log.md`.
|
||||
|
||||
## 6. Открытые вопросы и решения по умолчанию
|
||||
|
||||
| Вопрос | Решение по умолчанию |
|
||||
| ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Опускать ли порог ещё ниже (z3-z4)? | **Нет.** На z3-z4 даже 10-км треки превращаются в точку — нужна heat-map. Это отдельный work item. |
|
||||
| Увеличить ли `_GPS_TILE_CACHE_MAX`? | **Нет в MVP.** Текущие 1024 покрывают z5..z11. Только если PERF-Z5-01 покажет деградацию. |
|
||||
| Уменьшать ли buffer endpoint'а до 5 % для z≤6? | **Нет в MVP.** 10 % буфер на z5-тайле в большинстве регионов не критичен (≈ 100 км запас в bbox-запросе вместо 1250). Можно вернуться, если PERF-Z5-01 не пройдёт. |
|
||||
| Делать ли разные tier для color-by-source vs color-by-activity на z5? | **Нет.** Геометрия одна, цвет — runtime-выражение MapLibre, не зависит от tier. |
|
||||
| Что показывать пользователю на z3-z4? | Hint «Зум 5+» (REQ-F-07) даёт явное объяснение. Heat-map — отдельный work item. |
|
||||
| Сохранять ли поведение «слой пуст, но включён» через localStorage на z<5? | **Да** — чекбокс остаётся checked, hint объясняет, что нужно зумить. Логика уже есть в `_syncGpsLayersVisibility`. |
|
||||
| Сразу прогружать MVT z5 при включении слоя, если карта на z2? | **Нет.** Source.minzoom=5 защищает: тайлы не запрашиваются до z≥5. Не меняем. |
|
||||
| Менять ли LRU FIFO на настоящий LRU? | **Нет в MVP.** При работе с 10-20 тайлами в кадре FIFO эквивалентен LRU; разница только при больших кэшах. |
|
||||
214
docs/work-items/ET-012/03-acceptance-criteria.md
Normal file
214
docs/work-items/ET-012/03-acceptance-criteria.md
Normal file
@@ -0,0 +1,214 @@
|
||||
---
|
||||
type: acceptance-criteria
|
||||
work_item_id: ET-012
|
||||
title: "Acceptance Criteria: Показывать пользовательские треки с зума z5"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-06-04
|
||||
updated_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
---
|
||||
|
||||
# Acceptance Criteria — ET-012
|
||||
|
||||
Критерии в Gherkin-стиле. Все — обязательные. Задача считается
|
||||
принятой, когда каждый критерий прошёл проверку (автоматическую
|
||||
в CI или ручную в test-среде).
|
||||
|
||||
## AC-01 — Константа `GPS_TRACKS_MIN_ZOOM` понижена до 5
|
||||
|
||||
**Given** ветка `feature/ET-012-z5-z8` с правками
|
||||
**When** проверяется код
|
||||
**Then**:
|
||||
- В `src/web/gps_tracks.js` есть ровно одно объявление
|
||||
`const GPS_TRACKS_MIN_ZOOM = 5;` (с возможным trailing comment).
|
||||
- `grep -R "GPS_TRACKS_MIN_ZOOM" src/web/` не находит других значений,
|
||||
кроме `5`.
|
||||
|
||||
## AC-02 — Vector-source `gps-tracks-tiles` имеет minzoom=5
|
||||
|
||||
**Given** test-среда после деплоя ET-012
|
||||
**When** в DevTools выполнить
|
||||
```js
|
||||
window._map.getSource('gps-tracks-tiles').minzoom
|
||||
```
|
||||
**Then** результат `5`.
|
||||
|
||||
## AC-03 — При z=5 слой публичных треков виден
|
||||
|
||||
**Given** пользователь на `https://openclaw.mva154.duckdns.org/enduro/`,
|
||||
включён чекбокс «Публичные треки», БД содержит ≥ 50 треков по ЦФО
|
||||
длиннее 10 км
|
||||
**When** установить `zoom = 5` (через DevTools или панорамированием)
|
||||
и центр карты над ЦФО
|
||||
**Then**:
|
||||
- На карте видны линии треков (визуально — не менее 3 различимых
|
||||
линий в кадре).
|
||||
- `window._map.getLayoutProperty('gps-tracks-layer-mvt', 'visibility') === 'visible'`.
|
||||
- Hint `#public-tracks-zoom-hint` имеет `display: none`.
|
||||
|
||||
## AC-04 — При z=6 и z=7 слой публичных треков виден
|
||||
|
||||
Аналогично AC-03 для z=6 (lim min_length = 5 км) и z=7
|
||||
(min_length = 2 км). Количество видимых линий в кадре ≥ AC-03.
|
||||
|
||||
## AC-05 — Hint «Зум 5+» появляется при z<5
|
||||
|
||||
**Given** включён чекбокс «Публичные треки»
|
||||
**When** установить `zoom = 4`
|
||||
**Then**:
|
||||
- Hint `#public-tracks-zoom-hint` имеет `display: inline` (или иное
|
||||
ненулевое отображение).
|
||||
- Текст hint'а — «Зум 5+».
|
||||
- На карте нет линий публичных треков (vector-source не запрашивает
|
||||
тайлы при `zoom < source.minzoom`).
|
||||
|
||||
## AC-06 — Регрессия z8-z11: слой работает как прежде
|
||||
|
||||
**Given** ветка после ET-012
|
||||
**When** установить `zoom = 8, 9, 10, 11` поочерёдно
|
||||
**Then**:
|
||||
- На каждом зуме слой `gps-tracks-layer-mvt` имеет `visibility: visible`.
|
||||
- Набор отображаемых треков не уже, чем до ET-012 (за вычетом того,
|
||||
что в z=8 включаются ВСЕ треки независимо от длины, как было).
|
||||
- Запросы `/api/gps-tracks/tiles/{z}/x/y.mvt` возвращают 200.
|
||||
|
||||
## AC-07 — Регрессия z12+: GeoJSON-слой работает как прежде
|
||||
|
||||
**Given** включён чекбокс
|
||||
**When** установить `zoom = 12, 13, 14, 15`
|
||||
**Then**:
|
||||
- `gps-tracks-layer-mvt` имеет `visibility: none`.
|
||||
- `gps-tracks-layer-geo` имеет `visibility: visible`.
|
||||
- На карте видны те же треки, что и до ET-012.
|
||||
|
||||
## AC-08 — Читаемость карты на z5 (качественный критерий)
|
||||
|
||||
**Given** test-среда с ≥ 200 треками по ЦФО (после E2E-PROD-01/02 из ET-009)
|
||||
**When** скриншот при `zoom = 5`, центр над Москвой
|
||||
**Then**:
|
||||
- На скриншоте `et012-z5-readable.png` видны минимум 3 разных
|
||||
«нити» в разных квадрантах кадра.
|
||||
- Нет «сплошной заливки» одной зоны (треки не сливаются в кашу).
|
||||
- Допустимо отличать «нить» как любую видимую линию длиной ≥ 20 px
|
||||
в кадре.
|
||||
|
||||
Проверка ручная по скриншоту в `13-test-report.md`.
|
||||
|
||||
## AC-09 — Производительность endpoint z=5 в test-среде
|
||||
|
||||
**Given** test-среда
|
||||
**When** 10 раз подряд `curl -w '%{time_total}\n' -o /dev/null
|
||||
"https://openclaw.mva154.duckdns.org/enduro/api/gps-tracks/tiles/5/19/9.mvt"`,
|
||||
последовательно (первый — cold, последующие — cache hits)
|
||||
**Then**:
|
||||
- Cold-запрос ≤ 1.5 с (M-7 c запасом для сети).
|
||||
- Median последующих ≤ 200 мс (cache hit).
|
||||
- HTTP 200 на каждый запрос.
|
||||
- Размер тела ≤ 200 KB (после gzip-decompression).
|
||||
|
||||
## AC-10 — Размер MVT-тайла z=5 не превышает 200 KB
|
||||
|
||||
**Given** test-среда
|
||||
**When** скачать тайл `tiles/5/19/9.mvt` (Москва) и `tiles/5/20/9.mvt`
|
||||
(восток ЦФО)
|
||||
**Then** размер тела ≤ 200 KB для каждого.
|
||||
|
||||
## AC-11 — Unit-тесты zoom-tier зелёные
|
||||
|
||||
**Given** ветка
|
||||
**When** `pytest tests/unit/test_gps_mvt_zoom_tiers.py -v`
|
||||
**Then** все UT-Z5-*, UT-Z6-*, UT-Z7-*, UT-Z8-*, UT-Z12-* проходят.
|
||||
|
||||
## AC-12 — Unit-тесты simplify зелёные
|
||||
|
||||
**Given** ветка
|
||||
**When** `pytest tests/unit/test_gps_mvt_simplify.py -v`
|
||||
**Then** все UT-SIMP-Z5-*, UT-SIMP-Z6-*, UT-SIMP-Z7-*, UT-SIMP-Z10-*,
|
||||
UT-SIMP-Z12-* проходят.
|
||||
|
||||
## AC-13 — Integration-тесты endpoint z5-z7 зелёные
|
||||
|
||||
**Given** ветка
|
||||
**When** `pytest tests/integration/test_gps_tile_z5_z7.py -v`
|
||||
**Then** все IT-Z5-*, IT-Z6-*, IT-Z7-*, IT-CACHE-* проходят.
|
||||
|
||||
## AC-14 — Регрессионные тесты ET-008/ET-009 зелёные
|
||||
|
||||
**Given** ветка
|
||||
**When** `pytest tests/unit/ tests/integration/ -v` (исключая perf-маркер)
|
||||
**Then** все существующие тесты ET-008 (U-01..U-62 / I-01..I-57)
|
||||
и ET-009 (UT-ER-*, UT-WL-*, UT-CFG-*, IT-*) проходят без регрессий.
|
||||
|
||||
## AC-15 — Регрессия фильтров на z6
|
||||
|
||||
**Given** включён слой, на карте `zoom = 6`, видны треки трёх
|
||||
источников (osm/enduro_russia/wikiloc)
|
||||
**When** пользователь открывает `#sheet-gps-filters` и снимает галку
|
||||
«EnduroRussia»
|
||||
**Then** через ≤ 1.5 с (с учётом инвалидации MVT тайлов через
|
||||
`map.setFilter`) с карты исчезают линии цвета EnduroRussia,
|
||||
остальные остаются.
|
||||
|
||||
## AC-16 — Регрессия popup на z6
|
||||
|
||||
**Given** включён слой, на карте `zoom = 6` или `7`, в кадре есть
|
||||
длинный (≥ 10 км) трек
|
||||
**When** пользователь кликает по линии трека
|
||||
**Then**:
|
||||
- Открывается popup `.track-popup` с названием, активностью, длиной,
|
||||
источниками.
|
||||
- Если трек из источника `osm` — в popup'е есть кнопка «Скачать GPX»
|
||||
(`.track-popup-download-btn`).
|
||||
- Клик по кнопке скачивает GPX-файл (контракт ET-011 не нарушен).
|
||||
|
||||
## AC-17 — Halo на спутнике на z5 виден, но не «глушит» подложку
|
||||
|
||||
**Given** включён слой, переключена базовая подложка на спутник
|
||||
(`#base-btn-satellite`), `zoom = 5`
|
||||
**When** скриншот
|
||||
**Then**:
|
||||
- Линии видны на тёмной спутниковой подложке (благодаря halo).
|
||||
- Halo-width ≤ 2 px (т.е. ореол не превращается в «пузырь»).
|
||||
- `gps-tracks-halo-mvt-satellite.visibility === 'visible'`.
|
||||
|
||||
## AC-18 — Поведение на мобильном (375×667 viewport)
|
||||
|
||||
**Given** Playwright mobile viewport, включён слой, `zoom = 5`
|
||||
**When** скриншот
|
||||
**Then**:
|
||||
- Линии видны.
|
||||
- Толщина линии по «зрительному ощущению» ≥ 1 пикселя.
|
||||
- Hint скрыт.
|
||||
|
||||
## AC-19 — Performance-test PERF-Z5-01
|
||||
|
||||
**Given** ветка
|
||||
**When** `pytest -m perf tests/performance/test_gps_mvt_z5_perf.py -v`
|
||||
**Then**:
|
||||
- PERF-Z5-01 проходит: avg ≤ 200 мс, p95 ≤ 500 мс на CI-runner
|
||||
при БД 500 треков.
|
||||
|
||||
(Этот тест запускается отдельным джобом / pre-merge gate.)
|
||||
|
||||
## AC-20 — Документация work item полная
|
||||
|
||||
**Given** репо после слияния ET-012
|
||||
**When** проверка `docs/work-items/ET-012/`
|
||||
**Then** существуют:
|
||||
- `00-business-request.md`
|
||||
- `01-brd.md`
|
||||
- `02-trz.md`
|
||||
- `03-acceptance-criteria.md`
|
||||
- `04-test-plan.yaml`
|
||||
- `04b-ui-test-cases.md`
|
||||
- `12-review.md` (после Review)
|
||||
- `13-test-report.md` (после Тестирования)
|
||||
- `14-deploy-log.md` (после Деплоя)
|
||||
|
||||
## AC-21 — `make lint` и `make test` зелёные
|
||||
|
||||
**Given** ветка
|
||||
**When** `make lint` и `make test`
|
||||
**Then** обе команды exit-code 0.
|
||||
401
docs/work-items/ET-012/04-test-plan.yaml
Normal file
401
docs/work-items/ET-012/04-test-plan.yaml
Normal file
@@ -0,0 +1,401 @@
|
||||
---
|
||||
type: test-plan
|
||||
work_item_id: ET-012
|
||||
title: "Test Plan: Показывать пользовательские треки с зума z5"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-06-04
|
||||
updated_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
related:
|
||||
- "ET-008"
|
||||
- "ET-009"
|
||||
- "ET-011"
|
||||
|
||||
scope_note: >
|
||||
ET-012 опускает порог видимости слоя публичных GPS-треков с z8 до z5.
|
||||
Изменения локализованы:
|
||||
- backend mvt.py: zoom-tier для z5/z6 (min_length, limit, tolerance);
|
||||
- frontend gps_tracks.js: константа GPS_TRACKS_MIN_ZOOM=5,
|
||||
line-width stops для z5 в основном слое и halo;
|
||||
- index.html: текст hint «Зум 5+».
|
||||
Тест-план фокусируется на:
|
||||
(1) корректности новых zoom-tier'ов в build_gps_mvt и _simplify_coords;
|
||||
(2) что endpoint отдаёт нормально-размерные MVT на z5-z7;
|
||||
(3) что клиент действительно показывает слой на z5-z7;
|
||||
(4) что регрессий ET-008/009/011 нет;
|
||||
(5) что производительность не уплыла.
|
||||
|
||||
test_suites:
|
||||
|
||||
- name: unit-mvt-zoom-tiers
|
||||
type: unit
|
||||
description: "Тиры min_length_m и limit в build_gps_mvt по зумам"
|
||||
cases:
|
||||
- id: UT-Z5-01
|
||||
name: "z=5: треки < 10 км отфильтровываются"
|
||||
input: |
|
||||
Mock rows: 10 треков, длина = [500, 2000, 3000, 8000, 12000, 15000, 25000, 50000, 80000, 120000].
|
||||
Вызов build_gps_mvt(rows, z=5, x=19, y=9).
|
||||
expected: |
|
||||
В MVT попадают только треки длиной >= 10000 м, т.е. ровно 6 features.
|
||||
|
||||
- id: UT-Z5-02
|
||||
name: "z=5: limit=1500"
|
||||
input: |
|
||||
Mock rows: 2000 треков длиной 15 км каждый (все пройдут min_length).
|
||||
expected: |
|
||||
В MVT попадают первые 1500 features, остальные отбрасываются.
|
||||
|
||||
- id: UT-Z6-01
|
||||
name: "z=6: треки < 5 км отфильтровываются"
|
||||
input: |
|
||||
Mock rows: 5 треков, длина = [1000, 3000, 5000, 7000, 10000].
|
||||
expected: |
|
||||
В MVT 3 features (5000, 7000, 10000).
|
||||
|
||||
- id: UT-Z6-02
|
||||
name: "z=6: limit=2000"
|
||||
input: |
|
||||
Mock rows: 2500 треков длиной 6 км каждый.
|
||||
expected: |
|
||||
В MVT 2000 features.
|
||||
|
||||
- id: UT-Z7-01
|
||||
name: "z=7: регрессия — поведение до ET-012"
|
||||
input: |
|
||||
Mock rows: 4 трека [1000, 2000, 3000, 5000].
|
||||
expected: |
|
||||
В MVT 3 features (2000, 3000, 5000), как раньше.
|
||||
|
||||
- id: UT-Z8-01
|
||||
name: "z=8: регрессия — нет min_length-фильтра"
|
||||
input: |
|
||||
Mock rows: 4 трека [500, 1000, 2000, 5000].
|
||||
expected: |
|
||||
В MVT 4 features, limit=8000.
|
||||
|
||||
- id: UT-Z12-01
|
||||
name: "z=12: регрессия — limit=25000, без min_length"
|
||||
input: |
|
||||
Mock rows: 100 треков любой длины.
|
||||
expected: |
|
||||
В MVT 100 features.
|
||||
|
||||
- name: unit-mvt-simplify
|
||||
type: unit
|
||||
description: "Tolerance Douglas-Peucker по зумам в _simplify_coords"
|
||||
cases:
|
||||
- id: UT-SIMP-Z5-01
|
||||
name: "z=5: прямая линия 100 точек → ≤ 5 точек"
|
||||
input: |
|
||||
coords = [(37.0 + i*0.001, 55.0 + i*0.001) for i in range(100)]
|
||||
(приблизительно прямая ~10 км по диагонали)
|
||||
expected: |
|
||||
len(_simplify_coords(coords, 5)) <= 5
|
||||
|
||||
- id: UT-SIMP-Z5-02
|
||||
name: "z=5: зигзаг с амплитудой < tolerance → 2 точки"
|
||||
input: |
|
||||
coords = зигзаг 100 точек, амплитуда 0.01° (~1 км)
|
||||
expected: |
|
||||
len(_simplify_coords(coords, 5)) == 2 (только концы)
|
||||
|
||||
- id: UT-SIMP-Z6-01
|
||||
name: "z=6: зигзаг 5 км → видны крупные пики"
|
||||
input: |
|
||||
coords = зигзаг 100 точек, амплитуда 0.05° (~5 км)
|
||||
expected: |
|
||||
len(_simplify_coords(coords, 6)) > 5
|
||||
|
||||
- id: UT-SIMP-Z7-01
|
||||
name: "z=7: регрессия — tolerance = 0.008"
|
||||
input: |
|
||||
coords = зигзаг 100 точек, амплитуда 0.005° (~500 м)
|
||||
expected: |
|
||||
len(_simplify_coords(coords, 7)) близок к до-ET-012 значению
|
||||
(округлённо в пределах +/-1).
|
||||
|
||||
- id: UT-SIMP-Z10-01
|
||||
name: "z=10: регрессия — tolerance = 0.0005"
|
||||
input: |
|
||||
coords = зигзаг 100 точек, амплитуда 0.001° (~100 м)
|
||||
expected: |
|
||||
Поведение совпадает с до-ET-012 (контрольный snapshot).
|
||||
|
||||
- id: UT-SIMP-Z12-01
|
||||
name: "z=12: регрессия — без упрощения"
|
||||
input: |
|
||||
coords = 100 точек
|
||||
expected: |
|
||||
_simplify_coords(coords, 12) is coords (или эквивалент)
|
||||
|
||||
- id: UT-SIMP-EDGE-01
|
||||
name: "Слишком мало точек → возвращаем как есть"
|
||||
input: |
|
||||
coords = [(37.0, 55.0), (37.001, 55.001)] (2 точки)
|
||||
expected: |
|
||||
На любом zoom — функция возвращает [(37.0, 55.0), (37.001, 55.001)].
|
||||
|
||||
- id: UT-SIMP-EDGE-02
|
||||
name: "DP схлопнул до < 2 точек → возвращаем оригинал"
|
||||
input: |
|
||||
coords = 100 одинаковых точек (вырожденный трек)
|
||||
expected: |
|
||||
Функция возвращает оригинальный coords, не пустой список.
|
||||
|
||||
- name: integration-tile-endpoint
|
||||
type: integration
|
||||
description: "Endpoint /api/gps-tracks/tiles/{z}/{x}/{y}.mvt на z=5..7"
|
||||
cases:
|
||||
- id: IT-Z5-01
|
||||
name: "Тайл z=5 над Москвой: 200, тело > 0, < 200 KB"
|
||||
input: |
|
||||
Test DB: 50 треков по ЦФО, длина 12..30 км каждый.
|
||||
GET /api/gps-tracks/tiles/5/19/9.mvt
|
||||
expected: |
|
||||
status 200,
|
||||
Content-Type 'application/x-protobuf',
|
||||
0 < len(body) < 200_000
|
||||
|
||||
- id: IT-Z5-02
|
||||
name: "Тайл z=5 с большой БД: limit держит размер"
|
||||
input: |
|
||||
Test DB: 200 треков по ЦФО, длина 12..30 км.
|
||||
GET /api/gps-tracks/tiles/5/19/9.mvt
|
||||
expected: |
|
||||
status 200,
|
||||
len(body) < 200_000,
|
||||
mapbox_vector_tile.decode(body)['gps_tracks']['features'] <= 1500
|
||||
|
||||
- id: IT-Z5-03
|
||||
name: "Тайл z=5 над пустым регионом: пустое тело"
|
||||
input: |
|
||||
Test DB: те же 50 треков по ЦФО.
|
||||
GET /api/gps-tracks/tiles/5/4/12.mvt (Тихий океан)
|
||||
expected: |
|
||||
status 200,
|
||||
len(body) == 0
|
||||
|
||||
- id: IT-Z6-01
|
||||
name: "Тайл z=6 над Москвой: больше фич, чем z=5"
|
||||
input: |
|
||||
Test DB: 100 треков, длина 4..20 км.
|
||||
GET /api/gps-tracks/tiles/6/38/19.mvt
|
||||
expected: |
|
||||
status 200,
|
||||
features_count(z=6) >= features_count(z=5) для того же региона,
|
||||
len(body) < 200_000
|
||||
|
||||
- id: IT-Z7-01
|
||||
name: "Тайл z=7 над Москвой: регрессия + плюс короткие треки"
|
||||
input: |
|
||||
GET /api/gps-tracks/tiles/7/77/39.mvt с теми же 100 треками.
|
||||
expected: |
|
||||
status 200,
|
||||
features_count(z=7) >= features_count(z=6),
|
||||
features_count(z=7) <= 3000
|
||||
|
||||
- id: IT-CACHE-01
|
||||
name: "LRU-кэш: второй запрос — X-Cache: HIT"
|
||||
input: |
|
||||
GET /api/gps-tracks/tiles/5/19/9.mvt дважды подряд.
|
||||
expected: |
|
||||
1-й ответ: header X-Cache: MISS.
|
||||
2-й ответ: header X-Cache: HIT, тело идентично 1-му.
|
||||
|
||||
- id: IT-CACHE-02
|
||||
name: "Сброс кэша через /cache/clear"
|
||||
input: |
|
||||
GET tiles/5/19/9.mvt → POST /api/gps-tracks/cache/clear → GET tiles/5/19/9.mvt
|
||||
expected: |
|
||||
1-й ответ MISS, 2-й (после clear) MISS.
|
||||
|
||||
- id: IT-REGRESS-Z8-01
|
||||
name: "Регрессия z=8: контракт MVT не изменился"
|
||||
input: |
|
||||
GET /api/gps-tracks/tiles/8/154/79.mvt на тестовой БД.
|
||||
(Тайл-координаты выбраны над Москвой.)
|
||||
expected: |
|
||||
features_count(z=8) точно совпадает с snapshot до ET-012
|
||||
(записывается в tests/fixtures/gps-tracks/mvt-z8-snapshot.json).
|
||||
|
||||
- id: IT-REGRESS-Z10-01
|
||||
name: "Регрессия z=10"
|
||||
input: |
|
||||
GET /api/gps-tracks/tiles/10/617/319.mvt
|
||||
expected: |
|
||||
features_count(z=10) совпадает с snapshot до ET-012.
|
||||
|
||||
- id: IT-VALID-01
|
||||
name: "z вне диапазона — 400"
|
||||
input: |
|
||||
GET /api/gps-tracks/tiles/-1/0/0.mvt и tiles/23/0/0.mvt
|
||||
expected: |
|
||||
status 400, detail 'Invalid z'
|
||||
|
||||
- name: integration-api-geojson-cutoff
|
||||
type: integration
|
||||
description: "GeoJSON-слой не изменился"
|
||||
cases:
|
||||
- id: IT-GEO-01
|
||||
name: "GET /api/gps-tracks?bbox=… работает как раньше"
|
||||
input: |
|
||||
GET /api/gps-tracks?bbox=37,55,38,56&limit=500
|
||||
expected: |
|
||||
status 200,
|
||||
FeatureCollection с features, total_in_bbox, returned, truncated —
|
||||
контракт идентичен ET-009.
|
||||
|
||||
- name: performance
|
||||
type: performance
|
||||
description: "Производительность build_gps_mvt на z=5"
|
||||
marker: "@pytest.mark.perf"
|
||||
cases:
|
||||
- id: PERF-Z5-01
|
||||
name: "build_gps_mvt на z=5 при 500 треках"
|
||||
input: |
|
||||
Test DB: 500 треков длиной 12-25 км по ЦФО.
|
||||
10 повторных вызовов build_gps_mvt(rows, 5, 19, 9).
|
||||
expected: |
|
||||
avg time <= 200 ms,
|
||||
p95 time <= 500 ms на CI-runner (метрика M-6).
|
||||
|
||||
- id: PERF-Z5-02
|
||||
name: "build_gps_mvt на z=5 при 5000 треках (стресс)"
|
||||
input: |
|
||||
Test DB: 5000 треков, разные длины.
|
||||
5 повторных вызовов.
|
||||
expected: |
|
||||
p95 time <= 1500 ms.
|
||||
|
||||
- id: PERF-ENDPOINT-01
|
||||
name: "Endpoint p95 на z=5 (cold)"
|
||||
input: |
|
||||
10 cold-запросов tile-endpoint (после cache clear) на test-БД.
|
||||
expected: |
|
||||
p95 <= 700 ms.
|
||||
|
||||
- id: PERF-ENDPOINT-02
|
||||
name: "Endpoint p95 на z=5 (hot, кэш)"
|
||||
input: |
|
||||
100 повторных запросов одного тайла после прогрева.
|
||||
expected: |
|
||||
p95 <= 50 ms.
|
||||
|
||||
- name: regression-existing
|
||||
type: regression
|
||||
description: "Регрессия ET-008 / ET-009 / ET-011"
|
||||
cases:
|
||||
- id: RG-08-01
|
||||
name: "Все unit-тесты ET-008 проходят"
|
||||
input: "pytest tests/unit/test_gps_*.py -v (за исключением новых ET-012)"
|
||||
expected: "exit-code 0"
|
||||
|
||||
- id: RG-09-01
|
||||
name: "Все unit-тесты ET-009 (parser EnduroRussia/Wikiloc)"
|
||||
input: "pytest tests/unit/test_gps_tracks_enduro_russia.py tests/unit/test_gps_tracks_wikiloc.py -v"
|
||||
expected: "exit-code 0"
|
||||
|
||||
- id: RG-11-01
|
||||
name: "Тесты ET-011 download GPX"
|
||||
input: "pytest tests/integration/test_gps_download.py -v"
|
||||
expected: "exit-code 0"
|
||||
|
||||
- id: RG-INT-01
|
||||
name: "Все integration-тесты"
|
||||
input: "pytest tests/integration/ -v"
|
||||
expected: "exit-code 0"
|
||||
|
||||
- name: ui-playwright
|
||||
type: ui
|
||||
description: "Playwright UI-тесты на test-среде"
|
||||
reference: "04b-ui-test-cases.md"
|
||||
cases:
|
||||
- id: UI-LINK-01
|
||||
name: "См. 04b-ui-test-cases.md — TC-UI-01-Z5..TC-UI-12-Z5-Q"
|
||||
expected: "Каждый TC выполняется и check-visual подтверждается оператором."
|
||||
|
||||
- name: manual-deploy-validation
|
||||
type: e2e
|
||||
description: "Ручная проверка в test-среде после деплоя"
|
||||
marker: "manual"
|
||||
cases:
|
||||
- id: E2E-DEPLOY-01
|
||||
name: "Включить слой и поставить zoom=5"
|
||||
steps:
|
||||
- "Открыть https://openclaw.mva154.duckdns.org/enduro/"
|
||||
- "Open DevTools, в Console: localStorage.clear() для чистого старта"
|
||||
- "Click #terrain-toggle"
|
||||
- "Click #public-tracks-cb (включить)"
|
||||
- "В Console: window._map.setZoom(5); window._map.setCenter([37.6, 55.7])"
|
||||
- "Ждать 3 секунды"
|
||||
- "Visual: видны линии публичных треков"
|
||||
- "Зафиксировать скриншот в 14-deploy-log.md"
|
||||
|
||||
- id: E2E-DEPLOY-02
|
||||
name: "Network: размер тайла z=5"
|
||||
steps:
|
||||
- "В DevTools Network отфильтровать по 'tiles/5'"
|
||||
- "Проверить: каждый ответ ≤ 200 KB (Size column)"
|
||||
- "Зафиксировать в 14-deploy-log.md"
|
||||
|
||||
- id: E2E-DEPLOY-03
|
||||
name: "Уменьшить зум до z=4 — hint показывается"
|
||||
steps:
|
||||
- "window._map.setZoom(4)"
|
||||
- "Visual: hint 'Зум 5+' появился"
|
||||
- "На карте нет линий публичных треков"
|
||||
|
||||
- id: E2E-DEPLOY-04
|
||||
name: "Зум z=12 — переход на GeoJSON"
|
||||
steps:
|
||||
- "window._map.setZoom(12)"
|
||||
- "Wait 1.5s"
|
||||
- "В DevTools Network отфильтровать по '/api/gps-tracks?bbox'"
|
||||
- "Запрос ушёл, status 200"
|
||||
- "На карте видны линии, но из GeoJSON-source (gps-tracks-layer-geo)"
|
||||
|
||||
- id: E2E-DEPLOY-05
|
||||
name: "Регрессия: popup и скачивание GPX"
|
||||
steps:
|
||||
- "window._map.setZoom(8)"
|
||||
- "Кликнуть по треку из источника osm"
|
||||
- "Popup открылся, в нём есть кнопка 'Скачать GPX'"
|
||||
- "Клик по кнопке скачивает .gpx файл (ET-011 контракт)"
|
||||
|
||||
test_data:
|
||||
fixtures_dir: "tests/fixtures/gps-tracks/"
|
||||
fixtures:
|
||||
- name: "mvt-z8-snapshot.json"
|
||||
description: "Snapshot число features в тайле z=8/154/79 над Москвой до ET-012 (для IT-REGRESS-Z8-01)"
|
||||
- name: "mvt-z10-snapshot.json"
|
||||
description: "Аналогично для z=10/617/319 (IT-REGRESS-Z10-01)"
|
||||
notes:
|
||||
- "Snapshot'ы создаются разово до начала разработки ET-012 на текущем состоянии test-БД и кладутся в репо."
|
||||
- "Для unit-тестов использовать sqlite3.Row mock — реальная БД не нужна."
|
||||
|
||||
test_environment:
|
||||
unit:
|
||||
- "pytest tmp_path для временной sqlite (по необходимости)"
|
||||
- "Mock sqlite3.Row через unittest.mock или фабрика"
|
||||
integration:
|
||||
- "Test sqlite БД с фикстурными треками из existing ET-008/009 фабрик"
|
||||
- "FastAPI TestClient для endpoint вызовов"
|
||||
performance:
|
||||
- "Маркер @pytest.mark.perf, не в обычном CI"
|
||||
- "Запуск перед merge: pytest -m perf"
|
||||
e2e:
|
||||
- "Test-среда https://openclaw.mva154.duckdns.org/enduro/"
|
||||
- "Реальная БД после ET-009 прогона"
|
||||
- "UI-тесты — см. 04b-ui-test-cases.md (Playwright)"
|
||||
|
||||
ci_gates:
|
||||
- "Unit-тесты UT-Z*-* и UT-SIMP-* — обязательны (AC-11, AC-12)"
|
||||
- "Integration IT-Z*-*, IT-CACHE-*, IT-REGRESS-* — обязательны (AC-13)"
|
||||
- "Регрессия RG-* — обязательна (AC-14)"
|
||||
- "Performance PERF-Z5-01 — обязателен перед merge (AC-19)"
|
||||
- "UI-тесты — запуск после деплоя, фиксация в 13-test-report.md"
|
||||
- "E2E-DEPLOY-* — ручные шаги в 14-deploy-log.md"
|
||||
---
|
||||
375
docs/work-items/ET-012/04b-ui-test-cases.md
Normal file
375
docs/work-items/ET-012/04b-ui-test-cases.md
Normal file
@@ -0,0 +1,375 @@
|
||||
---
|
||||
type: ui-test-cases
|
||||
work_item_id: ET-012
|
||||
title: "UI Test Cases: Публичные треки на z5-z7"
|
||||
version: 1
|
||||
status: draft
|
||||
created_at: 2026-06-04
|
||||
updated_at: 2026-06-04
|
||||
authors:
|
||||
- "agent:analyst"
|
||||
related:
|
||||
- "ET-008"
|
||||
- "ET-009"
|
||||
- "ET-011"
|
||||
---
|
||||
|
||||
# UI Test Cases — ET-012: Публичные треки на зумах z5-z7
|
||||
|
||||
Базовый URL: `https://openclaw.mva154.duckdns.org/enduro/`
|
||||
|
||||
ET-012 не добавляет новых UI-компонентов — только меняет нижний
|
||||
порог видимости слоя публичных треков с z8 до z5 и тонкие настройки
|
||||
толщины линий/халобокса для малых зумов. UI-тесты проверяют, что:
|
||||
|
||||
1. На z5, z6, z7 слой действительно появляется.
|
||||
2. Hint обновлён или скрыт корректно.
|
||||
3. Регрессий ET-008/009/011 нет.
|
||||
4. На спутнике на z5 линии видны и halo не «глушит» подложку.
|
||||
5. На мобильном viewport всё работает.
|
||||
|
||||
Селекторы (унаследованы из ET-008/009/011):
|
||||
|
||||
- `#terrain-toggle` — кнопка попапа слоёв.
|
||||
- `#public-tracks-cb` — чекбокс «Публичные треки».
|
||||
- `#public-tracks-zoom-hint` — hint «Зум 5+».
|
||||
- `#public-tracks-filters-btn` — кнопка «Фильтры…» (видна при включённом слое).
|
||||
- `#sheet-gps-filters` — bottom sheet фильтров.
|
||||
- `#gps-source-grid input[value='osm' | 'enduro_russia' | 'wikiloc']` — чекбоксы.
|
||||
- `#base-btn-satellite` — кнопка спутника.
|
||||
- `.track-popup` / `.track-popup-download-btn` — popup и кнопка скачивания.
|
||||
- `#map` — карта.
|
||||
|
||||
Предусловие для всех тестов: в БД test-среды есть треки всех трёх
|
||||
источников (после E2E-PROD-01/02 из ET-009). Все TC выполняются
|
||||
Playwright'ом против test-среды; check-visual подтверждается
|
||||
оператором или визуальным diff-тулом.
|
||||
|
||||
Особенность ET-012 — каждый сценарий выставляет zoom программно,
|
||||
чтобы не зависеть от перетаскивания карты. Команда:
|
||||
```js
|
||||
window._map.setZoom(N);
|
||||
window._map.setCenter([37.6, 55.7]); // Москва, по умолчанию
|
||||
```
|
||||
выполняется через `page.evaluate(...)`.
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-01-Z5 — На z=5 слой публичных треков виден
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. screenshot: "et012-01-z5-tracks-visible"
|
||||
10. check-visual: "На карте при zoom=5 (виден кусок Восточной Европы / ЦФО) поверх подложки нарисованы линии публичных треков как минимум двух разных цветов (по источнику). Линии тонкие, но различимые на дисплее. Hint #public-tracks-zoom-hint скрыт. Чекбокс #public-tracks-cb включён."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-02-Z6 — На z=6 слой виден, треков больше чем на z5
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(6); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. screenshot: "et012-02-z6-tracks-visible"
|
||||
10. check-visual: "При zoom=6 (виден кусок Центральной России) на карте видно явно больше линий, чем на z5: появляются треки длиной 5-10 км, которые не прошли фильтр z5. Линии лучше различимы (толще). Hint скрыт."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-03-Z7 — На z=7 слой виден, регрессия
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(7); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. screenshot: "et012-03-z7-tracks-visible"
|
||||
10. check-visual: "При zoom=7 видны треки длиной от 2 км и выше (как было до ET-012). На карте — заметная сеть. Поведение должно соответствовать прежнему 'z=8 минус один уровень', но с min_length=2000 (т.е. чуть строже фильтр чем z8). Hint скрыт."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-04-HINT-OFF — Hint «Зум 5+» скрыт при z=5
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 2000
|
||||
7. evaluate: window._map.setZoom(5);
|
||||
8. wait: 1500
|
||||
9. screenshot: "et012-04-hint-off-z5"
|
||||
10. check-visual: "Элемент #public-tracks-zoom-hint имеет display:none (не виден в попапе слоёв). Чекбокс «Публичные треки» включён. Никакой подсказки 'нужно увеличить зум' не показано."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-05-HINT-ON — Hint «Зум 5+» виден при z=4
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 2000
|
||||
7. evaluate: window._map.setZoom(4);
|
||||
8. wait: 1500
|
||||
9. screenshot: "et012-05-hint-on-z4"
|
||||
10. check-visual: "В попапе слоёв (#terrain-popup) рядом с чекбоксом «Публичные треки» виден hint с текстом «Зум 5+». На карте линий публичных треков нет — vector-source не запрашивает тайлы при zoom < minzoom=5."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-06-FILTER-Z6 — Фильтр источников работает на z6
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(6); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. screenshot: "et012-06a-z6-all-sources"
|
||||
10. check-visual: "На z=6 видны треки разных цветов (нескольких источников)."
|
||||
11. click: "#public-tracks-filters-btn"
|
||||
12. wait: 800
|
||||
13. click: "#gps-source-grid input[value='enduro_russia']"
|
||||
14. wait: 1500
|
||||
15. screenshot: "et012-06b-z6-no-enduro-russia"
|
||||
16. check-visual: "Чекбокс EnduroRussia снят. На z=6 линии цвета EnduroRussia (характерный красноватый по дефолтной палитре) исчезли. Линии osm/wikiloc остались. Регрессия фильтра — поведение идентично z=8."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-07-POPUP-Z6 — Popup трека открывается на z6
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(6); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. click: "#map"
|
||||
10. wait: 1500
|
||||
11. screenshot: "et012-07-popup-z6"
|
||||
12. check-visual: "При клике в линию трека (или близко к ней) открылся popup .track-popup с названием, активностью, длиной, списком источников. Если трек из источника osm — внутри есть кнопка .track-popup-download-btn (ET-011 регрессия). Popup корректно позиционирован, не уходит за границы карты."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-08-Z11-REGRESS — На z=11 слой по-прежнему виден (регрессия)
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(11); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. screenshot: "et012-08-z11-regress"
|
||||
10. check-visual: "На zoom=11 слой публичных треков виден; на карте много линий разных цветов; поведение визуально идентично состоянию ДО ET-012 (тот же набор треков, та же толщина 1.5-1.75 px согласно interpolate-выражению)."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-09-Z12-CUTOFF — На z=12 переход на GeoJSON
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(12); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 5000
|
||||
9. screenshot: "et012-09-z12-geojson"
|
||||
10. check-visual: "На zoom=12 публичные треки видны (через GeoJSON-source). В DevTools Network должен быть запрос /api/gps-tracks?bbox=... (а не tiles/12/...). Регрессия cutoff поведения не нарушена."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-10-Z5-MOBILE — На мобильном при z=5 слой виден
|
||||
|
||||
- тип: ui
|
||||
- viewport: mobile
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 5000
|
||||
9. screenshot: "et012-10-z5-mobile"
|
||||
10. check-visual: "На мобильном viewport (375×667) при zoom=5 видны линии публичных треков. Линии тонкие, но различимые (минимум 1 физический пиксель). Hint скрыт. Bottom sheet с настройками слоёв закрывается корректно после клика по карте."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-11-Z5-SAT — На спутнике на z=5 halo читается, не глушит подложку
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. click: "#base-btn-satellite"
|
||||
8. wait: 5000
|
||||
9. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
10. wait: 5000
|
||||
11. screenshot: "et012-11-z5-satellite-halo"
|
||||
12. check-visual: "На спутниковой подложке при zoom=5 видны цветные линии треков с тонким белым halo (контур ~1.8 px). Halo делает линии читаемыми на тёмных участках космоснимка, но не превращается в 'пузырь' и не закрывает деталей подложки. Слой gps-tracks-halo-mvt-satellite имеет visibility:visible."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-12-Z5-Q — Качественная проверка читаемости на z5
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
- условие: запускается после E2E-PROD-01 (БД содержит ≥ 200 треков)
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 4000
|
||||
7. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 5000
|
||||
9. screenshot: "et012-12-z5-readability"
|
||||
10. check-visual: "На скриншоте видны 3+ различимых нити (линии длиной ≥ 20 px) в разных квадрантах кадра. Нет 'сплошной заливки' одной зоны (треки не сливаются в большое цветное пятно). Подложка карты остаётся читаемой. Качественная проверка — оператор смотрит и принимает либо отбраковывает. При отбраковке: ужесточить limit/min_length в build_gps_mvt (REQ-F-03)."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-13-Z5-PAN — Панорамирование на z=5 без зависаний
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. evaluate: window._map.panBy([300, 0]);
|
||||
10. wait: 2500
|
||||
11. evaluate: window._map.panBy([0, 300]);
|
||||
12. wait: 2500
|
||||
13. evaluate: window._map.panBy([-300, 0]);
|
||||
14. wait: 2500
|
||||
15. screenshot: "et012-13-z5-pan-complete"
|
||||
16. check-visual: "После трёх pan-шагов на z=5 карта показывает Восток ЦФО (или соседний регион). Тайлы соседних областей подгружены, нет 'белых дыр'. Тайл-LRU отрабатывает: возврат на исходную область (центр Москвы) — мгновенный (cache hit). Перфоманс субъективно гладкий, нет блокировок UI."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-14-Z5-COLOR-ACTIVITY — Color-by-activity на z=5
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. click: "#terrain-toggle"
|
||||
4. wait: 500
|
||||
5. click: "#public-tracks-cb"
|
||||
6. wait: 3000
|
||||
7. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
8. wait: 4000
|
||||
9. click: "#public-tracks-filters-btn"
|
||||
10. wait: 800
|
||||
11. click: "#gps-color-by-activity"
|
||||
12. wait: 1500
|
||||
13. screenshot: "et012-14-z5-color-by-activity"
|
||||
14. check-visual: "На z=5 активен переключатель «По активности». Линии перекрашены по activity_type (enduro/moto/offroad/bicycle). Видно минимум 2 разных цвета. Регрессия — color-mode тоggle работает идентично z=8+."
|
||||
|
||||
---
|
||||
|
||||
### TC-UI-15-DARK-Z5 — Тёмная тема на z=5
|
||||
|
||||
- тип: ui
|
||||
- viewport: desktop
|
||||
|
||||
шаги:
|
||||
1. navigate: https://openclaw.mva154.duckdns.org/enduro/
|
||||
2. wait: 5000
|
||||
3. evaluate: localStorage.setItem('theme', 'dark'); location.reload();
|
||||
4. wait: 5000
|
||||
5. click: "#terrain-toggle"
|
||||
6. wait: 500
|
||||
7. click: "#public-tracks-cb"
|
||||
8. wait: 3000
|
||||
9. evaluate: window._map.setZoom(5); window._map.setCenter([37.6, 55.7]);
|
||||
10. wait: 5000
|
||||
11. screenshot: "et012-15-z5-dark"
|
||||
12. check-visual: "При тёмной теме на z=5 линии публичных треков видны и читаются на тёмной подложке. Цвета линий не изменились (палитра задана в коде). Регрессия dark-theme."
|
||||
|
||||
---
|
||||
|
||||
### Заметки по запуску
|
||||
|
||||
- Все TC можно автоматизировать в Playwright; check-visual — через
|
||||
`expect(page).toHaveScreenshot(...)` или визуальный baseline.
|
||||
- Скриншоты складываются в `docs/work-items/ET-012/screenshots/`
|
||||
и пришиваются к `13-test-report.md`.
|
||||
- При первой регрессии TC-UI-12-Z5-Q (нечитаемая карта на z5)
|
||||
возвращаемся к разработчику с просьбой ужесточить
|
||||
`min_length_m`/`limit` для z5 (REQ-F-03) — это норма
|
||||
калибровки, не баг ETLкета.
|
||||
Reference in New Issue
Block a user