PR Checks / Frontend Typecheck + Tests (pull_request) Successful in 9m40s
PR Checks / Backend Smoke (pull_request) Successful in 7s
PR Checks / Build Backend (no push) (pull_request) Successful in 20s
PR Checks / Build Frontend (no push) (pull_request) Successful in 54s
PR Checks / Build Pipeline (no push) (pull_request) Successful in 37s
PR Checks / AI Code Review (Claude) (pull_request) Successful in 2m35s
ReligiousCharacter 99 (~4k schools) and AdmissionsPolicy 9 (~5.6k) carry a code with a blank name in the GIAS CSV; the generator skipped them so they hit the Unknown(<code>) path — wrongly triggering the Faith-priority tag and polluting filters. Blank-only codes now map to "" (byte-identical to the old name pipeline); accepted_values lists extended to match the seed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
96 lines
3.6 KiB
Python
96 lines
3.6 KiB
Python
"""Tests for the GIAS code->name dictionaries (spec 2026-07-09).
|
|
|
|
The dictionaries are generated from the live GIAS bulk CSV by
|
|
pipeline/scripts/generate_gias_codes.py — these tests assert the module's
|
|
contract, key sentinel values the marts/UI depend on, and that the pipeline
|
|
copy has not drifted from the canonical backend module.
|
|
"""
|
|
|
|
import math
|
|
from pathlib import Path
|
|
|
|
from backend.gias_codes import (
|
|
ADMISSIONS_POLICY,
|
|
ESTABLISHMENT_STATUS,
|
|
OFFICIAL_SIXTH_FORM,
|
|
PHASE_OF_EDUCATION,
|
|
RELIGIOUS_CHARACTER,
|
|
SCHOOL_TYPE,
|
|
translate,
|
|
)
|
|
|
|
REPO = Path(__file__).resolve().parents[2]
|
|
|
|
|
|
def test_translate_known_code():
|
|
open_code = next(c for c, n in ESTABLISHMENT_STATUS.items() if n == "Open")
|
|
assert translate(open_code, ESTABLISHMENT_STATUS) == "Open"
|
|
|
|
|
|
def test_translate_unknown_code_degrades_gracefully():
|
|
assert translate(9999, ESTABLISHMENT_STATUS) == "Unknown (9999)"
|
|
|
|
|
|
def test_translate_none_and_nan_return_none():
|
|
assert translate(None, ESTABLISHMENT_STATUS) is None
|
|
assert translate(float("nan"), ESTABLISHMENT_STATUS) is None
|
|
|
|
|
|
def test_translate_accepts_float_codes():
|
|
# pd.read_sql yields float columns when NULLs are present
|
|
open_code = next(c for c, n in ESTABLISHMENT_STATUS.items() if n == "Open")
|
|
assert translate(float(open_code), ESTABLISHMENT_STATUS) == "Open"
|
|
|
|
|
|
def test_sentinel_names_present():
|
|
"""Names the marts/UI compare against must exist verbatim."""
|
|
assert "Open" in ESTABLISHMENT_STATUS.values()
|
|
assert "Open, but proposed to close" in ESTABLISHMENT_STATUS.values()
|
|
assert "Has a sixth form" in OFFICIAL_SIXTH_FORM.values()
|
|
assert "Primary" in PHASE_OF_EDUCATION.values()
|
|
assert "Secondary" in PHASE_OF_EDUCATION.values()
|
|
assert "Does not apply" in RELIGIOUS_CHARACTER.values()
|
|
assert all(len(d) > 0 for d in (
|
|
SCHOOL_TYPE, ESTABLISHMENT_STATUS, PHASE_OF_EDUCATION,
|
|
OFFICIAL_SIXTH_FORM, RELIGIOUS_CHARACTER, ADMISSIONS_POLICY,
|
|
))
|
|
|
|
|
|
def test_pipeline_copy_is_identical():
|
|
canonical = (REPO / "backend" / "gias_codes.py").read_text()
|
|
copy = (REPO / "pipeline" / "scripts" / "gias_codes.py").read_text()
|
|
assert canonical == copy, (
|
|
"pipeline/scripts/gias_codes.py has drifted from backend/gias_codes.py — "
|
|
"regenerate with pipeline/scripts/generate_gias_codes.py and copy the file"
|
|
)
|
|
|
|
|
|
def test_seed_matches_dictionaries():
|
|
import csv
|
|
fields = {
|
|
"school_type": SCHOOL_TYPE,
|
|
"establishment_status": ESTABLISHMENT_STATUS,
|
|
"phase_of_education": PHASE_OF_EDUCATION,
|
|
"official_sixth_form": OFFICIAL_SIXTH_FORM,
|
|
"religious_character": RELIGIOUS_CHARACTER,
|
|
"admissions_policy": ADMISSIONS_POLICY,
|
|
}
|
|
seed_path = REPO / "pipeline" / "transform" / "seeds" / "gias_code_names.csv"
|
|
seed: dict[str, dict[int, str]] = {k: {} for k in fields}
|
|
with open(seed_path, newline="") as fh:
|
|
for row in csv.DictReader(fh):
|
|
seed[row["field"]][int(row["code"])] = row["name"]
|
|
assert seed == fields
|
|
|
|
|
|
def test_blank_name_sentinel_codes_map_to_empty_string():
|
|
"""GIAS carries codes whose (name) column is blank — e.g. ReligiousCharacter
|
|
99 (~4k schools) and AdmissionsPolicy 9 (~5.6k schools). The old name
|
|
pipeline served these as empty strings; the dictionaries must reproduce
|
|
that ("" is falsy, so UI tag heuristics stay silent) rather than letting
|
|
them hit the "Unknown (<code>)" path meant for genuinely new codes."""
|
|
assert RELIGIOUS_CHARACTER[99] == ""
|
|
assert ADMISSIONS_POLICY[9] == ""
|
|
assert translate(99, RELIGIOUS_CHARACTER) == ""
|
|
assert translate(9, ADMISSIONS_POLICY) == ""
|