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