File size: 5,307 Bytes
aefac4f
 
 
9659593
696f787
aefac4f
 
 
 
 
696f787
aefac4f
 
 
 
 
 
 
 
9659593
aefac4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9659593
aefac4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9659593
aefac4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9659593
aefac4f
 
 
 
 
9659593
aefac4f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9659593
aefac4f
 
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
"""
Tests for codebase fixes: confidence cap, validator, thresholds, schema validation
"""

import json
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).parent.parent))

from api.app.models.schemas import HealthResponse, StructuredAnalysisRequest
from api.app.services.extraction import predict_disease_simple as api_predict
from scripts.chat import predict_disease_simple as cli_predict
from src.biomarker_validator import BiomarkerValidator

# ============================================================================
# Confidence cap tests
# ============================================================================


class TestConfidenceCap:
    """Verify confidence never exceeds 1.0"""

    def test_api_confidence_capped_at_one(self):
        # Glucose>126 (+0.4), Glucose>180 (+0.2), HbA1c>=6.5 (+0.5) = 1.1 raw
        result = api_predict({"Glucose": 200, "HbA1c": 7.0})
        assert result["confidence"] <= 1.0

    def test_cli_confidence_capped_at_one(self):
        result = cli_predict({"Glucose": 200, "HbA1c": 7.0})
        assert result["confidence"] <= 1.0

    def test_confidence_is_exactly_one_for_high_diabetes(self):
        result = api_predict({"Glucose": 200, "HbA1c": 7.0})
        assert result["confidence"] == 1.0

    def test_confidence_not_capped_when_below_one(self):
        result = api_predict({"Glucose": 130})
        assert result["confidence"] == 0.4


# ============================================================================
# Updated critical threshold tests
# ============================================================================


class TestCriticalThresholds:
    """Verify biomarker_references.json has clinically appropriate critical thresholds"""

    def setup_method(self):
        config_path = Path(__file__).parent.parent / "config" / "biomarker_references.json"
        with open(config_path) as f:
            self.refs = json.load(f)["biomarkers"]

    def test_glucose_critical_high_is_emergency(self):
        assert self.refs["Glucose"]["critical_high"] >= 300

    def test_glucose_critical_low_is_emergency(self):
        assert self.refs["Glucose"]["critical_low"] <= 54

    def test_hba1c_critical_high_is_emergency(self):
        assert self.refs["HbA1c"]["critical_high"] >= 10

    def test_troponin_critical_high_above_normal(self):
        normal_max = self.refs["Troponin"]["normal_range"]["max"]
        assert self.refs["Troponin"]["critical_high"] > normal_max

    def test_bmi_critical_high_is_morbid(self):
        assert self.refs["BMI"]["critical_high"] >= 40

    def test_systolic_bp_critical_high_is_crisis(self):
        assert self.refs["Systolic Blood Pressure"]["critical_high"] >= 180

    def test_diastolic_bp_critical_low_is_shock(self):
        assert self.refs["Diastolic Blood Pressure"]["critical_low"] <= 40


# ============================================================================
# Validator threshold removal tests
# ============================================================================


class TestValidatorNoThreshold:
    """Verify validator flags all out-of-range values (no 15% threshold)"""

    def setup_method(self):
        self.validator = BiomarkerValidator()

    def test_slightly_high_glucose_is_flagged(self):
        """Glucose=105 is above normal max=100 — should be HIGH, not NORMAL"""
        flag = self.validator.validate_biomarker("Glucose", 105.0)
        assert flag.status == "HIGH"

    def test_slightly_low_hemoglobin_is_flagged(self):
        """Hemoglobin=13.0 for male (min=13.5) should be LOW"""
        flag = self.validator.validate_biomarker("Hemoglobin", 13.0, gender="male")
        assert flag.status == "LOW"

    def test_normal_glucose_stays_normal(self):
        flag = self.validator.validate_biomarker("Glucose", 90.0)
        assert flag.status == "NORMAL"

    def test_critical_high_glucose_flagged(self):
        flag = self.validator.validate_biomarker("Glucose", 500.0)
        assert flag.status == "CRITICAL_HIGH"

    def test_high_glucose_200_not_critical(self):
        """Glucose=200 is above normal but below critical_high=400"""
        flag = self.validator.validate_biomarker("Glucose", 200.0)
        assert flag.status == "HIGH"


# ============================================================================
# Pydantic schema validation tests
# ============================================================================


class TestSchemaValidation:
    """Verify Pydantic models enforce constraints correctly"""

    def test_structured_request_rejects_empty_biomarkers(self):
        import pytest

        with pytest.raises(Exception):
            StructuredAnalysisRequest(biomarkers={})

    def test_structured_request_accepts_valid_biomarkers(self):
        req = StructuredAnalysisRequest(biomarkers={"Glucose": 100.0})
        assert req.biomarkers == {"Glucose": 100.0}

    def test_health_response_uses_llm_status_field(self):
        resp = HealthResponse(
            status="healthy",
            timestamp="2025-01-01T00:00:00",
            llm_status="connected",
            vector_store_loaded=True,
            available_models=["test"],
            uptime_seconds=100.0,
            version="1.0.0",
        )
        assert resp.llm_status == "connected"