337 lines
15 KiB
YAML
337 lines
15 KiB
YAML
---
|
||
type: test-plan
|
||
work_item_id: ET-013
|
||
title: "Test Plan: Перепады высот на z9-z11"
|
||
version: 1
|
||
status: draft
|
||
created_at: 2026-06-04
|
||
updated_at: 2026-06-04
|
||
authors:
|
||
- "agent:analyst"
|
||
related:
|
||
- "PH-6.terrain"
|
||
- "ET-007"
|
||
|
||
scope_note: >
|
||
ET-013 — frontend-калибровка: понижает UI-минзум hillshade с 10 до 9
|
||
и переводит paint-параметры (raster-opacity, raster-contrast,
|
||
raster-resampling) hillshade и TRI в zoom-aware форму. Backend
|
||
и pipeline растровых тайлов не трогаются. Тест-план фокусируется
|
||
на:
|
||
(1) корректности новых zoom-tier paint-выражений;
|
||
(2) обратной совместимости applyTerrainLayer;
|
||
(3) визуальной читаемости перепадов на z9-z11;
|
||
(4) регрессии z8 (TRI не изменился), z14 (hillshade не перегрет);
|
||
(5) совместимости с тёмной темой и спутниковой подложкой;
|
||
(6) что network-объём не уплыл больше +35%.
|
||
|
||
test_suites:
|
||
|
||
- name: unit-terrain-paint
|
||
type: unit
|
||
description: "Структура paint-выражений HILLSHADE_PAINT и TRI_PAINT"
|
||
cases:
|
||
- id: UT-PAINT-HS-OPACITY
|
||
name: "HILLSHADE_PAINT: raster-opacity — interpolate с правильными stops"
|
||
input: |
|
||
Python-парсер: чтение src/web/app.js, regex по блоку
|
||
HILLSHADE_PAINT = { ... }; вытаскивание raster-opacity.
|
||
expected: |
|
||
Тип: ['interpolate', ['linear'], ['zoom'], ...].
|
||
Stops содержат: (9, 0.65), (10, 0.60), (11, 0.55),
|
||
(12, 0.50), (14, 0.40). Допустимо отклонение значений ±0.05
|
||
(калибровка) — но порядок монотонно убывающий от 9 к 14.
|
||
|
||
- id: UT-PAINT-HS-CONTRAST
|
||
name: "HILLSHADE_PAINT: raster-contrast — пик на z9, 0 на z14"
|
||
input: |
|
||
Тот же парсер.
|
||
expected: |
|
||
Тип interpolate. Значение на z=9 ≥ 0.30. Значение на z=14
|
||
≤ 0.10. Монотонно убывает.
|
||
|
||
- id: UT-PAINT-HS-RESAMPLING
|
||
name: "HILLSHADE_PAINT: raster-resampling = 'nearest'"
|
||
input: |
|
||
Парсер.
|
||
expected: |
|
||
Строка 'nearest' (не 'linear').
|
||
|
||
- id: UT-PAINT-TRI-OPACITY-Z8
|
||
name: "TRI_PAINT: на z8 opacity = 0.70 (регрессия)"
|
||
input: |
|
||
Парсер по TRI_PAINT.
|
||
expected: |
|
||
Stop (8, 0.70) присутствует ровно (без округления).
|
||
|
||
- id: UT-PAINT-TRI-OPACITY-PEAK
|
||
name: "TRI_PAINT: пик на z9-z11"
|
||
input: |
|
||
Парсер.
|
||
expected: |
|
||
Stops содержат (10, X) с X ≥ 0.80 и (11, Y) с Y ≥ 0.80.
|
||
|
||
- id: UT-PAINT-TRI-RESAMPLING
|
||
name: "TRI_PAINT: raster-resampling = 'nearest'"
|
||
input: |
|
||
Парсер.
|
||
expected: |
|
||
'nearest'.
|
||
|
||
- id: UT-PAINT-COMPAT-01
|
||
name: "applyTerrainLayer обратно-совместим с числовым opacity"
|
||
input: |
|
||
Вызов с opacity=0.5 (Node + JSDOM-mock карты).
|
||
expected: |
|
||
Внутри map.addLayer передан paint:
|
||
{ 'raster-opacity': 0.5, 'raster-resampling': 'linear' }.
|
||
notes: |
|
||
Если запуск JS-теста не настроен — заменить на статический
|
||
grep по src/web/app.js: проверить ветвление
|
||
'typeof opacityOrPaint === "number"'.
|
||
|
||
- id: UT-PAINT-COMPAT-02
|
||
name: "applyTerrainLayer принимает paint-объект"
|
||
input: |
|
||
Вызов с opacityOrPaint = { 'raster-opacity': 0.4,
|
||
'raster-contrast': 0.2, 'raster-resampling': 'nearest' }.
|
||
expected: |
|
||
Этот объект передан в map.addLayer paint как есть.
|
||
|
||
- id: UT-REG-MINZOOM-9
|
||
name: "updateHillshadeAvailability порог = 9"
|
||
input: |
|
||
grep по src/web/app.js: 'if (zoom < 9)' внутри функции
|
||
updateHillshadeAvailability.
|
||
expected: |
|
||
Совпадение найдено; 'if (zoom < 10)' отсутствует.
|
||
|
||
- id: UT-REG-HINT-TEXT
|
||
name: "Hint текст обновлён до 'Зум 9+'"
|
||
input: |
|
||
grep по src/web/index.html: '#terrain-hillshade-hint'
|
||
содержит 'Зум 9+'.
|
||
expected: |
|
||
Совпадение найдено; 'Зум 10+' отсутствует.
|
||
|
||
- id: UT-REG-CALLERS
|
||
name: "applyTerrainLayer вызывается ровно дважды в onTerrainCheckbox"
|
||
input: |
|
||
regex 'applyTerrainLayer\s*\(' в src/web/*.js — count.
|
||
expected: |
|
||
Минимум 2 вызова в src/web/app.js. Все они находятся
|
||
внутри функции onTerrainCheckbox.
|
||
|
||
- name: integration-terrain-tiles
|
||
type: integration
|
||
description: "Endpoint /terrain/{layer}/{z}/{x}/{y}.png на z9-z11"
|
||
cases:
|
||
- id: IT-TILE-Z9-01
|
||
name: "Тайл z=9 для hillshade: 200 или skipped если данных нет"
|
||
input: |
|
||
Test-среда или CI с TERRAIN_DIR. Найти первый существующий
|
||
тайл z9 в директории hillshade, выполнить GET.
|
||
expected: |
|
||
Если data/terrain/hillshade/9/ существует:
|
||
status 200, content-type image/png, тело > 0.
|
||
Иначе:
|
||
test skipped с reason 'PH-6 data not in repo'.
|
||
|
||
- id: IT-TILE-Z10-01
|
||
name: "Тайл z=10 для hillshade: 200 или skipped"
|
||
input: |
|
||
То же, что IT-TILE-Z9-01 для z=10.
|
||
expected: |
|
||
status 200 или skipped.
|
||
|
||
- id: IT-TILE-Z11-01
|
||
name: "Тайл z=11 для hillshade: 200 или skipped"
|
||
input: |
|
||
То же для z=11.
|
||
expected: |
|
||
status 200 или skipped.
|
||
|
||
- id: IT-TILE-TRI-Z9
|
||
name: "TRI на z9 доступен (минзум 5, тайлы должны быть)"
|
||
input: |
|
||
GET tiles/9/X/Y.png под TRI.
|
||
expected: |
|
||
200 или skipped (если данных нет на CI).
|
||
|
||
- id: IT-TILE-INVALID-LAYER
|
||
name: "Неизвестный layer → 404 (регрессия)"
|
||
input: |
|
||
GET /terrain/unknown/9/0/0.png
|
||
expected: |
|
||
status 404.
|
||
|
||
- id: IT-TILE-MISSING
|
||
name: "Несуществующий тайл → 404 (регрессия)"
|
||
input: |
|
||
GET /terrain/hillshade/9/99999/99999.png
|
||
expected: |
|
||
status 404.
|
||
|
||
- id: IT-TILE-CACHE-HEADER
|
||
name: "Cache-Control: immutable сохраняется"
|
||
input: |
|
||
GET существующего тайла.
|
||
expected: |
|
||
Header 'Cache-Control' содержит 'immutable' и max-age=31536000.
|
||
|
||
- name: regression-existing
|
||
type: regression
|
||
description: "Регрессия ET-007 / PH-6 / общих unit-тестов"
|
||
cases:
|
||
- id: RG-UNIT-ALL
|
||
name: "Все unit-тесты проекта зелёные"
|
||
input: "pytest tests/unit/ -v"
|
||
expected: "exit-code 0"
|
||
|
||
- id: RG-INTEG-ALL
|
||
name: "Все integration-тесты проекта зелёные"
|
||
input: "pytest tests/integration/ -v"
|
||
expected: "exit-code 0"
|
||
|
||
- id: RG-LINT
|
||
name: "Линтеры зелёные"
|
||
input: "make lint"
|
||
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..TC-UI-12"
|
||
expected: |
|
||
Каждый TC выполняется; check-visual подтверждается
|
||
оператором либо визуальным diff-инструментом
|
||
(baseline до ET-013 vs текущий).
|
||
|
||
- name: manual-deploy-validation
|
||
type: e2e
|
||
description: "Ручная проверка в test-среде после деплоя"
|
||
marker: "manual"
|
||
cases:
|
||
- id: E2E-PRE-DEPLOY-01
|
||
name: "Pre-deploy: тайлы z9-z11 на test-среде доступны"
|
||
steps:
|
||
- "curl -sI https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/9/308/158.png | head -1"
|
||
- "curl -sI https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/10/617/317.png | head -1"
|
||
- "curl -sI https://openclaw.mva154.duckdns.org/enduro/terrain/hillshade/11/1234/635.png | head -1"
|
||
- "Все три — HTTP/1.1 200 OK. При 404 — стоп, открыть PH-6 follow-up."
|
||
- "Зафиксировать в 14-deploy-log.md."
|
||
|
||
- id: E2E-DEPLOY-01
|
||
name: "Hillshade доступен на z=9"
|
||
steps:
|
||
- "Открыть https://openclaw.mva154.duckdns.org/enduro/"
|
||
- "localStorage.clear(); location.reload()"
|
||
- "Click #terrain-toggle"
|
||
- "В Console: window._map.setZoom(9); window._map.setCenter([37.6, 54.5])"
|
||
- "Wait 2s"
|
||
- "Кнопка #terrain-hillshade-cb имеет disabled=false"
|
||
- "Hint #terrain-hillshade-hint имеет display:none"
|
||
- "Click #terrain-hillshade-cb"
|
||
- "Wait 3s"
|
||
- "На карте видны тени"
|
||
- "Screenshot et013-deploy-z9.png"
|
||
- "Зафиксировать в 14-deploy-log.md"
|
||
|
||
- id: E2E-DEPLOY-02
|
||
name: "Network-объём: рост ≤ 35%"
|
||
steps:
|
||
- "Открыть DevTools Network, фильтр /terrain/"
|
||
- "Очистить network log"
|
||
- "В Console: window._map.setZoom(8); ждать 3s; setZoom(9); ждать 3s; setZoom(10); ждать 3s; setZoom(11); ждать 3s"
|
||
- "Замерить суммарный transferred size в фильтре /terrain/"
|
||
- "Сравнить с baseline (записан в 13-test-report.md до ET-013): рост ≤ 135%"
|
||
- "Зафиксировать"
|
||
|
||
- id: E2E-DEPLOY-03
|
||
name: "Регрессия z=8 (TRI выглядит как до ET-013)"
|
||
steps:
|
||
- "localStorage.clear(); location.reload()"
|
||
- "Включить только #terrain-tri-cb (без hillshade)"
|
||
- "window._map.setZoom(8); setCenter([37.6, 54.5])"
|
||
- "Screenshot et013-deploy-z8-tri-regress.png"
|
||
- "Визуально сравнить с baseline из 13-test-report.md до ET-013 — не отличается заметно."
|
||
|
||
- id: E2E-DEPLOY-04
|
||
name: "Регрессия z=14 (hillshade не перегрет)"
|
||
steps:
|
||
- "Включить #terrain-hillshade-cb"
|
||
- "window._map.setZoom(14); setCenter([37.6, 54.5])"
|
||
- "Screenshot et013-deploy-z14-regress.png"
|
||
- "Эффективное raster-opacity ≈ 0.40, raster-contrast ≈ 0"
|
||
- "В Console: window._map.getPaintProperty('terrain-hillshade', 'raster-opacity')"
|
||
- "(вернёт interpolate-выражение — proof zoom-aware)"
|
||
|
||
- id: E2E-DEPLOY-05
|
||
name: "Спутник + hillshade на z=10 (R-3)"
|
||
steps:
|
||
- "Включить hillshade, переключить #base-btn-satellite"
|
||
- "window._map.setZoom(10); setCenter([37.6, 54.5])"
|
||
- "Screenshot et013-deploy-z10-sat.png"
|
||
- "Визуальная приёмка: hillshade видим, не глушит снимок"
|
||
- "При проблеме — задача отправляется на корректировку stops"
|
||
|
||
- id: E2E-DEPLOY-06
|
||
name: "Тёмная тема + hillshade на z=10 (R-2)"
|
||
steps:
|
||
- "Click #btn-theme (переключить в тёмную)"
|
||
- "window._map.setZoom(10)"
|
||
- "Screenshot et013-deploy-z10-dark.png"
|
||
- "Визуальная приёмка: hillshade читается, не сливается с тёмной подложкой"
|
||
|
||
- id: E2E-DEPLOY-07
|
||
name: "Persistence: F5 не теряет состояние"
|
||
steps:
|
||
- "Включить оба чекбокса"
|
||
- "location.reload()"
|
||
- "Чекбоксы остаются включёнными"
|
||
- "На текущем zoom оба слоя восстановлены"
|
||
|
||
test_data:
|
||
fixtures_dir: "tests/fixtures/terrain/"
|
||
fixtures:
|
||
- name: "hillshade-z9-sample.png"
|
||
description: |
|
||
Опционально: один валидный PNG-тайл из data/terrain/hillshade/9/
|
||
для CI-окружения без полного набора данных. Скопировать любой
|
||
тайл над ЦФО, переименовать. ~10 KB.
|
||
- name: "hillshade-z10-sample.png"
|
||
description: "То же для z10."
|
||
- name: "tri-z10-sample.png"
|
||
description: "TRI sample для z10."
|
||
notes:
|
||
- "Если на CI нет TERRAIN_DIR с данными — IT-TILE-* тесты skipped (REQ-F-15)."
|
||
- "Сравнения 'до/после' визуальные — baseline скриншоты лежат в 13-test-report.md и фиксируются до начала ET-013."
|
||
- "Для unit-тестов paint никаких fixture не нужно — парсинг исходника."
|
||
|
||
test_environment:
|
||
unit:
|
||
- "Python 3.12, pytest"
|
||
- "regex-парсер src/web/app.js (Вариант B в TRZ REQ-F-13)"
|
||
- "Опционально Node + JSDOM, если в проекте появятся JS-тесты"
|
||
integration:
|
||
- "FastAPI TestClient против src.api.main:app"
|
||
- "TERRAIN_DIR через env или skip-if-missing"
|
||
performance:
|
||
- "Не требуется специально: NFR-01/02 говорят о невидимом изменении render-time"
|
||
- "Сетевой объём — ручной замер в DevTools Network (E2E-DEPLOY-02)"
|
||
e2e:
|
||
- "Test-среда https://openclaw.mva154.duckdns.org/enduro/"
|
||
- "Playwright (см. 04b-ui-test-cases.md)"
|
||
|
||
ci_gates:
|
||
- "Unit UT-PAINT-* и UT-REG-* — обязательны (AC-15)"
|
||
- "Integration IT-TILE-* — обязательны (с skipif для отсутствующих данных) (AC-16)"
|
||
- "Регрессия RG-UNIT-ALL, RG-INTEG-ALL, RG-LINT — обязательны (AC-17, AC-18)"
|
||
- "Pre-deploy E2E-PRE-DEPLOY-01 — ручной gate перед merge (AC-19)"
|
||
- "UI-тесты Playwright — после деплоя, фиксация в 13-test-report.md"
|
||
- "E2E-DEPLOY-01..07 — ручные шаги в 14-deploy-log.md"
|
||
---
|