File size: 3,975 Bytes
1676aa7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from core.schemas import VisionAssessment, ReportExtraction
from core.need_engine import build_need_profiles, score_vision_severity


def test_high_confidence_collapse_scores_near_base_severity():
    damage_type, severity = score_vision_severity("Damaged_Infrastructure", confidence=0.95)
    assert damage_type == "structural_collapse"
    assert severity > 8.0, f"expected high severity for confident structural collapse, got {severity}"
    print(f"PASS: confident structural-collapse classification -> severity {severity}")


def test_low_confidence_pulls_toward_neutral():
    _, severity_high_conf = score_vision_severity("Fire_Disaster", confidence=0.95)
    _, severity_low_conf = score_vision_severity("Fire_Disaster", confidence=0.20)
    assert severity_low_conf < severity_high_conf, "low classifier confidence should pull severity toward neutral, not stay extreme"
    print(f"PASS: fire severity at 95% conf = {severity_high_conf}, at 20% conf = {severity_low_conf}")


def test_non_damage_scores_low():
    _, severity = score_vision_severity("Non_Damage", confidence=0.9)
    assert severity < 3.0
    print(f"PASS: Non_Damage classification scores low severity ({severity})")


def test_more_people_affected_raises_priority_all_else_equal():
    vision = {
        "A": VisionAssessment("A", "structural_collapse", "Damaged_Infrastructure", 0.9, "cap", 8.0),
        "B": VisionAssessment("B", "structural_collapse", "Damaged_Infrastructure", 0.9, "cap", 8.0),
    }
    reports = {
        "A": ReportExtraction("A", people_affected=5, need_types=["rescue"], urgency=7),
        "B": ReportExtraction("B", people_affected=80, need_types=["rescue"], urgency=7),
    }
    profiles = build_need_profiles(vision, reports, {"A": "A", "B": "B"}, {"A": (0, 0), "B": (0, 0)})
    a = next(p for p in profiles if p.location_id == "A")
    b = next(p for p in profiles if p.location_id == "B")
    assert b.priority_score > a.priority_score, "more people affected (all else equal) must raise priority score"
    print(f"PASS: A (5 people) priority={a.priority_score}, B (80 people) priority={b.priority_score}")


def test_missing_report_falls_back_gracefully():
    vision = {"A": VisionAssessment("A", "fire", "Fire_Disaster", 0.8, "smoke visible", 7.0)}
    profiles = build_need_profiles(vision, {}, {"A": "Zone A"}, {"A": (1.0, 2.0)})
    assert len(profiles) == 1
    assert profiles[0].people_affected == 0
    print("PASS: location with vision data but no report does not crash, defaults people_affected=0")


def test_missing_vision_falls_back_gracefully():
    reports = {"A": ReportExtraction("A", people_affected=15, need_types=["medical"], urgency=6)}
    profiles = build_need_profiles({}, reports, {"A": "Zone A"}, {"A": (1.0, 2.0)})
    assert len(profiles) == 1
    assert profiles[0].severity == 5.0  # NEUTRAL_SEVERITY fallback
    print("PASS: location with report but no image does not crash, defaults to neutral severity")


def test_profiles_sorted_by_priority_descending():
    vision = {
        "A": VisionAssessment("A", "no_significant_damage", "Non_Damage", 0.9, "minor", 1.0),
        "B": VisionAssessment("B", "structural_collapse", "Damaged_Infrastructure", 0.95, "collapse", 9.0),
    }
    reports = {
        "A": ReportExtraction("A", people_affected=5, need_types=[], urgency=2),
        "B": ReportExtraction("B", people_affected=50, need_types=["medical", "rescue"], urgency=9),
    }
    profiles = build_need_profiles(vision, reports, {"A": "A", "B": "B"}, {"A": (0, 0), "B": (0, 0)})
    assert profiles[0].location_id == "B", "highest priority location must sort first"
    print("PASS: profiles sorted with highest priority first")


if __name__ == "__main__":
    tests = [v for k, v in list(globals().items()) if k.startswith("test_")]
    for t in tests:
        t()
    print(f"\n{len(tests)}/{len(tests)} need-engine tests passed.")