@@ -28,7 +28,7 @@ def test_slc_snapshot_values_match_higher_education_total_rows():
2828 assert targets ["slc/plan_2_borrowers_liable" ].values [2025 ] == 8_940_000
2929 assert targets ["slc/plan_2_borrowers_liable" ].values [2030 ] == 10_525_000
3030
31- assert 2025 not in targets ["slc/plan_5_borrowers_above_threshold" ].values
31+ assert targets ["slc/plan_5_borrowers_above_threshold" ].values [ 2025 ] == 0
3232 assert targets ["slc/plan_5_borrowers_above_threshold" ].values [2026 ] == 35_000
3333 assert targets ["slc/plan_5_borrowers_above_threshold" ].values [2030 ] == 1_235_000
3434 assert targets ["slc/plan_5_borrowers_liable" ].values [2025 ] == 10_000
@@ -120,6 +120,53 @@ def raise_for_status():
120120 slc ._fetch_slc_data .cache_clear ()
121121
122122
123+ def test_slc_parser_preserves_zero_value_years (monkeypatch ):
124+ """A literal zero should remain a real target year, not be dropped."""
125+ from policyengine_uk_data .targets .sources import slc
126+
127+ table_json = {
128+ "thead" : [
129+ [],
130+ [{"text" : "2024-25" }] * 6 + [{"text" : "2024-25" }] * 6 ,
131+ ],
132+ "tbody" : [
133+ [{"text" : "Higher education total" }, {"text" : "liable" }]
134+ + [{"text" : "8,940,000" }] * 6
135+ + [{"text" : "10,000" }] * 6 ,
136+ [
137+ {
138+ "text" : "Number of borrowers liable to repay and earning above repayment threshold"
139+ }
140+ ]
141+ + [{"text" : "3,985,000" }] * 6
142+ + [{"text" : "0" }] * 6 ,
143+ ],
144+ }
145+ html = (
146+ '<script id="__NEXT_DATA__" type="application/json">'
147+ + json .dumps (
148+ {"props" : {"pageProps" : {"data" : {"table" : {"json" : table_json }}}}}
149+ )
150+ + "</script>"
151+ )
152+
153+ class DummyResponse :
154+ text = html
155+
156+ @staticmethod
157+ def raise_for_status ():
158+ return None
159+
160+ slc ._fetch_slc_data .cache_clear ()
161+ monkeypatch .delenv ("TESTING" , raising = False )
162+ monkeypatch .setattr (slc .requests , "get" , lambda * args , ** kwargs : DummyResponse ())
163+
164+ data = slc ._fetch_slc_data ()
165+ assert data ["plan_5" ]["above_threshold" ][2025 ] == 0
166+
167+ slc ._fetch_slc_data .cache_clear ()
168+
169+
123170def test_student_loan_target_compute_distinguishes_liable_from_repaying ():
124171 """Above-threshold counts should require repayments, while liable counts should not."""
125172 from policyengine_uk_data .targets .compute .other import (
0 commit comments