Spaces:
Sleeping
Sleeping
File size: 6,134 Bytes
49e9f9d 5d63bd6 49e9f9d | 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 | import pytest
from unittest.mock import AsyncMock
from bson import ObjectId
from app.core.security import create_token
from app.services.verification import compute_verified_score
from app.services import weather as weather_svc
from app.services.ai import triage, detect_language, classify_urgency, generate_headline, similarity, is_duplicate
def _token(role="volunteer"):
return create_token({"sub": str(ObjectId()), "role": role})
def test_verified_score_caps_at_100():
assert compute_verified_score(99, 99, True) == 100
def test_verified_score_zero_when_no_signals():
assert compute_verified_score(0, 0, False) == 0
def test_verified_score_each_source_capped():
# witnesses alone capped at 40
assert compute_verified_score(100, 0, False) == 40
# corroboration alone capped at 40
assert compute_verified_score(0, 100, False) == 40
# weather adds exactly 20
assert compute_verified_score(0, 0, True) == 20
def test_weather_supports_flood_on_heavy_rain():
assert weather_svc.supports_category("flood", {"precipitation_mm": 8, "wind_kph": 0, "code": 0})
assert not weather_svc.supports_category("flood", {"precipitation_mm": 0, "wind_kph": 0, "code": 0})
def test_weather_supports_fire_on_dry_wind():
assert weather_svc.supports_category("fire", {"precipitation_mm": 0, "wind_kph": 30, "code": 0})
assert not weather_svc.supports_category("fire", {"precipitation_mm": 2, "wind_kph": 30, "code": 0})
def test_weather_supports_returns_false_for_unknown_categories():
assert not weather_svc.supports_category("medical", {"precipitation_mm": 100, "wind_kph": 100})
assert not weather_svc.supports_category("flood", None)
# --- AI triage tests (use heuristic fallback via NA_DISABLE_AI_MODEL=1) ---
def test_triage_critical_detects_unconscious():
t = triage("Man collapsed and is unconscious, not breathing")
assert t.urgency == "CRITICAL"
assert "unconscious" in t.triggers
assert t.priority_score >= 80
def test_triage_detects_child_vulnerability():
t = triage("A bachcha is trapped inside the flooded building")
assert t.vulnerability == "child"
def test_triage_detects_elderly_vulnerability():
t = triage("Elderly man alone and injured after the accident")
assert t.vulnerability == "elderly"
def test_triage_time_sensitivity_immediate():
t = triage("Need help immediately, right now")
assert t.time_sensitivity == "immediate"
def test_triage_time_sensitivity_days():
t = triage("Can someone come tomorrow to check the power line")
assert t.time_sensitivity == "days"
def test_detect_language_devanagari():
assert detect_language("मदद चाहिए जल्दी") == "hi"
def test_detect_language_hinglish():
assert detect_language("aag lagi hai madad karo") == "hi-Latn"
def test_detect_language_english():
assert detect_language("fire in the building help") == "en"
def test_backcompat_classify_urgency_signature():
urgency, reason = classify_urgency("person is bleeding heavily")
assert urgency == "CRITICAL"
assert reason # non-empty
# --- headline + similarity ---
def test_headline_prefers_first_sentence():
assert generate_headline("Fire in the kitchen. Everyone is safe.") == "Fire in the kitchen."
def test_headline_truncates_long_text():
long_text = "a very long description " * 20
h = generate_headline(long_text, max_len=50)
assert len(h) <= 51 # 50 + ellipsis
assert h.endswith("…")
def test_headline_empty():
assert generate_headline("") == ""
def test_similarity_identical_is_one():
s = "fire near park gate"
assert similarity(s, s) == 1.0
def test_similarity_disjoint_is_low():
assert similarity("fire in kitchen", "cat chased mouse") < 0.2
def test_is_duplicate_detects_near_match():
a = "fire in the building near gate 3"
b = "there is a fire near gate 3 in the building"
assert is_duplicate(a, b)
def test_is_duplicate_rejects_unrelated():
assert not is_duplicate("fire near park", "child lost at market")
@pytest.mark.asyncio
async def test_witness_rejects_own_alert(client):
c, db = client
alert_id = ObjectId()
token = _token("volunteer")
# The JWT sub becomes the user id; make the alert owned by the same id
import jose.jwt as jj
from app.core.config import settings
sub = jj.decode(token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM])["sub"]
db.alerts.find_one = AsyncMock(
return_value={
"_id": alert_id,
"reporter_id": ObjectId(sub),
"status": "open",
"location": {"type": "Point", "coordinates": [76.7794, 30.7333]},
}
)
resp = await c.post(
f"/api/alerts/{alert_id}/witness",
headers={"Authorization": f"Bearer {token}"},
)
assert resp.status_code == 400
@pytest.mark.asyncio
async def test_witness_rejects_far_user(client):
c, db = client
alert_id = ObjectId()
token = _token("volunteer")
import jose.jwt as jj
from app.core.config import settings
sub = jj.decode(token, settings.JWT_SECRET, algorithms=[settings.JWT_ALGORITHM])["sub"]
db.alerts.find_one = AsyncMock(
return_value={
"_id": alert_id,
"reporter_id": ObjectId(), # someone else
"status": "open",
"location": {"type": "Point", "coordinates": [76.7794, 30.7333]},
}
)
db.users.find_one = AsyncMock(
return_value={
"_id": ObjectId(sub),
# >2 km away
"location": {"type": "Point", "coordinates": [77.2090, 28.6139]},
}
)
resp = await c.post(
f"/api/alerts/{alert_id}/witness",
headers={"Authorization": f"Bearer {token}"},
)
assert resp.status_code == 403
@pytest.mark.asyncio
async def test_witness_missing_alert(client):
c, db = client
db.alerts.find_one = AsyncMock(return_value=None)
resp = await c.post(
f"/api/alerts/{ObjectId()}/witness",
headers={"Authorization": f"Bearer {_token()}"},
)
assert resp.status_code == 404
|