mirror of
https://github.com/lrnselfreliance/wrolpi.git
synced 2026-10-26 19:01:39 -04:00
- Add MapLibre GL JS viewer with light/dark themes, terrain layer, and embedded static map page for coordinate sharing - Add map pin support with persistent map_pins.yaml config - Add map region subscriptions and downloader with progress tracking, sourcing pre-extracted PMTiles from a cold CDN - Add map export to PNG and GPS coordinate copy via context menu - Add MapViewer React component alongside reworked Map page - Add map catalog and region-building script (build_map_regions.py) - Remove OSM import/render scripts, leaflet files, Apache configs, renderd service, and osm-map-dumper Docker container - Update Caddyfile to serve PMTiles and map static assets - Update build/install/repair scripts for Protomaps dependencies - Drop map_file table, add/drop map_subscription table (alembic) - Adding button to skip primary drive mounting during onboarding.
130 lines
5.1 KiB
Python
130 lines
5.1 KiB
Python
import json
|
|
from http import HTTPStatus
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_files_empty(async_client, test_directory):
|
|
"""An empty map directory returns no files."""
|
|
request, response = await async_client.get('/api/map/files')
|
|
assert response.status_code == HTTPStatus.OK
|
|
assert response.json['files'] == []
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_files(async_client, test_directory, make_files_structure):
|
|
"""PMTiles files in the map directory are listed."""
|
|
make_files_structure([
|
|
'map/usa.pmtiles',
|
|
'map/oregon.pmtiles',
|
|
'map/not-a-map.txt',
|
|
])
|
|
|
|
request, response = await async_client.get('/api/map/files')
|
|
assert response.status_code == HTTPStatus.OK
|
|
files = response.json['files']
|
|
names = [f['name'] for f in files]
|
|
assert 'oregon.pmtiles' in names
|
|
assert 'usa.pmtiles' in names
|
|
assert 'not-a-map.txt' not in names
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_file(async_client, test_directory, make_files_structure):
|
|
"""A PMTiles file can be deleted."""
|
|
pmtiles_file, = make_files_structure(['map/oregon.pmtiles'])
|
|
|
|
# File exists.
|
|
request, response = await async_client.get('/api/map/files')
|
|
assert len(response.json['files']) == 1
|
|
|
|
# Delete the file.
|
|
request, response = await async_client.delete('/api/map/files/oregon.pmtiles')
|
|
assert response.status_code == HTTPStatus.NO_CONTENT
|
|
|
|
# File is gone.
|
|
request, response = await async_client.get('/api/map/files')
|
|
assert len(response.json['files']) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_nonexistent_file(async_client, test_directory):
|
|
"""Deleting a nonexistent file returns 404."""
|
|
request, response = await async_client.delete('/api/map/files/nonexistent.pmtiles')
|
|
assert response.status_code == HTTPStatus.NOT_FOUND
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_delete_path_traversal(async_client, test_directory, make_files_structure):
|
|
"""Path traversal attempts are rejected."""
|
|
request, response = await async_client.delete('/api/map/files/..%2F..%2Fetc%2Fpasswd')
|
|
# Path traversal is blocked — either 400 (caught by validation) or 404 (file not found).
|
|
assert response.status_code in (HTTPStatus.BAD_REQUEST, HTTPStatus.NOT_FOUND)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_subscriptions_empty(async_client, test_session, test_directory):
|
|
"""Catalog and empty subscriptions can be fetched."""
|
|
request, response = await async_client.get('/api/map/subscribe')
|
|
assert response.status_code == HTTPStatus.OK
|
|
assert 'catalog' in response.json
|
|
assert 'subscriptions' in response.json
|
|
assert len(response.json['catalog']) > 0
|
|
assert len(response.json['subscriptions']) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_subscribe_and_unsubscribe(async_client, test_session, test_directory):
|
|
"""A map region can be subscribed to and unsubscribed from."""
|
|
body = json.dumps({'name': 'Alaska', 'region': 'us-alaska'})
|
|
request, response = await async_client.post('/api/map/subscribe', content=body)
|
|
assert response.status_code == HTTPStatus.CREATED
|
|
|
|
# Subscription appears in the list.
|
|
request, response = await async_client.get('/api/map/subscribe')
|
|
assert response.status_code == HTTPStatus.OK
|
|
regions = [s['region'] for s in response.json['subscriptions']]
|
|
assert 'us-alaska' in regions
|
|
|
|
# Unsubscribe.
|
|
request, response = await async_client.delete('/api/map/subscribe/us-alaska')
|
|
assert response.status_code == HTTPStatus.NO_CONTENT
|
|
|
|
# Subscription is gone.
|
|
request, response = await async_client.get('/api/map/subscribe')
|
|
assert len(response.json['subscriptions']) == 0
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_subscribe_multiple_regions(async_client, test_session, test_directory):
|
|
"""Multiple regions share a single Download record."""
|
|
body = json.dumps({'name': 'Alaska', 'region': 'us-alaska'})
|
|
request, response = await async_client.post('/api/map/subscribe', content=body)
|
|
assert response.status_code == HTTPStatus.CREATED
|
|
|
|
body = json.dumps({'name': 'United States (West)', 'region': 'us-west'})
|
|
request, response = await async_client.post('/api/map/subscribe', content=body)
|
|
assert response.status_code == HTTPStatus.CREATED
|
|
|
|
request, response = await async_client.get('/api/map/subscribe')
|
|
regions = [s['region'] for s in response.json['subscriptions']]
|
|
assert 'us-alaska' in regions
|
|
assert 'us-west' in regions
|
|
|
|
# Unsubscribe one — the other remains.
|
|
request, response = await async_client.delete('/api/map/subscribe/us-alaska')
|
|
assert response.status_code == HTTPStatus.NO_CONTENT
|
|
|
|
request, response = await async_client.get('/api/map/subscribe')
|
|
regions = [s['region'] for s in response.json['subscriptions']]
|
|
assert 'us-alaska' not in regions
|
|
assert 'us-west' in regions
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_subscribe_invalid_region(async_client, test_session, test_directory):
|
|
"""Subscribing to an invalid region returns an error."""
|
|
body = json.dumps({'name': 'Atlantis', 'region': 'atlantis'})
|
|
request, response = await async_client.post('/api/map/subscribe', content=body)
|
|
assert response.status_code == HTTPStatus.BAD_REQUEST
|