File size: 3,546 Bytes
05c7321
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import asyncio
import sys
import types

# L'environnement local de test peut ne pas contenir FastAPI.
# Le module endpoint utilise uniquement HTTPException ; ce substitut
# permet donc de tester son contrat sans modifier le code de production.
try:
    import fastapi  # noqa: F401
except ModuleNotFoundError:
    fastapi_stub = types.ModuleType("fastapi")

    class HTTPException(Exception):
        def __init__(
            self,
            status_code: int,
            detail: str,
        ) -> None:
            super().__init__(detail)
            self.status_code = status_code
            self.detail = detail

    fastapi_stub.HTTPException = HTTPException
    sys.modules["fastapi"] = fastapi_stub

from endpoints.validate_numeric import post_validate_numeric
from numeric_core import extract, validate_numeric


EVIDENCE = (
    "Effect sizes were 1.19 and 0.57. "
    "There were 22 studies. "
    "The response rate was 20%. "
    "The dose was 1.5 mg. "
    "The 95% CI was 0.74 to 1.64."
)


def test_decimal_point_with_final_period():
    values = extract("Effect size 1.19.")

    assert len(values) == 1
    assert values[0].kind == "scalar"
    assert values[0].lo == 1.19


def test_decimal_comma_with_final_period():
    values = extract("Taille d'effet 1,19.")

    assert len(values) == 1
    assert values[0].kind == "scalar"
    assert values[0].lo == 1.19


def test_two_plain_decimals_are_extracted():
    values = extract("Values were 1.19 and 0.57.")

    assert [item.lo for item in values] == [1.19, 0.57]


def test_french_decimals_match_exactly():
    result = validate_numeric(
        "Les tailles d'effet étaient 1,19 et 0,57.",
        EVIDENCE,
        rel_tol=0.0,
    )

    assert result["has_numbers"] is True
    assert result["numeric_ok"] is True
    assert result["unparsed_numeric"] is False


def test_modified_decimal_is_rejected_exactly():
    result = validate_numeric(
        "Effect sizes were 1.20 and 0.57.",
        EVIDENCE,
        rel_tol=0.0,
    )

    assert result["numeric_ok"] is False


def test_close_decimal_is_allowed_at_two_percent():
    result = validate_numeric(
        "Effect sizes were 1.20 and 0.57.",
        EVIDENCE,
        rel_tol=0.02,
    )

    assert result["numeric_ok"] is True


def test_large_decimal_difference_is_rejected():
    result = validate_numeric(
        "Effect sizes were 1.30 and 0.57.",
        EVIDENCE,
        rel_tol=0.02,
    )

    assert result["numeric_ok"] is False


def test_specialized_numeric_types_are_preserved():
    assert extract("Dose 1,5 mg.")[0].kind == "dose"
    assert extract("Response 20,5%.")[0].kind == "percent"
    assert extract("95% IC 0,74 à 1,64.")[0].kind == "ci"


def test_contextual_integer_mismatch_is_rejected():
    result = validate_numeric(
        "There were 23 studies.",
        EVIDENCE,
        rel_tol=0.0,
    )

    assert result["numeric_ok"] is False


def test_endpoint_contract_uses_exact_mode():
    payload = {
        "items": [
            {
                "id": 1,
                "claim": "Effect sizes were 1.19 and 0.57.",
                "evidence": EVIDENCE,
            },
            {
                "id": 2,
                "claim": "Effect sizes were 1.20 and 0.57.",
                "evidence": EVIDENCE,
            },
        ],
        "relative_tolerance": 0.0,
    }

    response = asyncio.run(
        post_validate_numeric(payload)
    )

    assert response["results"][0]["numeric_ok"] is True
    assert response["results"][1]["numeric_ok"] is False