Skip to content

Commit 3a847e7

Browse files
committed
Add tests for parallel long-run wrapper
1 parent e2d1d70 commit 3a847e7

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

policyengine_us_data/tests/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:
@@ -1246,6 +1252,155 @@ def test_write_support_augmentation_report_custom_filename(tmp_path):
12461252
assert loaded["target_year"] == 2090
12471253

12481254

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

0 commit comments

Comments
 (0)