Skip to content

Commit cfa4997

Browse files
committed
Add tests for parallel long-run wrapper
1 parent f40796c commit cfa4997

2 files changed

Lines changed: 159 additions & 1 deletion

File tree

policyengine_us_data/datasets/cps/long_term/run_household_projection_parallel.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
from concurrent.futures import ThreadPoolExecutor, as_completed
99
from pathlib import Path
1010

11-
from calibration_artifacts import update_dataset_manifest
11+
try:
12+
from .calibration_artifacts import update_dataset_manifest
13+
except ImportError: # pragma: no cover - script execution fallback
14+
from calibration_artifacts import update_dataset_manifest
1215

1316

1417
SCRIPT_DIR = Path(__file__).resolve().parent

tests/unit/test_long_term_calibration_contract.py

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@
5858
build_role_donor_composites,
5959
summarize_realized_clone_translation,
6060
)
61+
from policyengine_us_data.datasets.cps.long_term.run_household_projection_parallel import (
62+
merge_outputs,
63+
parse_years,
64+
validate_forwarded_args,
65+
year_output_dir,
66+
)
6167

6268

6369
class ExplodingCalibrator:
@@ -1245,6 +1251,155 @@ def test_write_support_augmentation_report_custom_filename(tmp_path):
12451251
assert loaded["target_year"] == 2090
12461252

12471253

1254+
def test_parallel_projection_parse_years_supports_ranges_and_sorting():
1255+
assert parse_years("2030,2028-2029,2030,2027") == [2027, 2028, 2029, 2030]
1256+
1257+
1258+
def test_parallel_projection_validate_forwarded_args_rejects_wrapper_flags():
1259+
with pytest.raises(ValueError, match="--output-dir"):
1260+
validate_forwarded_args(["--output-dir", "/tmp/out"])
1261+
with pytest.raises(ValueError, match="--save-h5"):
1262+
validate_forwarded_args(["--save-h5"])
1263+
1264+
1265+
def _write_parallel_temp_year(
1266+
*,
1267+
root,
1268+
year,
1269+
profile,
1270+
audit,
1271+
target_source=None,
1272+
tax_assumption=None,
1273+
support_augmentation=None,
1274+
):
1275+
temp_output_dir = year_output_dir(root, year)
1276+
temp_output_dir.mkdir(parents=True, exist_ok=True)
1277+
year_h5 = temp_output_dir / f"{year}.h5"
1278+
year_h5.write_text("", encoding="utf-8")
1279+
metadata_path = write_year_metadata(
1280+
year_h5,
1281+
year=year,
1282+
base_dataset_path="test.h5",
1283+
profile=profile,
1284+
calibration_audit=audit,
1285+
target_source=target_source,
1286+
tax_assumption=tax_assumption,
1287+
support_augmentation=support_augmentation,
1288+
)
1289+
update_dataset_manifest(
1290+
temp_output_dir,
1291+
year=year,
1292+
h5_path=year_h5,
1293+
metadata_path=metadata_path,
1294+
base_dataset_path="test.h5",
1295+
profile=profile,
1296+
calibration_audit=audit,
1297+
target_source=target_source,
1298+
tax_assumption=tax_assumption,
1299+
support_augmentation=support_augmentation,
1300+
)
1301+
1302+
1303+
def test_parallel_projection_merge_outputs_rebuilds_manifest(tmp_path):
1304+
profile = get_profile("ss-payroll-tob").to_dict()
1305+
audit = {
1306+
"method_used": "entropy",
1307+
"fell_back_to_ipf": False,
1308+
"age_max_pct_error": 0.0,
1309+
"negative_weight_pct": 0.0,
1310+
"positive_weight_count": 70000,
1311+
"effective_sample_size": 5000.0,
1312+
"top_10_weight_share_pct": 1.5,
1313+
"top_100_weight_share_pct": 10.0,
1314+
"max_constraint_pct_error": 0.0,
1315+
"constraints": {},
1316+
"validation_passed": True,
1317+
"validation_issues": [],
1318+
"calibration_quality": "exact",
1319+
}
1320+
target_source = {
1321+
"name": "oact_2025_08_05_provisional",
1322+
"source_type": "oact_note",
1323+
}
1324+
tax_assumption = {
1325+
"name": "trustees-core-thresholds-v1",
1326+
"start_year": 2035,
1327+
"end_year": 2100,
1328+
}
1329+
1330+
_write_parallel_temp_year(
1331+
root=tmp_path,
1332+
year=2045,
1333+
profile=profile,
1334+
audit=audit,
1335+
target_source=target_source,
1336+
tax_assumption=tax_assumption,
1337+
)
1338+
_write_parallel_temp_year(
1339+
root=tmp_path,
1340+
year=2049,
1341+
profile=profile,
1342+
audit=audit,
1343+
target_source=target_source,
1344+
tax_assumption=tax_assumption,
1345+
)
1346+
1347+
manifest_path = merge_outputs(
1348+
years=[2045, 2049],
1349+
output_root=tmp_path,
1350+
keep_temp=False,
1351+
)
1352+
1353+
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
1354+
assert manifest["years"] == [2045, 2049]
1355+
assert manifest["target_source"]["name"] == "oact_2025_08_05_provisional"
1356+
assert manifest["tax_assumption"]["name"] == "trustees-core-thresholds-v1"
1357+
assert (tmp_path / "2045.h5").exists()
1358+
assert (tmp_path / "2049.h5.metadata.json").exists()
1359+
assert not (tmp_path / ".parallel_tmp").exists()
1360+
1361+
1362+
def test_parallel_projection_merge_outputs_rejects_mismatched_contract(tmp_path):
1363+
profile = get_profile("ss-payroll-tob").to_dict()
1364+
audit = {
1365+
"method_used": "entropy",
1366+
"fell_back_to_ipf": False,
1367+
"age_max_pct_error": 0.0,
1368+
"negative_weight_pct": 0.0,
1369+
"positive_weight_count": 70000,
1370+
"effective_sample_size": 5000.0,
1371+
"top_10_weight_share_pct": 1.5,
1372+
"top_100_weight_share_pct": 10.0,
1373+
"max_constraint_pct_error": 0.0,
1374+
"constraints": {},
1375+
"validation_passed": True,
1376+
"validation_issues": [],
1377+
"calibration_quality": "exact",
1378+
}
1379+
1380+
_write_parallel_temp_year(
1381+
root=tmp_path,
1382+
year=2062,
1383+
profile=profile,
1384+
audit=audit,
1385+
tax_assumption={"name": "trustees-core-thresholds-v1"},
1386+
)
1387+
_write_parallel_temp_year(
1388+
root=tmp_path,
1389+
year=2063,
1390+
profile=profile,
1391+
audit=audit,
1392+
tax_assumption={"name": "different-tax-assumption"},
1393+
)
1394+
1395+
with pytest.raises(ValueError, match="Temp manifest mismatch for tax_assumption"):
1396+
merge_outputs(
1397+
years=[2062, 2063],
1398+
output_root=tmp_path,
1399+
keep_temp=True,
1400+
)
1401+
1402+
12481403
def test_summarize_realized_clone_translation_matches_toy_clone():
12491404
import pandas as pd
12501405

0 commit comments

Comments
 (0)