mirror of
https://github.com/azaion/gps-denied-onboard.git
synced 2026-04-23 03:46:37 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
import pytest
|
||||
import os
|
||||
import yaml
|
||||
import tempfile
|
||||
from unittest.mock import Mock
|
||||
|
||||
from f02_1_flight_lifecycle_manager import CameraParameters
|
||||
from f17_configuration_manager import (
|
||||
ConfigurationManager, SystemConfig, OperationalArea,
|
||||
ModelPaths, FlightConfig
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def valid_yaml_content():
|
||||
return """
|
||||
camera:
|
||||
focal_length_mm: 35.0
|
||||
sensor_width_mm: 36.0
|
||||
resolution:
|
||||
width: 3840
|
||||
height: 2160
|
||||
operational_area:
|
||||
name: "Test Area"
|
||||
min_lat: 40.0
|
||||
max_lat: 45.0
|
||||
min_lon: -10.0
|
||||
max_lon: 10.0
|
||||
"""
|
||||
|
||||
@pytest.fixture
|
||||
def temp_config_file(valid_yaml_content):
|
||||
fd, path = tempfile.mkstemp(suffix=".yaml")
|
||||
with os.fdopen(fd, 'w') as f:
|
||||
f.write(valid_yaml_content)
|
||||
yield path
|
||||
os.remove(path)
|
||||
|
||||
@pytest.fixture
|
||||
def mock_db():
|
||||
db = Mock()
|
||||
flight_mock = Mock()
|
||||
flight_mock.camera_params = CameraParameters(focal_length_mm=25, sensor_width_mm=36, resolution={"width": 1920, "height": 1080})
|
||||
flight_mock.altitude_m = 400.0
|
||||
db.get_flight_by_id.return_value = flight_mock
|
||||
return db
|
||||
|
||||
@pytest.fixture
|
||||
def cm(mock_db):
|
||||
return ConfigurationManager(f03_database=mock_db)
|
||||
|
||||
class TestConfigurationManager:
|
||||
|
||||
# --- 17.01 Feature: System Configuration ---
|
||||
|
||||
def test_load_config_valid_yaml(self, cm, temp_config_file):
|
||||
config = cm.load_config(temp_config_file)
|
||||
assert isinstance(config, SystemConfig)
|
||||
assert config.camera.focal_length_mm == 35.0
|
||||
assert config.operational_area.name == "Test Area"
|
||||
|
||||
def test_load_config_missing_file_uses_defaults(self, cm):
|
||||
config = cm.load_config("nonexistent_file.yaml")
|
||||
assert config.camera.focal_length_mm == 25.0 # Default value
|
||||
assert config.operational_area.name == "Eastern Ukraine"
|
||||
|
||||
def test_load_config_invalid_yaml_raises_error(self, cm):
|
||||
fd, path = tempfile.mkstemp(suffix=".yaml")
|
||||
with os.fdopen(fd, 'w') as f:
|
||||
f.write("invalid: yaml: : syntax")
|
||||
|
||||
with pytest.raises(ValueError, match="Malformed YAML"):
|
||||
cm.load_config(path)
|
||||
os.remove(path)
|
||||
|
||||
def test_load_config_partial_uses_defaults(self, cm):
|
||||
fd, path = tempfile.mkstemp(suffix=".yaml")
|
||||
with os.fdopen(fd, 'w') as f:
|
||||
f.write("camera:\n focal_length_mm: 50.0\n")
|
||||
|
||||
config = cm.load_config(path)
|
||||
assert config.camera.focal_length_mm == 50.0
|
||||
assert config.camera.sensor_width_mm == 36.0 # Kept default
|
||||
assert config.database.url == "sqlite:///flights.db" # Default database
|
||||
os.remove(path)
|
||||
|
||||
def test_validate_config_valid(self, cm):
|
||||
config = cm._apply_defaults({})
|
||||
val = cm.validate_config(config)
|
||||
assert val.is_valid is True
|
||||
|
||||
def test_validate_config_invalid_focal_length(self, cm):
|
||||
config = cm._apply_defaults({})
|
||||
config.camera.focal_length_mm = -10.0
|
||||
val = cm.validate_config(config)
|
||||
assert val.is_valid is False
|
||||
assert any("Focal length" in e for e in val.errors)
|
||||
|
||||
def test_validate_config_invalid_operational_area(self, cm):
|
||||
config = cm._apply_defaults({})
|
||||
config.operational_area.min_lat = 100.0 # Invalid lat
|
||||
val = cm.validate_config(config)
|
||||
assert val.is_valid is False
|
||||
assert any("latitude bounds" in e for e in val.errors)
|
||||
|
||||
def test_get_camera_params_default(self, cm, temp_config_file):
|
||||
cm.load_config(temp_config_file)
|
||||
params = cm.get_camera_params()
|
||||
assert params.focal_length_mm == 35.0
|
||||
assert params.resolution["width"] == 3840
|
||||
|
||||
def test_update_config_valid_key(self, cm, temp_config_file):
|
||||
cm.load_config(temp_config_file)
|
||||
assert cm.update_config("operational_area", "name", "Western Ukraine") is True
|
||||
assert cm._system_config.operational_area.name == "Western Ukraine"
|
||||
|
||||
def test_update_config_invalid_section(self, cm, temp_config_file):
|
||||
cm.load_config(temp_config_file)
|
||||
assert cm.update_config("nonexistent", "key", "value") is False
|
||||
|
||||
# --- 17.02 Feature: Flight Configuration ---
|
||||
|
||||
def test_get_flight_config_existing_via_db(self, cm):
|
||||
# Should use mock_db to fetch the config
|
||||
config = cm.get_flight_config("flight_123")
|
||||
assert isinstance(config, FlightConfig)
|
||||
assert config.altitude == 400.0
|
||||
assert config.camera_params.focal_length_mm == 25.0
|
||||
|
||||
def test_get_flight_config_nonexistent(self, cm):
|
||||
cm.db.get_flight_by_id.return_value = None
|
||||
with pytest.raises(ValueError, match="not found"):
|
||||
cm.get_flight_config("missing_flight")
|
||||
|
||||
def test_save_flight_config_valid(self, cm):
|
||||
config = FlightConfig(
|
||||
camera_params=CameraParameters(focal_length_mm=50, sensor_width_mm=36, resolution={"width": 100, "height": 100}),
|
||||
altitude=300.0
|
||||
)
|
||||
assert cm.save_flight_config("flight_saved", config) is True
|
||||
|
||||
# Verify retrieval via cache instead of DB
|
||||
retrieved = cm.get_flight_config("flight_saved")
|
||||
assert retrieved.altitude == 300.0
|
||||
|
||||
def test_save_flight_config_invalid_flight_id(self, cm):
|
||||
assert cm.save_flight_config("", None) is False
|
||||
|
||||
def test_get_operational_altitude_existing(self, cm):
|
||||
assert cm.get_operational_altitude("flight_123") == 400.0
|
||||
|
||||
def test_get_frame_spacing_existing(self, cm):
|
||||
assert cm.get_frame_spacing("flight_123") == 100.0
|
||||
|
||||
def test_get_frame_spacing_default(self, cm):
|
||||
cm.db.get_flight_by_id.return_value = None
|
||||
# Should fallback to default 100.0 on error
|
||||
assert cm.get_frame_spacing("missing_flight") == 100.0
|
||||
Reference in New Issue
Block a user