This document summarizes the testing structure, datasets, tolerances, and test execution patterns implemented in PyTransDatRO (primarily in tests/test_trans_ro.py and the tests/ directory).
The test suite relies on official reference coordinates and verification thresholds based on the official ROMPOS documentation (Help_TransDatRO_code_source_EN.pdf) and outputs from the official TransDatRO v4.08 software.
Tolerances define the maximum acceptable discrepancy against official reference outputs, matching the criteria specified in Help_TransDatRO_code_source_EN.pdf (page 3) and sub-millimeter round-trip precision:
| Tolerance Fixture | Value | Physical Meaning | Description |
|---|---|---|---|
coo_rad_tol |
0.00000000014544410 rad |
Angular tolerance for geographic latitude and longitude against official software. | |
coo_plan_tol |
0.003 m |
Planar tolerance for Stereo70 Northing and Easting coordinates against official software. | |
coo_elev_tol |
0.003 m |
Vertical tolerance for elevation / height components ( |
|
roundtrip_plan_tol |
0.0005 m |
Planar tolerance for forward-and-back round-trip conversions (accommodating the algebraic linear inverse in Helmert 2D). | |
roundtrip_rad_tol |
1e-10 rad |
Angular tolerance for forward-and-back round-trip conversions (matching the 6th decimal of an arcsecond). |
For extensive surface verification across Romania, benchmark datasets are organized in a dedicated directory under tests/data/ using a standardized naming convention (<source>_to_<target>_<role>.csv):
- Stereo70 to ETRS89 Transformation:
tests/data/st70_to_etrs89_input.csv:- Contains 9,046 Stereo70 coordinates formatted as CSV (
id,n,e,z). - Generated across a half-grid (nodes and mid-points) spanning the entire 2D shift grid extent.
- Contains 9,046 Stereo70 coordinates formatted as CSV (
tests/data/st70_to_etrs89_expected.csv:- Contains the corresponding 9,046 ETRS89 coordinates processed through official
TransDatRO v4.08. - Encoded in ANSI/Windows-1252, with coordinates in sexagesimal DMS strings (e.g.,
47°42'56.40000"N, 22°28'31.99998"E).
- Contains the corresponding 9,046 ETRS89 coordinates processed through official
- ETRS89 to Stereo70 Transformation:
3.
tests/data/etrs89_to_st70_input.csv: Contains ETRS89 coordinates in sexagesimal DMS format for reverse transformation inputs. 4.tests/data/etrs89_to_st70_expected.csv: Reference Stereo70 outputs for reverse transformation verification.
All tests are implemented using pytest and execute against the top-level coordinator class pytransdatro.TransRO.
To ensure mathematical reversibility without relying on hardcoded discrete reference points, two comprehensive round-trip tests process the bulk coordinate datasets:
-
test_st70_to_etrs89_to_st70_roundtrip:- Transforms
$(N, E, Z) \rightarrow (\text{lat}, \text{lon}, h) \rightarrow (N', E', Z')$ . - Verifies planar coordinates match within
roundtrip_plan_tol($0.5\text{ mm}$ ) and vertical elevation matches within$10^{-9}\text{ m}$ .
- Transforms
-
test_etrs89_to_st70_to_etrs89_roundtrip:- Transforms
$(\text{lat}, \text{lon}, h) \rightarrow (N, E, Z) \rightarrow (\text{lat}', \text{lon}', h')$ . - Verifies geographic coordinates match within
roundtrip_rad_tol($10^{-10}\text{ rad}$ ) and ellipsoidal height matches within$10^{-9}\text{ m}$ .
- Transforms
- Ingests the 7,866 points from
tests/data/st70_to_etrs89_input.csvandtests/data/st70_to_etrs89_expected.csv. - Converts DMS strings to radians using
pytransdatro.utils.sexa_dms_chars_to_rad(). - Executes
st70_to_etrs89(n, e)on all points. - Validates that every point across the grid matches the official
TransDatROoutput withincoo_rad_tol.
Edge cases and out-of-boundary behavior are tested using @pytest.mark.parametrize:
-
Out of Grid Boundaries (
test_st70_to_etrs89_extent_inner_out_of_grid):- Evaluates a 16-point Cartesian product (
$4 \times 4$ of$N$ and$E$ ) placed at or outside the outer grid bounding box. - Asserts that
pytransdatro.exceptions.OutOfGridErris raised.
- Evaluates a 16-point Cartesian product (
-
No-Data Zones Within Grid Extents (
test_st70_to_etrs89_no_grid_inner_no_data):- Evaluates 16 points located inside the grid bounding box, but positioned where the binary grid cells contain no valid interpolation data.
- Asserts that
pytransdatro.exceptions.NoDataGridErris raised.
-
National Border Transition Areas (
test_st70_to_etrs89_just_out_ro_border_no_data):- Tests 4 coordinates located immediately outside the Romanian territorial boundary.
- Asserts that
pytransdatro.exceptions.NoDataGridErris raised.
create_transdatro_test_file(file, template='st70_half_grid'):- A utility function contained within
test_trans_ro.py(not a test itself). - Iterates through the active 2D grid (
_t_gr2d), generating points at grid nodes and half-step intervals. - Outputs a CSV file that can be loaded directly into the official TransDatRO desktop software to produce new benchmark files.
- A utility function contained within
The API layer is thoroughly tested using pytest and starlette.testclient.TestClient:
| Module | Tests | Focus Areas |
|---|---|---|
tests/test_trans_ro.py |
54 | Core 2D/3D transformations, round-trip accuracy, 6-point extended dual-CRS bounding boxes, OutOfGrid2DErr vs OutOfGrid1DErr, pole singularity/periodic aliasing guards, 4-stage transform_batch fault isolation, sequence length/finite number validation (InvalidCoordinateErr, WrongDimensionErr), and strict DMS parsing. |
tests/test_api_modern.py |
36 | Point (/api/v1/transform/point), Option B fault-isolated batch (/transform/batch), file streaming (/transform/file), No data on grid / OutOfGrid1DErr / curved-border wedge / northern bulge warnings, 3-token -Inf/Inf/NaN file guards, HTTP 413 size & row truncation limits, grid metadata (/grid/info, /grid/metadata, /grid/comparison), seeded SQLite telemetry (/telemetry/stats, /telemetry/timeline, /telemetry/spatial-matrix), health (/health healthy/degraded), and structured validation errors (ErrorResponse). |
tests/test_api_legacy.py |
42 | Full local and live differential parity suite for /transdatonline/cooOpService across GET & POST, Stereo70ToETRS89 & ETRS89ToStereo70, 2D & 3D, Out of grid (2D/1D/wedge), No data on grid, Invalid coordinate data, Wrong dimension, string-encoded numbers in POST, obsolete Stereo30*, multipart/form-data, and live comparison against https://www.geo-spatial.org/transdatonline/cooOpService (@pytest.mark.legacy_live). |
tests/test_logging.py |
17 | Structured application logging, rotating file handlers, console formatters, and correlation ID tracking. |
tests/test_api_legacy_stats.py |
14 | Legacy TransdatOnline database queries, historical volume timelines (2011–2026), and spatial aggregation. |
tests/test_config.py |
9 | Centralized Settings resolution, log dir atomic relocation, environment variable overrides (.env), webapp_dist_dir default and relative override anchoring, CORS parsing, and dictConfig logging dictionary generation. |
tests/test_feedback.py |
7 | User feedback submission (/api/v1/feedback), honeypot bot misdirection, schema validation, file persistence (feedback.jsonl), mocked SMTP TLS/auth delivery, and SMTP failure fallback. |
tests/test_telemetry.py |
6 | Asynchronous background SQLite WAL logging, transaction queues, rolling dynamic window retention, and quadrant coordinate aggregation. |
tests/test_api_rate_limiter.py |
5 | Sliding-window in-memory rate limiting enforcement (60/min point, 10/min batch shared across /batch, /file, and legacy POST), per-IP X-Forwarded-For isolation, unmetered route pass-through, Retry-After headers, and window expiration recovery. |
Execution Commands:
# Run full test suite (including live legacy differential parity if reachable)
python -m pytest -q
# Run offline only (explicitly skipping live legacy server tests)
python -m pytest -m "not legacy_live" -q(Runs all 189 Python unit, integration, contract, and live differential parity tests. Live legacy tests automatically skip via pytest.skip if PYTRANSDAT_LEGACY_LIVE_ENABLED=0 or once the legacy server is decommissioned).
Client-side parsing, serialization, and UI factory helpers are tested with Vitest:
| Test File | Tests | Coverage Scope |
|---|---|---|
src/utils/pointParser.test.ts |
32 | Auto-delimiter detection (tab, space, comma, semicolon), Point ID extraction, 2D vs 3D tuples, coordinate ordering (NE(H) vs EN(H)). |
src/utils/numberFormat.test.ts |
15 | Romanian and international number formatting, metric prefixes, telemetry percent and compact formatting. |
src/utils/dmsParser.test.ts |
14 | Unspaced DMS parsing (DD°MM'SS.SSSSS"), edge cases, whitespace tolerance, sexagesimal to decimal conversions. |
src/utils/coordinateFormat.test.ts |
11 | Canonical angular, planar, and coordinate pair formatting with ordering and quasigeoid undulation ( |
src/utils/gridBounds.test.ts |
10 | Romania national bounding envelope checks, coordinate bounds validation, out-of-grid pre-flight filters. |
src/utils/exporters.test.ts |
9 | Pure client-side export generation for CSV, GeoJSON, and AutoCAD DXF R12 ASCII formats (including out-of-grid and no-data warnings). |
src/utils/dateUtils.test.ts |
9 | Telemetry date range presets ('7d', '30d', 'ytd', '12m', 'all') and localized date formatting. |
src/ui/ui.test.ts |
9 | Accessible UI factory helpers (renderDropdownMenu, renderSegmentedControl, renderSelect, renderCopyBadge, renderCopyButton). |
src/ui/chartCore.test.ts |
8 | SVG cubic Bézier smooth path calculation, nearest data point search, and timeline X coordinate geometry. |
src/i18n/i18n.test.ts |
6 | Translation dictionary key and variable placeholder parity between Romanian (ro) and English (en), fallback resolution. |
Execution Commands:
# Run Vitest suite once
npm --prefix webapp test
# Run Vitest in interactive watch mode
npm --prefix webapp run test:watch(Runs all 123 TypeScript unit tests in $< 500\text{ ms}$).
Coordinate transformations must maintain 100% byte-for-byte fidelity against ANCPI benchmark reference sets:
# Unified orchestration tool (preferred):
python tools/update_grid.py benchmark
# Or standalone report generator:
python tools/generate_ancpi_comparison_reports.pyImportant
When updating the grid file, official ANCPI expected reference datasets in tests/data/*_expected.csv must be updated using official ANCPI desktop software. See docs/grid_lifecycle.md for full details.
- Reference Dataset: 9,046 national geodetic control points across Romania.
-
Criteria for Success:
- Horizontal planar discrepancy:
$< 10^{-6}\text{ m}$ ($< 0.001\text{ mm}$ ). - Vertical ellipsoidal height discrepancy:
$< 10^{-6}\text{ m}$ . - Overall status:
PASSexported topytransdatro/grids/ancpi_comparison_summary.json.
- Horizontal planar discrepancy:
Browser-level flow tests live in webapp/e2e/*.spec.ts (Chromium only, no retries). Vitest remains
the default for parsers and src/ui/ factories.
One-time setup:
npm --prefix webapp install
npx --prefix webapp playwright install chromiumRun Options:
-
Recommended Single-Command Runners (Auto-detects Python & Conda):
# Windows PowerShell .\test-e2e.ps1 # Run all 59 E2E browser tests across 11 suites (headless) .\test-e2e.ps1 -Ui # Open interactive visual runner with time-travel debugging .\test-all.ps1 # Run Python suite + Vitest unit tests + Playwright E2E in one go
# macOS / Linux (POSIX) ./test-e2e.sh # Run all 59 E2E browser tests across 11 suites (headless) ./test-e2e.sh --ui # Open interactive visual runner ./test-all.sh # Run full verification suite
-
Direct NPM Command (Python is auto-detected by
webapp/playwright.config.ts, or overridden viaPYTHON):npm --prefix webapp run test:e2e
The script typechecks webapp/e2e (tsc -p e2e, separate from the app tsconfig.json) and then runs Playwright.
Server: playwright.config.ts starts one process from the repo root: it builds the frontend
with VITE_E2E=true into webapp/dist-e2e/ (npm run build:e2e) and then serves web + API with
python -m uvicorn api.main:app on http://127.0.0.1:8100. reuseExistingServer is false, so the
normal dev server on :8000 is never touched.
Isolation (env set by the config): PYTRANSDAT_WEBAPP_DIST_DIR=webapp/dist-e2e,
PYTRANSDAT_LOG_DIR and PYTRANSDAT_FEEDBACK_LOG_PATH under webapp/e2e/.tmp/ (git-ignored),
PYTRANSDAT_SMTP_HOST="" (no mail), and rate limits raised to 100000.
Fixtures (webapp/e2e/fixtures.ts; import test/expect from there, not from Playwright):
- External hosts (map tiles, Google Fonts, ...) are intercepted and answered with a stub (1x1
transparent PNG / empty CSS) carrying an
x-e2e-stubheader;externalRequestslists them. - Language is pinned to
envialocalStorage['pytransdat_lang'](only if unset, so a test that switches language keeps it across reloads). consoleErrorscollects console errors and uncaught page errors.mapView()/readMapView(page)returnwindow.__e2e.mapView().
window.__e2e: exposed only when the bundle is built with VITE_E2E=true (flag
E2E_ENABLED in webapp/src/config.ts); absent from the normal production build. mapView()
returns a plain snapshot: center, zoom, bounds, maxBounds (or null), markerCount and
markersByLayer. Use it to assert that transforms never auto-pan or zoom the map.
Reference data: webapp/e2e/referencePoints.ts holds ANCPI benchmark points (decimal degrees
and metres) and matchesAtDisplayPrecision() for comparisons at UI precision (8 decimals degrees,
3 decimals metres, +-1 in the last digit).
Failures: traces (retain-on-failure) and screenshots (only-on-failure) are written to
webapp/test-results/, the HTML report to webapp/playwright-report/ (both git-ignored). Open a
trace with npx --prefix webapp playwright show-trace <trace.zip>.
Before committing or releasing changes, execute the static quality audit:
# Ruff linter and formatter check
D:\anaconda3\envs\pytransdat\python.exe -m ruff check pytransdatro api tests tools
D:\anaconda3\envs\pytransdat\python.exe -m ruff format --check pytransdatro api tests tools
# TypeScript compilation
npm --prefix webapp run build