From c7c6762c96ef2641c30e2948ec7413aad47ebcf8 Mon Sep 17 00:00:00 2001 From: Vahid Ahmadi Date: Tue, 14 Jan 2025 18:46:57 +0000 Subject: [PATCH] Add unit tests --- .../tax_free_childcare/income_thresholds.yaml | 2 +- .../childcare_age_child_condition.yaml | 31 ++++ .../childcare_income_condition.yaml | 114 ++++++++++++ .../childcare_work_condition.yaml | 163 ++++++++++++++++++ .../incompatibilities_childcare_eligible.yaml | 38 ++++ .../tax_free_childcare_benefits.yaml | 109 ++++++++++++ .../childcare_age_child_condition.py | 27 +-- .../conditions/childcare_work_condition.py | 30 ++-- 8 files changed, 474 insertions(+), 40 deletions(-) create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml create mode 100644 policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml diff --git a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml index 343605a0..0b76070a 100644 --- a/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml +++ b/policyengine_uk/parameters/gov/dwp/childcare_subsidies/tax_free_childcare/income_thresholds.yaml @@ -12,7 +12,7 @@ brackets: - threshold: 2015-01-01: 0 amount: - 2015-01-01: 0 + 2015-01-01: 1_331 - threshold: 2015-01-01: 18 amount: diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml new file mode 100644 index 00000000..6f397992 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_age_child_condition.yaml @@ -0,0 +1,31 @@ +- name: Under standard age limit - eligible + period: 2025 + input: + age: 10 + is_disabled_for_benefits: false + output: + child_age_eligible: true + +- name: Over standard age limit and not disabled - ineligible + period: 2025 + input: + age: 12 + is_disabled_for_benefits: false + output: + child_age_eligible: false + +- name: Over standard age but under disability limit and disabled - eligible + period: 2025 + input: + age: 15 + is_disabled_for_benefits: true + output: + child_age_eligible: true + +- name: Over disability age limit and disabled - ineligible + period: 2025 + input: + age: 17 + is_disabled_for_benefits: true + output: + child_age_eligible: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml new file mode 100644 index 00000000..d50889af --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_income_condition.yaml @@ -0,0 +1,114 @@ +# Tests for age under 18 bracket (threshold: £1,331 quarterly) +- name: Under threshold for age 15 with low income and no other income sources - ineligible + period: 2025 + input: + age: 15 + total_income: 5000 + private_pension_income: 0 + savings_interest_income: 500 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: false + +- name: At threshold for age 15 - eligible + period: 2025 + input: + age: 15 + total_income: 6324 + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 200 + output: + meets_income_requirements: true + +# Tests for age 18-20 bracket (threshold: £1,788 quarterly) +- name: Under threshold for age 19 - ineligible + period: 2025 + input: + age: 19 + total_income: 7000 # £1,750 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: false + +- name: At threshold for age 19 - eligible + period: 2025 + input: + age: 18 + total_income: 7152 # £1,788 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: true + +# Tests for age 21+ bracket (threshold: £2,379 quarterly) +- name: Under threshold for age 22 - ineligible + period: 2025 + input: + age: 22 + total_income: 9000 # £2,250 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: false + +- name: age 18 - eligible + period: 2025 + input: + age: 18 + total_income: 9516 # £2,379 quarterly + private_pension_income: 0 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: true + +# Tests with investment income +- name: Above threshold but with investment income making eligible income below threshold - ineligible + period: 2025 + input: + age: 22 + total_income: 12000 + private_pension_income: 3000 + savings_interest_income: 500 + dividend_income: 1000 + property_income: 500 + # Net eligible income: 7000 (£1,750 quarterly) + output: + meets_income_requirements: false + +- name: Edge case with investment income exactly at threshold - eligible + period: 2025 + input: + age: 15 + total_income: 7324 + private_pension_income: 2000 + savings_interest_income: 0 + dividend_income: 0 + property_income: 0 + # Net eligible income: 5324 (£1,331 quarterly) + output: + meets_income_requirements: true + +# Test age boundary cases +- name: Age boundary test at 18 - using higher threshold + period: 2025 + input: + age: 18 + total_income: 7152 + private_pension_income: 0 + savings_interest_income: 100 + dividend_income: 0 + property_income: 0 + output: + meets_income_requirements: true diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml new file mode 100644 index 00000000..0b5ca14c --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/childcare_work_condition.yaml @@ -0,0 +1,163 @@ +- name: Couple both working - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: true + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Couple neither working - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: false + +- name: Couple one working one with disability - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Couple one working one with incapacity - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Couple one working without qualifying condition - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: false + +- name: Single working adult - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 0 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: true + +- name: Single non-working adult - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 0 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: false + +- name: Couple both with disability one working - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 100 + spouse: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person, spouse] + output: + childcare_work_condition: true + +- name: Single working adult with disability - eligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: true + incapacity_benefit: 100 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: true + +- name: Single disabled non-working adult - ineligible + period: 2025 + input: + people: + person: + is_adult: true + in_work: false + incapacity_benefit: 100 + benunits: + benunit: + members: [person] + output: + childcare_work_condition: false \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml new file mode 100644 index 00000000..b4b6e366 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/incompatibilities_childcare_eligible.yaml @@ -0,0 +1,38 @@ +- name: Eligible - does NOT receive any of these benefits + period: 2025 + input: + working_tax_credit: 0 + child_tax_credit: 0 + universal_credit: 0 + output: + incompatibilities_childcare_eligible: True + + +- name: Non eligible - receives benefits + period: 2025 + input: + working_tax_credit: 1 + child_tax_credit: 0 + universal_credit: 0 + output: + incompatibilities_childcare_eligible: False + + +- name: Non eligible - receives benefits + period: 2025 + input: + working_tax_credit: 1 + child_tax_credit: 1 + universal_credit: 0 + output: + incompatibilities_childcare_eligible: False + + +- name: Non eligible - receives benefits + period: 2025 + input: + working_tax_credit: 1 + child_tax_credit: 1 + universal_credit: 1 + output: + incompatibilities_childcare_eligible: False \ No newline at end of file diff --git a/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml new file mode 100644 index 00000000..4f949a60 --- /dev/null +++ b/policyengine_uk/tests/policy/baseline/gov/dwp/childcare_subsidies/tax_free_childcare/tax_free_childcare_benefits.yaml @@ -0,0 +1,109 @@ +- name: Eligible family with standard child + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 2000 + +- name: Eligible family with disabled child + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: true + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 4000 + +- name: Ineligible due to not being a child + period: 2025 + input: + meets_income_requirements: true + is_child: false + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Ineligible due to age condition + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: false + child_age_eligible: false + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Ineligible due to income requirements + period: 2025 + input: + meets_income_requirements: false + is_child: true + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Ineligible due to incompatibilities + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: false + child_age_eligible: true + incompatibilities_childcare_eligible: false + output: + tax_free_childcare: 0 + +- name: Disabled child but fails income requirements + period: 2025 + input: + meets_income_requirements: false + is_child: true + is_disabled_for_benefits: true + child_age_eligible: true + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Disabled child but fails age condition + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: true + child_age_eligible: false + incompatibilities_childcare_eligible: true + output: + tax_free_childcare: 0 + +- name: Disabled child but fails incompatibilities + period: 2025 + input: + meets_income_requirements: true + is_child: true + is_disabled_for_benefits: true + child_age_eligible: true + incompatibilities_childcare_eligible: false + output: + tax_free_childcare: 0 + +- name: Fails all eligibility conditions + period: 2025 + input: + meets_income_requirements: false + is_child: true + is_disabled_for_benefits: false + child_age_eligible: false + incompatibilities_childcare_eligible: false + output: + tax_free_childcare: 0 \ No newline at end of file diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py index d4156e11..17cc7d63 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_age_child_condition.py @@ -5,7 +5,9 @@ class child_age_eligible(Variable): value_type = bool entity = Person label = "Child age eligibility requirements" - documentation = "Whether this person meets the age and disability requirements for eligibility" + documentation = ( + "Whether this person meets the age and disability requirements for eligibility" + ) definition_period = YEAR def formula(person, period, parameters): @@ -25,28 +27,13 @@ def formula(person, period, parameters): standard_age_limit = age_limits.standard disability_age_limit = age_limits.disability - # Check disability conditions - gc = parameters( - period - ).gov.dwp.pension_credit.guarantee_credit.child.disability - standard_disability_benefits = gc.eligibility - severe_disability_benefits = gc.severe.eligibility - - # Convert to boolean arrays before combining - standard_benefits = add( - person, period, standard_disability_benefits - ).astype(bool) - severe_benefits = add( - person, period, severe_disability_benefits - ).astype(bool) - is_disabled = (standard_benefits | severe_benefits).astype(bool) + # Check disability status + is_disabled = person("is_disabled_for_benefits", period) # Check age conditions using parameterized values basic_age_condition = (age < standard_age_limit).astype(bool) age_under_disability_limit = (age < disability_age_limit).astype(bool) - # Convert to boolean before final combination - combined_condition = (age_under_disability_limit & is_disabled).astype( - bool - ) + # Combine conditions + combined_condition = (age_under_disability_limit & is_disabled).astype(bool) return (basic_age_condition | combined_condition).astype(bool) diff --git a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py index d7f8bd5a..bdf3922d 100644 --- a/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py +++ b/policyengine_uk/variables/gov/dwp/tax_free_childcare/conditions/childcare_work_condition.py @@ -5,7 +5,9 @@ class childcare_work_condition(Variable): value_type = bool entity = Person label = "Work conditions for tax-free childcare" - documentation = "Whether the person/couple meets work requirements for tax-free childcare" + documentation = ( + "Whether the person/couple meets work requirements for tax-free childcare" + ) definition_period = YEAR def formula(person, period, parameters): @@ -29,9 +31,8 @@ def formula(person, period, parameters): | (add(person, period, severe_disability_benefits) > 0) ).astype(bool) - has_incapacity = (person("incapacity_benefit", period) > 0).astype( - bool - ) + has_incapacity = (person("incapacity_benefit", period) > 0).astype(bool) + has_condition = (is_disabled | has_incapacity).astype(bool) # Build conditions # Single adult conditions @@ -40,22 +41,13 @@ def formula(person, period, parameters): # Couple conditions is_couple = (benunit.sum(is_adult) == 2).astype(bool) - partner_in_work = in_work - partner_has_condition = (is_disabled | has_incapacity).astype(bool) - - couple_both_working = (is_couple & in_work & partner_in_work).astype( - bool - ) - is_partner_working_with_disabled_person = ( - is_couple & partner_in_work & (is_disabled | has_incapacity) - ).astype(bool) - is_person_working_with_disabled_partner = ( - is_couple & in_work & partner_has_condition + benunit_has_condition = benunit.any(has_condition) + benunit_has_worker = benunit.any(in_work) + couple_both_working = (is_couple & benunit.all(in_work)).astype(bool) + couple_one_working_one_disabled = ( + is_couple & benunit_has_worker & benunit_has_condition ).astype(bool) return ( - single_working - | couple_both_working - | is_person_working_with_disabled_partner - | is_partner_working_with_disabled_person + single_working | couple_both_working | couple_one_working_one_disabled ).astype(bool)