Initial commit

This commit is contained in:
Denys Zaitsev
2026-04-03 23:25:54 +03:00
parent 531a1301d5
commit d7e1066c60
3843 changed files with 1554468 additions and 0 deletions
+170
View File
@@ -0,0 +1,170 @@
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"