feat(api): drive has_sixth_form filter and payloads from GIAS flag
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"""Tests for the GIAS-driven has_sixth_form flag (spec 2026-07-07 §3).
|
||||
|
||||
The filter and payloads must use dim_school.has_sixth_form, not the old
|
||||
age_range-contains-"18" substring heuristic. The key regression case is a
|
||||
16-19 sixth-form college: flag true, but "16-19" contains no "18".
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
|
||||
def _schools_df() -> pd.DataFrame:
|
||||
"""Latest-year snapshot rows as produced by load_latest_school_data."""
|
||||
base = {
|
||||
"local_authority": "Testshire",
|
||||
"school_type": "Academy",
|
||||
"phase": "Secondary",
|
||||
"address": "1 Test Street",
|
||||
"town": "Testtown",
|
||||
"postcode": "TS1 1AA",
|
||||
"religious_denomination": None,
|
||||
"gender": "Mixed",
|
||||
"admissions_policy": None,
|
||||
"ofsted_grade": np.nan,
|
||||
"ofsted_date": None,
|
||||
"ofsted_framework": None,
|
||||
"latitude": 51.5,
|
||||
"longitude": -0.1,
|
||||
"year": 202425,
|
||||
"total_pupils": 1000,
|
||||
"rwm_expected_pct": np.nan,
|
||||
"attainment_8_score": 50.0,
|
||||
}
|
||||
return pd.DataFrame(
|
||||
[
|
||||
# 11-18 school WITH a registered sixth form
|
||||
{**base, "urn": 100001, "school_name": "Alpha High",
|
||||
"age_range": "11-18", "has_sixth_form": True},
|
||||
# 16-19 college: old heuristic said NO ("16-19" has no "18"),
|
||||
# GIAS flag says YES — must appear in the yes-filter results
|
||||
{**base, "urn": 100002, "school_name": "Beta Sixth Form College",
|
||||
"age_range": "16-19", "has_sixth_form": True},
|
||||
# 11-18 age range on paper but NO registered sixth form:
|
||||
# old heuristic said YES, GIAS flag says NO
|
||||
{**base, "urn": 100003, "school_name": "Gamma Academy",
|
||||
"age_range": "11-18", "has_sixth_form": False},
|
||||
# Missing flag (pipeline not yet re-run) — must not crash,
|
||||
# must not match the yes-filter
|
||||
{**base, "urn": 100004, "school_name": "Delta School",
|
||||
"age_range": "11-16", "has_sixth_form": None},
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(monkeypatch):
|
||||
from backend import app as app_module
|
||||
|
||||
monkeypatch.setattr(app_module, "load_latest_school_data", _schools_df)
|
||||
monkeypatch.setattr(app_module, "load_school_data", _schools_df)
|
||||
monkeypatch.setattr(app_module, "get_supplementary_data", lambda db, urn: {})
|
||||
return TestClient(app_module.app, raise_server_exceptions=False)
|
||||
|
||||
|
||||
def _urns(resp):
|
||||
return sorted(s["urn"] for s in resp.json()["schools"])
|
||||
|
||||
|
||||
def test_filter_yes_uses_flag_not_age_range(client):
|
||||
resp = client.get("/api/schools?has_sixth_form=yes")
|
||||
assert resp.status_code == 200, resp.text
|
||||
# 16-19 college included; 11-18-without-sixth-form excluded
|
||||
assert _urns(resp) == [100001, 100002]
|
||||
|
||||
|
||||
def test_filter_no_uses_flag_not_age_range(client):
|
||||
resp = client.get("/api/schools?has_sixth_form=no")
|
||||
assert resp.status_code == 200, resp.text
|
||||
# Gamma (flag false) and Delta (flag missing => not true)
|
||||
assert _urns(resp) == [100003, 100004]
|
||||
|
||||
|
||||
def test_list_payload_includes_flag(client):
|
||||
resp = client.get("/api/schools")
|
||||
assert resp.status_code == 200, resp.text
|
||||
by_urn = {s["urn"]: s for s in resp.json()["schools"]}
|
||||
assert by_urn[100002]["has_sixth_form"] is True
|
||||
assert by_urn[100003]["has_sixth_form"] is False
|
||||
assert by_urn[100004]["has_sixth_form"] is None
|
||||
|
||||
|
||||
def test_detail_payload_includes_flag(client):
|
||||
resp = client.get("/api/schools/100002")
|
||||
assert resp.status_code == 200, resp.text
|
||||
assert resp.json()["school_info"]["has_sixth_form"] is True
|
||||
Reference in New Issue
Block a user