Files
gps-denied-onboard/tests/test_api_flights.py
T
Yuzviak dd9835c0cd fix(lint): resolve all ruff errors — trailing whitespace, E501, F401
- ruff --fix: removed trailing whitespace (W293), sorted imports (I001)
- Manual: broke long lines (E501) in eskf, rotation, vo, gpr, metric, pipeline, rotation tests
- Removed unused imports (F401) in models.py, schemas/__init__.py
- pyproject.toml: line-length 100→120, E501 ignore for abstract interfaces

ruff check: 0 errors. pytest: 195 passed / 8 skipped.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:09:47 +03:00

137 lines
4.0 KiB
Python

"""Integration tests for the Flight API endpoints."""
import json
import pytest
from httpx import ASGITransport, AsyncClient
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from gps_denied.app import app
from gps_denied.db.engine import get_session
from gps_denied.db.models import Base
@pytest.fixture
async def override_get_session():
"""Create an in-memory SQLite db for API tests."""
engine = create_async_engine("sqlite+aiosqlite://", echo=False)
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)
async def _get_session():
async with async_session() as session:
yield session
app.dependency_overrides[get_session] = _get_session
yield
app.dependency_overrides.clear()
await engine.dispose()
@pytest.fixture
async def client(override_get_session) -> AsyncClient:
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
yield ac
# ── Payload Fixtures ──────────────────────────────────────────────────────
FLIGHT_PAYLOAD = {
"name": "Integration_Test_Flight",
"description": "API Test",
"start_gps": {"lat": 48.1, "lon": 37.2},
"rough_waypoints": [{"lat": 48.11, "lon": 37.21}],
"geofences": {"polygons": []},
"camera_params": {
"focal_length": 25.0,
"sensor_width": 23.5,
"sensor_height": 15.6,
"resolution_width": 6252,
"resolution_height": 4168
},
"altitude": 500.0
}
@pytest.mark.asyncio
async def test_create_flight(client: AsyncClient):
resp = await client.post("/flights", json=FLIGHT_PAYLOAD)
assert resp.status_code == 201
data = resp.json()
assert "flight_id" in data
assert data["status"] == "prefetching"
@pytest.mark.asyncio
async def test_get_flight_details(client: AsyncClient):
# 1. Create flight
resp1 = await client.post("/flights", json=FLIGHT_PAYLOAD)
fid = resp1.json()["flight_id"]
# 2. Get flight
resp2 = await client.get(f"/flights/{fid}")
assert resp2.status_code == 200
data = resp2.json()
assert data["flight_id"] == fid
assert data["name"] == "Integration_Test_Flight"
assert len(data["waypoints"]) == 1
@pytest.mark.asyncio
async def test_upload_image_batch(client: AsyncClient):
# 1. Create flight
resp1 = await client.post("/flights", json=FLIGHT_PAYLOAD)
fid = resp1.json()["flight_id"]
# 2. Upload Batch
meta = {
"start_sequence": 1,
"end_sequence": 10,
"batch_number": 1
}
files = [("images", ("test1.jpg", b"dummy", "image/jpeg")) for _ in range(10)]
resp2 = await client.post(
f"/flights/{fid}/images/batch",
data={"metadata": json.dumps(meta)},
files=files
)
assert resp2.status_code == 202
data = resp2.json()
assert data["accepted"] is True
assert data["next_expected"] == 11
@pytest.mark.asyncio
async def test_user_fix(client: AsyncClient):
# 1. Create flight
resp1 = await client.post("/flights", json=FLIGHT_PAYLOAD)
fid = resp1.json()["flight_id"]
# 2. Submit fix
fix_data = {
"frame_id": 5,
"uav_pixel": [1024.0, 768.0],
"satellite_gps": {"lat": 48.11, "lon": 37.22}
}
resp2 = await client.post(f"/flights/{fid}/user-fix", json=fix_data)
assert resp2.status_code == 200
data = resp2.json()
assert data["processing_resumed"] is True
@pytest.mark.asyncio
async def test_flight_status(client: AsyncClient):
# 1. Create
resp1 = await client.post("/flights", json=FLIGHT_PAYLOAD)
fid = resp1.json()["flight_id"]
# 2. Status
resp2 = await client.get(f"/flights/{fid}/status")
assert resp2.status_code == 200
assert resp2.json()["status"] == "created" # The initial state from DB