Files
enduro-trails/tests/web/test_gps_tracks.py
claude-bot ba356ae317
Some checks failed
CI / lint (push) Failing after 4s
CI / test (push) Failing after 5s
CI / build (push) Has been skipped
CI / lint (pull_request) Failing after 4s
CI / test (pull_request) Failing after 5s
CI / build (pull_request) Has been skipped
fix(gps-tracks): rename health fields and fix layer insert priority (F-04, F-05)
F-04: rename gps_health() response fields per tester feedback:
  - total_tracks → tracks_total
  - by_activity  → tracks_by_activity
  - recent_pipeline_runs (list) → last_pipeline_run (object | null)
  Change LIMIT from 10 to 1; fetch single row instead of a list.

F-05: rewrite _findGpsInsertPosition with explicit priority order:
  1. gpx-layer-* (ET-006 GPX file layers) — highest priority
  2. route-* (ET-002 routing layers)
  Remove old combined find() that lacked clear priority semantics.

Add tests/web/gps_tracks.test.js (22 JS unit tests via node:test):
  - _findGpsInsertPosition priority logic (9 cases)
  - Filter state management — default state assertions (5 cases)
  - Color palette mapping and _buildColorExpression (8 cases)

Add tests/web/test_gps_tracks.py — Python pytest runner (8 static
  checks + node --test invocation).

Refs: ET-008

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-01 14:18:06 +00:00

134 lines
6.0 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""ET-008 — тесты модуля публичных GPS-треков (gps_tracks.js + endpoint).
Изменение ET-008 — модуль `src/web/gps_tracks.js` + FastAPI endpoint
`/api/gps-tracks`. В CI исполняется только `pytest tests/`, поэтому файл
покрывает фронтендовую часть двумя способами:
1. Статические проверки структуры gps_tracks.js — выполняются всегда.
2. Поведенческие JS unit-тесты — через встроенный тест-раннер Node
(`node --test`). Если `node` отсутствует — часть помечается `skip`.
API-тесты endpoint живут в tests/api/test_gps_tracks_endpoint.py.
Запуск JS-тестов напрямую:
node --test tests/web/gps_tracks.test.js
"""
from __future__ import annotations
import subprocess
from pathlib import Path
from shutil import which
import pytest
REPO_ROOT = Path(__file__).resolve().parents[2]
GPS_TRACKS_JS = REPO_ROOT / "src" / "web" / "gps_tracks.js"
JS_TEST = REPO_ROOT / "tests" / "web" / "gps_tracks.test.js"
def _read(path: Path) -> str:
assert path.is_file(), f"не найден {path}"
return path.read_text(encoding="utf-8")
# ──────────────────────────────────────────────────────────────────────────────
# Статические проверки gps_tracks.js
# ──────────────────────────────────────────────────────────────────────────────
def test_gps_tracks_module_exists():
"""Модуль src/web/gps_tracks.js присутствует в репозитории."""
assert GPS_TRACKS_JS.is_file(), "не найден src/web/gps_tracks.js"
def test_gps_tracks_find_insert_position_defined():
"""_findGpsInsertPosition() объявлена в модуле."""
js = _read(GPS_TRACKS_JS)
assert "function _findGpsInsertPosition(" in js, (
"_findGpsInsertPosition не объявлена в gps_tracks.js"
)
def test_gps_tracks_find_insert_position_priority_gpx_first():
"""F-05: поиск gpx-layer-* идёт до route-* (приоритет 1 > приоритет 2)."""
js = _read(GPS_TRACKS_JS)
fn_start = js.index("function _findGpsInsertPosition(")
fn_end = js.index("\n}", fn_start)
fn_body = js[fn_start:fn_end]
gpx_pos = fn_body.find("gpx-layer-")
route_pos = fn_body.find("route-")
assert gpx_pos != -1, "gpx-layer- не найден в _findGpsInsertPosition"
assert route_pos != -1, "route- не найден в _findGpsInsertPosition"
assert gpx_pos < route_pos, (
"gpx-layer-* должен проверяться ДО route-* (приоритет 1 > приоритет 2)"
)
def test_gps_tracks_find_insert_position_no_exact_route_line():
"""F-05: старый точный match 'route-line' удалён, используется startsWith."""
js = _read(GPS_TRACKS_JS)
fn_start = js.index("function _findGpsInsertPosition(")
fn_end = js.index("\n}", fn_start)
fn_body = js[fn_start:fn_end]
assert "l.id === 'route-line'" not in fn_body, (
"старый точный матч route-line не должен присутствовать (F-05)"
)
def test_gps_tracks_state_object_defined():
"""window.gpsTracksLayer инициализируется в модуле."""
js = _read(GPS_TRACKS_JS)
assert "window.gpsTracksLayer = {" in js, (
"gps_tracks.js не инициализирует window.gpsTracksLayer"
)
def test_gps_tracks_source_colors_defined():
"""GPS_SOURCE_COLORS объявлен для всех основных источников."""
js = _read(GPS_TRACKS_JS)
for src in ("osm", "enduro_russia", "ttrails", "offmaps", "nakarte"):
assert src in js, f"GPS_SOURCE_COLORS не содержит ключ {src}"
def test_gps_tracks_activity_colors_defined():
"""GPS_ACTIVITY_COLORS объявлен для всех 7 типов активности."""
js = _read(GPS_TRACKS_JS)
for act in ("enduro", "moto", "offroad", "bicycle", "hike", "ski", "other"):
assert act in js, f"GPS_ACTIVITY_COLORS не содержит ключ {act}"
def test_gps_tracks_build_color_expression_defined():
"""_buildColorExpression() объявлена в модуле."""
js = _read(GPS_TRACKS_JS)
assert "function _buildColorExpression(" in js, (
"_buildColorExpression не объявлена в gps_tracks.js"
)
# ──────────────────────────────────────────────────────────────────────────────
# Поведенческие JS unit-тесты через Node
# ──────────────────────────────────────────────────────────────────────────────
node_required = pytest.mark.skipif(
which("node") is None,
reason="node не установлен — поведенческие JS unit-тесты пропущены",
)
@node_required
def test_js_unit_tests_pass():
"""F-05 + filters + colors: behavioral JS-тесты gps_tracks.js через `node --test`."""
assert JS_TEST.is_file(), f"не найден JS-тест {JS_TEST}"
node = which("node")
result = subprocess.run(
[node, "--test", str(JS_TEST)],
capture_output=True,
text=True,
cwd=str(REPO_ROOT),
)
assert result.returncode == 0, (
f"JS unit-тесты GPS-треков упали (код {result.returncode}):\n"
f"{result.stdout}\n{result.stderr}"
)