mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-22 07:06:38 +00:00
170 lines
7.1 KiB
Python
170 lines
7.1 KiB
Python
import pytest
|
|
import asyncio
|
|
from fastapi.testclient import TestClient
|
|
from unittest.mock import Mock, MagicMock
|
|
from datetime import datetime
|
|
from fastapi import FastAPI
|
|
|
|
from f01_flight_api import router, get_lifecycle_manager, get_flight_database
|
|
from f02_1_flight_lifecycle_manager import FlightLifecycleManager, Flight, GPSPoint, CameraParameters, FlightState, Waypoint
|
|
|
|
# --- Setup ---
|
|
|
|
app = FastAPI()
|
|
app.include_router(router)
|
|
|
|
mock_manager = Mock(spec=FlightLifecycleManager)
|
|
mock_db = Mock()
|
|
|
|
app.dependency_overrides[get_lifecycle_manager] = lambda: mock_manager
|
|
app.dependency_overrides[get_flight_database] = lambda: mock_db
|
|
|
|
client = TestClient(app)
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def reset_mocks():
|
|
mock_manager.reset_mock()
|
|
mock_db.reset_mock()
|
|
|
|
# --- Unit Tests ---
|
|
|
|
class TestUnitFlightManagement:
|
|
"""Unit tests defined in 01.01_feature_flight_management.md"""
|
|
|
|
def test_create_flight_validation(self):
|
|
valid_payload = {
|
|
"name": "Test Flight",
|
|
"description": "Validation test",
|
|
"start_gps": {"lat": 48.0, "lon": 37.0},
|
|
"altitude": 400.0,
|
|
"camera_params": {"focal_length_mm": 25.0, "sensor_width_mm": 36.0, "resolution": {"width": 6000, "height": 4000}}
|
|
}
|
|
|
|
# 1. Valid request
|
|
mock_manager.create_flight.return_value = "flight_123"
|
|
resp = client.post("/api/v1/flights", json=valid_payload)
|
|
assert resp.status_code == 201
|
|
assert resp.json()["flight_id"] == "flight_123"
|
|
|
|
# 2. Missing required field (name)
|
|
invalid_payload = valid_payload.copy()
|
|
del invalid_payload["name"]
|
|
resp = client.post("/api/v1/flights", json=invalid_payload)
|
|
assert resp.status_code == 422
|
|
|
|
# 3. Invalid camera params (missing required resolution)
|
|
invalid_camera = valid_payload.copy()
|
|
invalid_camera["camera_params"] = {"focal_length_mm": 25.0}
|
|
resp = client.post("/api/v1/flights", json=invalid_camera)
|
|
assert resp.status_code == 422
|
|
|
|
def test_get_flight(self):
|
|
# 1. Valid flight_id
|
|
mock_flight = Flight(
|
|
flight_id="flight_123", flight_name="Test Mission",
|
|
start_gps=GPSPoint(lat=48.0, lon=37.0), altitude_m=400.0,
|
|
camera_params=CameraParameters(focal_length_mm=25, sensor_width_mm=23.5, resolution={"width": 6000, "height": 4000})
|
|
)
|
|
mock_manager.get_flight.return_value = mock_flight
|
|
mock_manager.get_flight_state.return_value = FlightState(flight_id="flight_123", state="active", processed_images=10, total_images=100, has_active_engine=True)
|
|
mock_db.get_waypoints.return_value = []
|
|
|
|
resp = client.get("/api/v1/flights/flight_123")
|
|
assert resp.status_code == 200
|
|
assert resp.json()["flight_id"] == "flight_123"
|
|
assert resp.json()["status"] == "active"
|
|
|
|
# 2. Non-existent flight_id
|
|
mock_manager.get_flight.return_value = None
|
|
resp = client.get("/api/v1/flights/missing_flight")
|
|
assert resp.status_code == 404
|
|
|
|
def test_delete_flight(self):
|
|
# 1. Valid flight_id
|
|
mock_manager.delete_flight.return_value = True
|
|
resp = client.delete("/api/v1/flights/flight_123")
|
|
assert resp.status_code == 200
|
|
assert resp.json()["deleted"] is True
|
|
|
|
# 2. Non-existent flight_id
|
|
mock_manager.delete_flight.return_value = False
|
|
resp = client.delete("/api/v1/flights/missing_flight")
|
|
assert resp.status_code == 404
|
|
|
|
def test_get_flight_status(self):
|
|
# 1. Valid flight_id
|
|
mock_manager.get_flight_state.return_value = FlightState(flight_id="flight_123", state="processing", processed_images=50, total_images=100, has_active_engine=True)
|
|
resp = client.get("/api/v1/flights/flight_123/status")
|
|
assert resp.status_code == 200
|
|
assert resp.json()["status"] == "processing"
|
|
assert resp.json()["frames_processed"] == 50
|
|
|
|
# 2. Non-existent flight_id
|
|
mock_manager.get_flight_state.return_value = None
|
|
resp = client.get("/api/v1/flights/missing_flight/status")
|
|
assert resp.status_code == 404
|
|
|
|
def test_update_waypoint(self):
|
|
waypoint_payload = {
|
|
"id": "wp1",
|
|
"lat": 48.0,
|
|
"lon": 37.0,
|
|
"confidence": 0.9,
|
|
"timestamp": datetime.utcnow().isoformat(),
|
|
"refined": False
|
|
}
|
|
|
|
# 1. Valid update
|
|
mock_db.update_waypoint.return_value = True
|
|
resp = client.put("/api/v1/flights/flight_123/waypoints/wp1", json=waypoint_payload)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["updated"] is True
|
|
|
|
# 2. Invalid waypoint_id
|
|
mock_db.update_waypoint.return_value = False
|
|
resp = client.put("/api/v1/flights/flight_123/waypoints/missing_wp", json=waypoint_payload)
|
|
assert resp.status_code == 404
|
|
|
|
def test_batch_update_waypoints(self):
|
|
payload = [
|
|
{"id": "wp1", "lat": 48.0, "lon": 37.0, "altitude": 400.0, "confidence": 0.9, "timestamp": datetime.utcnow().isoformat(), "refined": False},
|
|
{"id": "wp2", "lat": 48.1, "lon": 37.1, "altitude": 400.0, "confidence": 0.9, "timestamp": datetime.utcnow().isoformat(), "refined": False}
|
|
]
|
|
|
|
# 1. Valid batch
|
|
mock_db.batch_update_waypoints.return_value = Mock(failed_ids=[], updated_count=2)
|
|
resp = client.put("/api/v1/flights/flight_123/waypoints/batch", json=payload)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["success"] is True
|
|
assert resp.json()["updated_count"] == 2
|
|
|
|
# 2. Partial failures
|
|
mock_db.batch_update_waypoints.return_value = Mock(failed_ids=["wp2"], updated_count=1)
|
|
resp = client.put("/api/v1/flights/flight_123/waypoints/batch", json=payload)
|
|
assert resp.status_code == 200
|
|
assert resp.json()["success"] is False
|
|
assert "wp2" in resp.json()["failed_ids"]
|
|
|
|
# --- Integration Tests ---
|
|
|
|
class TestIntegrationFlightManagement:
|
|
"""Integration tests defined in 01.01_feature_flight_management.md"""
|
|
|
|
def test_flight_lifecycle(self):
|
|
"""Tests POST -> GET -> DELETE lifecycle"""
|
|
valid_payload = {
|
|
"name": "Lifecycle Mission",
|
|
"start_gps": {"lat": 48.0, "lon": 37.0},
|
|
"altitude": 400.0,
|
|
"camera_params": {"focal_length_mm": 25.0, "sensor_width_mm": 36.0, "resolution": {"width": 640, "height": 480}}
|
|
}
|
|
|
|
mock_manager.create_flight.return_value = "lifecycle_123"
|
|
post_resp = client.post("/api/v1/flights", json=valid_payload)
|
|
assert post_resp.status_code == 201
|
|
|
|
mock_manager.get_flight.return_value = Flight(flight_id="lifecycle_123", flight_name="Lifecycle Mission", start_gps=GPSPoint(lat=48.0, lon=37.0), altitude_m=400.0, camera_params=CameraParameters(**valid_payload["camera_params"]))
|
|
mock_manager.get_flight_state.return_value = FlightState(flight_id="lifecycle_123", state="prefetching", processed_images=0, total_images=0, has_active_engine=False)
|
|
get_resp = client.get("/api/v1/flights/lifecycle_123")
|
|
assert get_resp.status_code == 200
|
|
assert get_resp.json()["name"] == "Lifecycle Mission" |