forked from ChelseaKR/outcome-receipts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_mapping.py
More file actions
135 lines (114 loc) · 4.82 KB
/
Copy pathtest_mapping.py
File metadata and controls
135 lines (114 loc) · 4.82 KB
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
from __future__ import annotations
import json
from pathlib import Path
from outcome_receipts.cli import EXIT_OK, EXIT_VERIFY_FAIL, main
from outcome_receipts.mapping import build_mapping_queue
def _files(tmp_path: Path, requirement: dict[str, object]) -> tuple[Path, Path]:
data = tmp_path / "export.csv"
data.write_text("PersonalID,ProjectStartDate,Destination\nP1,2025-01-01,Permanent\n")
requirements = tmp_path / "requirements.json"
requirements.write_text(json.dumps({"requirements": [requirement]}))
return data, requirements
def test_known_hmis_aliases_produce_review_required_candidate(tmp_path: Path) -> None:
data, requirements = _files(
tmp_path,
{
"metric_id": "clients_served",
"description": "Unduplicated clients",
"definition": "Distinct participants in the export.",
"aggregation": "count_distinct",
"field": "client_id",
},
)
queue = build_mapping_queue(data, requirements)
candidate = queue.candidates[0]
assert queue.ok
assert queue.requires_human_review
assert candidate.status == "review_required"
assert candidate.decision == "pending"
assert candidate.confidence == 0.9
assert candidate.metric_spec is not None
assert candidate.metric_spec["value_sql"] == 'SELECT COUNT(DISTINCT "PersonalID") FROM data'
def test_filter_fields_are_mapped_and_sql_literals_are_escaped(tmp_path: Path) -> None:
data, requirements = _files(
tmp_path,
{
"metric_id": "permanent_exits",
"aggregation": "count_rows",
"filters": [{"field": "exit_destination", "equals": "Permanent's"}],
},
)
candidate = build_mapping_queue(data, requirements).candidates[0]
assert candidate.metric_spec is not None
assert candidate.metric_spec["value_sql"] == (
"SELECT COUNT(*) FROM data WHERE \"Destination\" = 'Permanent''s'"
)
assert str(candidate.metric_spec["slice_sql"]).startswith("SELECT * FROM data WHERE")
def test_zero_field_matches_reports_minimum_not_maximum_confidence(tmp_path: Path) -> None:
"""An unfiltered count_rows requirement maps no logical field at all, so
the candidate's field_matches is empty. The deterministic matcher never
checked anything for this candidate, so it must not report the maximum
confidence score (1.00) in the review-queue JSON -- that reads as "fully
verified" to a reviewer skimming for the low-confidence rows to check.
"""
data, requirements = _files(
tmp_path,
{
"metric_id": "clients_served_total",
"aggregation": "count_rows",
},
)
candidate = build_mapping_queue(data, requirements).candidates[0]
assert candidate.status == "review_required"
assert candidate.field_matches == ()
assert candidate.confidence == 0.0
def test_missing_field_is_blocked_without_metric_spec(tmp_path: Path) -> None:
data, requirements = _files(
tmp_path,
{"metric_id": "income", "aggregation": "count_distinct", "field": "income_id"},
)
candidate = build_mapping_queue(data, requirements).candidates[0]
assert candidate.status == "blocked"
assert candidate.metric_spec is None
assert "no source column" in candidate.blockers[0]
def test_ambiguous_aliases_fail_closed(tmp_path: Path) -> None:
data = tmp_path / "export.csv"
data.write_text("PersonalID,ParticipantID\nP1,P1\n")
requirements = tmp_path / "requirements.json"
requirements.write_text(
json.dumps(
{
"requirements": [
{
"metric_id": "clients",
"aggregation": "count_distinct",
"field": "client_id",
}
]
}
)
)
candidate = build_mapping_queue(data, requirements).candidates[0]
assert candidate.status == "blocked"
assert "ambiguous" in candidate.blockers[0]
def test_cli_writes_machine_readable_review_queue(tmp_path: Path) -> None:
data, requirements = _files(
tmp_path,
{"metric_id": "clients", "aggregation": "count_distinct", "field": "client_id"},
)
out = tmp_path / "queue.json"
assert (
main(["map", "--data", str(data), "--requirements", str(requirements), "--out", str(out)])
== EXIT_OK
)
payload = json.loads(out.read_text())
assert payload["requires_human_review"] is True
assert payload["candidates"][0]["decision"] == "pending"
def test_cli_returns_nonzero_when_mapping_is_blocked(tmp_path: Path) -> None:
data, requirements = _files(
tmp_path,
{"metric_id": "income", "aggregation": "count_distinct", "field": "income_id"},
)
assert (
main(["map", "--data", str(data), "--requirements", str(requirements)]) == EXIT_VERIFY_FAIL
)