--- 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" ---