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,
            )