Spaces:
Running
Running
| """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)") | |
| 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 | |
| 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 | |
| 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, | |
| ) | |
| 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 | |
| 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 | |
| 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), | |
| ] | |
| 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) | |
| def param_loader() -> ParameterLoader: | |
| """Create a parameter loader with default parameters. | |
| Returns: | |
| ParameterLoader instance | |
| """ | |
| return ParameterLoader() | |
| 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) | |
| 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) | |
| 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) | |
| 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 | |
| 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 | |
| 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 | |