File size: 8,010 Bytes
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
 
 
 
eadbc29
 
 
 
 
 
6a28f91
 
 
eadbc29
 
6a28f91
 
 
eadbc29
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
6a28f91
eadbc29
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
6a28f91
eadbc29
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6a28f91
eadbc29
 
 
 
 
 
 
 
 
 
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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
"""Pytest configuration and shared fixtures for court scheduling tests.

Provides common fixtures for:
- Sample cases with realistic data
- Courtrooms with various configurations
- Parameter loaders
- Temporary directories
- Pre-trained RL agents
"""

import tempfile
from datetime import date, datetime, timedelta
from pathlib import Path
from typing import List

import pytest

from src.core.case import Case, CaseStatus
from src.core.courtroom import Courtroom
from src.data.case_generator import CaseGenerator
from src.data.param_loader import ParameterLoader


# Test markers
def pytest_configure(config):
    """Configure custom pytest markers."""
    config.addinivalue_line("markers", "unit: Unit tests for individual components")
    config.addinivalue_line(
        "markers", "integration: Integration tests for multi-component workflows"
    )
    config.addinivalue_line("markers", "rl: Reinforcement learning tests")
    config.addinivalue_line("markers", "simulation: Simulation engine tests")
    config.addinivalue_line(
        "markers", "edge_case: Edge case and boundary condition tests"
    )
    config.addinivalue_line("markers", "failure: Failure scenario tests")
    config.addinivalue_line("markers", "slow: Slow-running tests (>5 seconds)")


@pytest.fixture
def sample_cases() -> List[Case]:
    """Generate 100 realistic test cases.

    Returns:
        List of 100 cases with diverse types, stages, and ages
    """
    generator = CaseGenerator(start=date(2024, 1, 1), end=date(2024, 3, 31), seed=42)
    cases = generator.generate(100, stage_mix_auto=True)
    return cases


@pytest.fixture
def small_case_set() -> List[Case]:
    """Generate 10 test cases for quick tests.

    Returns:
        List of 10 cases
    """
    generator = CaseGenerator(start=date(2024, 1, 1), end=date(2024, 1, 10), seed=42)
    cases = generator.generate(10)
    return cases


@pytest.fixture
def single_case() -> Case:
    """Create a single test case.

    Returns:
        Single Case object in ADMISSION stage
    """
    return Case(
        case_id="TEST-001",
        case_type="RSA",
        filed_date=date(2024, 1, 1),
        current_stage="ADMISSION",
        last_hearing_date=None,
        age_days=30,
        hearing_count=0,
        status=CaseStatus.PENDING,
    )


@pytest.fixture
def ripe_case() -> Case:
    """Create a case that should be classified as RIPE.

    Returns:
        Case with sufficient hearings and proper service
    """
    case = Case(
        case_id="RIPE-001",
        case_type="RSA",
        filed_date=date(2024, 1, 1),
        current_stage="ARGUMENTS",
        last_hearing_date=date(2024, 2, 1),
        age_days=90,
        hearing_count=5,
        status=CaseStatus.ACTIVE,
    )
    # Set additional attributes that may be needed
    if hasattr(case, "service_status"):
        case.service_status = "SERVED"
    if hasattr(case, "compliance_status"):
        case.compliance_status = "COMPLIED"
    return case


@pytest.fixture
def unripe_case() -> Case:
    """Create a case that should be classified as UNRIPE.

    Returns:
        Case with service pending (UNRIPE_SUMMONS)
    """
    case = Case(
        case_id="UNRIPE-001",
        case_type="CRP",
        filed_date=date(2024, 1, 1),
        current_stage="PRE-ADMISSION",
        last_hearing_date=None,
        age_days=15,
        hearing_count=1,
        status=CaseStatus.PENDING,
    )
    # Set additional attributes
    if hasattr(case, "service_status"):
        case.service_status = "PENDING"
    if hasattr(case, "last_hearing_purpose"):
        case.last_hearing_purpose = "FOR ISSUE OF SUMMONS"
    return case


@pytest.fixture
def courtrooms() -> List[Courtroom]:
    """Create 5 courtrooms with realistic configurations.

    Returns:
        List of 5 courtrooms with varied capacities
    """
    return [
        Courtroom(courtroom_id=1, judge_id="J001", daily_capacity=50),
        Courtroom(courtroom_id=2, judge_id="J002", daily_capacity=50),
        Courtroom(courtroom_id=3, judge_id="J003", daily_capacity=45),
        Courtroom(courtroom_id=4, judge_id="J004", daily_capacity=55),
        Courtroom(courtroom_id=5, judge_id="J005", daily_capacity=50),
    ]


@pytest.fixture
def single_courtroom() -> Courtroom:
    """Create a single courtroom for simple tests.

    Returns:
        Single courtroom with capacity 50
    """
    return Courtroom(courtroom_id=1, judge_id="J001", daily_capacity=50)


@pytest.fixture
def param_loader() -> ParameterLoader:
    """Create a parameter loader with default parameters.

    Returns:
        ParameterLoader instance
    """
    return ParameterLoader()


@pytest.fixture
def temp_output_dir():
    """Create a temporary output directory for test artifacts.

    Yields:
        Path to temporary directory (cleaned up after test)
    """
    with tempfile.TemporaryDirectory() as tmpdir:
        yield Path(tmpdir)


@pytest.fixture
def test_date() -> date:
    """Standard test date for reproducibility.

    Returns:
        date(2024, 6, 15) - a Saturday in the middle of the year
    """
    return date(2024, 6, 15)


@pytest.fixture
def test_datetime() -> datetime:
    """Standard test datetime for reproducibility.

    Returns:
        datetime(2024, 6, 15, 10, 0, 0)
    """
    return datetime(2024, 6, 15, 10, 0, 0)


@pytest.fixture
def disposed_case() -> Case:
    """Create a case that has been disposed.

    Returns:
        Case in DISPOSED status
    """
    case = Case(
        case_id="DISPOSED-001",
        case_type="CP",
        filed_date=date(2024, 1, 1),
        current_stage="ORDERS",
        last_hearing_date=date(2024, 3, 15),
        age_days=180,
        hearing_count=8,
        status=CaseStatus.DISPOSED,
    )
    return case


@pytest.fixture
def aged_case() -> Case:
    """Create an old case with many hearings.

    Returns:
        Case pending for 2+ years with 20+ hearings
    """
    case = Case(
        case_id="AGED-001",
        case_type="RSA",
        filed_date=date(2022, 1, 1),
        current_stage="EVIDENCE",
        last_hearing_date=date(2024, 5, 1),
        age_days=800,
        hearing_count=25,
        status=CaseStatus.ACTIVE,
    )
    return case


@pytest.fixture
def urgent_case() -> Case:
    """Create an urgent case (filed recently, high priority).

    Returns:
        Case with urgency flag
    """
    case = Case(
        case_id="URGENT-001",
        case_type="CMP",
        filed_date=date(2024, 6, 1),
        current_stage="ADMISSION",
        last_hearing_date=None,
        age_days=5,
        hearing_count=0,
        status=CaseStatus.PENDING,
        is_urgent=True,
    )
    return case


# Helper functions for tests


def assert_valid_case(case: Case):
    """Assert that a case has all required fields and valid values.

    Args:
        case: Case to validate
    """
    assert case.case_id is not None
    assert case.case_type in ["RSA", "CRP", "RFA", "CA", "CCC", "CP", "MISC.CVL", "CMP"]
    assert case.filed_date is not None
    assert case.current_stage is not None
    assert case.age_days >= 0
    assert case.hearing_count >= 0
    assert case.status in list(CaseStatus)


def create_case_with_hearings(n_hearings: int, days_between: int = 30) -> Case:
    """Create a case with a specific number of hearings.

    Args:
        n_hearings: Number of hearings to record
        days_between: Days between each hearing

    Returns:
        Case with hearing history
    """
    case = Case(
        case_id=f"MULTI-HEARING-{n_hearings}",
        case_type="RSA",
        filed_date=date(2024, 1, 1),
        current_stage="ARGUMENTS",
        status=CaseStatus.ACTIVE,
    )

    current_date = date(2024, 1, 1)
    for i in range(n_hearings):
        current_date += timedelta(days=days_between)
        outcome = "HEARD" if i % 3 != 0 else "ADJOURNED"
        was_heard = outcome == "HEARD"
        case.record_hearing(current_date, was_heard=was_heard, outcome=outcome)

    return case