Spaces:
Sleeping
Sleeping
File size: 5,900 Bytes
7e69b8f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | # tests/test_models.py
"""Tests for AquaGuard-RL data models (Action, Observation, State)."""
import sys
import os
import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
from models import AquaGuardAction, AquaGuardState, ZoneObservation, CropObservation
class TestAquaGuardAction:
"""Tests for AquaGuardAction validation."""
def test_default_action_is_valid(self):
"""Default action should be valid."""
action = AquaGuardAction()
assert action is not None
total = sum(action.crop_allocation.values())
assert total <= 1.001
def test_allocation_sum_valid(self):
"""Valid allocation summing to exactly 1.0."""
action = AquaGuardAction(
crop_allocation={
"rice": 0.20, "wheat": 0.20, "millet": 0.20,
"pulses": 0.15, "oilseeds": 0.15, "vegetables": 0.10,
}
)
assert abs(sum(action.crop_allocation.values()) - 1.0) < 0.01
def test_allocation_sum_too_high_raises(self):
"""Allocation sum > 1.0 should raise ValueError."""
with pytest.raises(Exception): # pydantic ValidationError
AquaGuardAction(
crop_allocation={
"rice": 0.40, "wheat": 0.40, "millet": 0.30,
"pulses": 0.10, "oilseeds": 0.05, "vegetables": 0.03,
}
)
def test_negative_allocation_raises(self):
"""Negative allocation should raise ValueError."""
with pytest.raises(Exception):
AquaGuardAction(
crop_allocation={"rice": -0.1, "wheat": 0.5}
)
def test_invalid_crop_raises(self):
"""Unknown crop in allocation should raise ValueError."""
with pytest.raises(Exception):
AquaGuardAction(crop_allocation={"corn": 0.5, "rice": 0.3})
def test_water_quota_bounds(self):
"""Water quota must be in [0, 2000]."""
# Valid
action = AquaGuardAction(water_quotas={"zone_a": 0, "zone_b": 2000, "zone_c": 500})
assert action.water_quotas["zone_b"] == 2000
# Invalid
with pytest.raises(Exception):
AquaGuardAction(water_quotas={"zone_a": 2001})
def test_invalid_irrigation_method_raises(self):
"""Invalid irrigation method should raise ValueError."""
with pytest.raises(Exception):
AquaGuardAction(irrigation_methods={"zone_a": "canal"})
def test_valid_irrigation_methods(self):
"""All valid irrigation methods should be accepted."""
for method in ["flood", "sprinkler", "drip"]:
action = AquaGuardAction(
irrigation_methods={"zone_a": method, "zone_b": method, "zone_c": method}
)
assert action.irrigation_methods["zone_a"] == method
def test_extraction_limit_bounds(self):
"""Extraction limit must be in [0, 60]."""
with pytest.raises(Exception):
AquaGuardAction(extraction_limits={"zone_a": 61.0})
def test_subsidy_adjustment_bounds(self):
"""Subsidy adjustment must be in [-1, 1]."""
with pytest.raises(Exception):
AquaGuardAction(subsidy_adjustments={"rice": 1.5})
with pytest.raises(Exception):
AquaGuardAction(subsidy_adjustments={"rice": -1.5})
def test_justification_max_length(self):
"""Justification cannot exceed 2000 characters."""
# Valid: empty
action = AquaGuardAction(justification="")
assert action.justification == ""
# Valid: long
action = AquaGuardAction(justification="x" * 2000)
assert len(action.justification) == 2000
def test_partial_allocation_valid(self):
"""Allocation summing to less than 1.0 (fallow land) is valid."""
action = AquaGuardAction(
crop_allocation={"rice": 0.30, "wheat": 0.20}
)
assert sum(action.crop_allocation.values()) < 1.0
class TestAquaGuardState:
"""Tests for AquaGuardState model."""
def test_default_state_valid(self):
"""Default state should be valid."""
state = AquaGuardState()
assert state.task_name == "baseline"
assert state.max_steps == 10
assert state.cumulative_reward == 0.0
def test_state_with_episode_id(self):
"""State with explicit episode_id."""
state = AquaGuardState(episode_id="test-123", step_count=5)
assert state.episode_id == "test-123"
assert state.step_count == 5
class TestZoneObservation:
"""Tests for ZoneObservation model."""
def test_valid_zone_obs(self):
"""Valid zone observation should be created."""
zone = ZoneObservation(
zone_id="zone_a",
groundwater_depth_m=25.0,
groundwater_recharge_rate_mm_yr=180.0,
soil_fertility=0.75,
soil_salinity=0.12,
arable_land_ha=80000.0,
active_irrigation_method="flood",
water_used_mm=900.0,
is_in_danger_zone=False,
is_collapsed=False,
)
assert zone.zone_id == "zone_a"
assert zone.groundwater_depth_m == 25.0
def test_fertility_bounds(self):
"""Soil fertility must be in [0, 1]."""
with pytest.raises(Exception):
ZoneObservation(
zone_id="zone_a",
groundwater_depth_m=25.0,
groundwater_recharge_rate_mm_yr=180.0,
soil_fertility=1.5, # Invalid
soil_salinity=0.12,
arable_land_ha=80000.0,
active_irrigation_method="flood",
water_used_mm=900.0,
is_in_danger_zone=False,
) |