Skip to content

feat: add YAML policy-test harness#54

Merged
vahid-ahmadi merged 1 commit into
mainfrom
vahid/yaml-test-harness
Jun 1, 2026
Merged

feat: add YAML policy-test harness#54
vahid-ahmadi merged 1 commit into
mainfrom
vahid/yaml-test-harness

Conversation

@vahid-ahmadi
Copy link
Copy Markdown
Contributor

Summary

Closes #47 in part — ships the runner and a starter set of YAML cases. Stacked on #53 (parity harness) → #52 (from_situation).

YAML cases mirror the format used by policyengine_uk/tests/policy/, so the existing 196 Python YAML tests can be ported into this repo one at a time without touching the runner.

# tests/policy/income_tax.yaml
- name: Spans basic and higher rates
  period: 2025
  absolute_error_margin: 1
  input:
    employment_income: 50_000
  output:
    baseline_income_tax: 7486

Run them:

$ python -m policyengine_uk_compiled.yaml_tests tests/policy
  PASS  Zero income — no tax [income_tax.yaml]
  PASS  Spans basic and higher rates [income_tax.yaml]
  PASS  Two children, modest income — full Child Benefit [child_benefit.yaml]
  ...
  11 passed, 0 failed of 11

…or pick them up automatically as parametrized pytest cases:

$ pytest interfaces/python/tests/test_yaml_policy_cases.py
test_yaml_policy_case[Zero income — no tax] PASSED
test_yaml_policy_case[Spans basic and higher rates] PASSED
...

What's included

Runner (interfaces/python/policyengine_uk_compiled/yaml_tests.py):

  • Two input forms: flat single-person shorthand (employment_income: 50000 → auto-builds a one-person household) and full situation (people: / benunits: / households: matching from_situation).
  • Variable name routing on the output side: each expected variable is auto-located in persons, benunits, or households microdata and summed across rows.
  • Absolute + relative error margins.
  • Boolean comparison treated as exact (so a £1 numeric tolerance doesn't make False ≈ True).
  • CLI: python -m policyengine_uk_compiled.yaml_tests <path> (file or dir).

Initial YAML cases (tests/policy/, 11 cases):

  • income_tax.yaml: zero income, below PA, basic-rate band, basic↔higher span, fully-tapered PA at £150k
  • national_insurance.yaml: below PT, main band, partial-higher tier, well above UEL
  • child_benefit.yaml: two children with full CB, no children = £0

Pytest integration (test_yaml_policy_cases.py): auto-discovers every YAML case and runs each as a parametrized test, skipping cleanly when the Rust binary isn't built.

Runner unit tests (21 cases in test_yaml_test_runner.py): cover input routing, tolerance edge cases (incl. boolean comparison), YAML parsing, and validation errors.

pyyaml added to runtime dependencies in pyproject.toml so the runner is usable from the installed package.

Verified locally

Stacking

vahid/yaml-test-harnessvahid/parity-harness (#53) ← vahid/from-situation (#52). Once #52 and #53 merge, GitHub will auto-rebase this onto main.

Test plan

  • CI passes (cargo + pytest + parity smoke + YAML cases)
  • Local: python -m policyengine_uk_compiled.yaml_tests tests/policy prints 11 passes
  • A new YAML test under tests/policy/ is automatically picked up by pytest without code changes

🤖 Generated with Claude Code

@vahid-ahmadi vahid-ahmadi force-pushed the vahid/parity-harness branch from 9a75e02 to b45e8e1 Compare May 29, 2026 09:17
@vahid-ahmadi vahid-ahmadi force-pushed the vahid/yaml-test-harness branch from 4b57f83 to d2c89be Compare May 29, 2026 09:17
vahid-ahmadi added a commit that referenced this pull request May 29, 2026
Property-transaction tax now dispatches by region:
- Scotland → LBTT (LBTT (Scotland) Act 2013)
- Wales    → LTT  (LTT and Anti-avoidance of Devolved Taxes (Wales) Act 2017)
- elsewhere → SDLT (Finance Act 2003 s.55, unchanged)

2025/26 residential bands per:
- SSI 2015/126 (Scotland)
- WSI 2018/128 (Wales)

Adds:
- `lbtt` and `ltt` parameter blocks in `parameters/2025_26.yaml`
- `Parameters.lbtt`/`Parameters.ltt` Rust fields and Python wrapper exposure
- `calculate_property_transaction_tax` dispatch function in
  `src/variables/wealth_taxes.rs`
- New `baseline_property_transaction_tax` and `reform_property_transaction_tax`
  per-household microdata columns
- Six Rust unit tests covering LBTT/LTT/SDLT dispatch and nil-band edges
- Six YAML policy-test cases (`tests/policy/property_transaction_tax.yaml`)

Stacked on #54 (YAML test harness).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vahid-ahmadi vahid-ahmadi changed the base branch from vahid/parity-harness to main May 29, 2026 09:19
Adds `policyengine_uk_compiled.yaml_tests` — a runner that mirrors the
format used by `policyengine_uk/tests/policy/` so cases can be ported one
at a time.

The runner accepts either single-person flat input
(`input: { employment_income: 50000 }`) or full-situation input
(`input: { people: ..., benunits: ..., households: ... }`), supports
absolute and relative error margins, and writes outputs against the Rust
microdata column names (`baseline_income_tax`, `baseline_universal_credit`,
`baseline_net_income`, etc.).

This PR ships:
- The runner module with CLI: `python -m policyengine_uk_compiled.yaml_tests tests/policy`
- 11 hand-written YAML cases under `tests/policy/` covering income tax,
  employee NI, and Child Benefit (single + multi-person)
- A pytest module that auto-discovers and parametrizes the YAML cases
- 21 unit tests for the runner itself (input mapping, tolerance, parsing)
- pyyaml added to the package's runtime dependencies

Stacked on #53 (parity harness) which is itself stacked on #52
(Simulation.from_situation). Future PRs port more of the 196 Python YAML
tests that already exist in `policyengine_uk/tests/policy/`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vahid-ahmadi vahid-ahmadi force-pushed the vahid/yaml-test-harness branch from d2c89be to 3e78c00 Compare May 29, 2026 11:58
@vahid-ahmadi
Copy link
Copy Markdown
Contributor Author

Rebased onto main and made self-contained — ready to merge.

This was previously stacked on #53/#52 and imported _situation_to_dataframes from the (contested) from_situation PR. The situation→DataFrame conversion is now inlined as a private helper inside the harness, so:

engine.py is byte-identical to main. Verified: the wrapper imports cleanly, the 21 runner unit tests pass, and all 11 committed YAML policy cases run through the real release binary and pass numerically. Assertion logic is sound (booleans compared exactly, relative margin skipped at expected 0, clear "column not found" failures). Mergeable against main.

@vahid-ahmadi vahid-ahmadi merged commit e334807 into main Jun 1, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Port the YAML policy-test harness (196 Python tests → ~0 Rust tests)

1 participant