From 6c872ce726f210433354ca38dc5314bc6a467534 Mon Sep 17 00:00:00 2001 From: Tudor Date: Sat, 4 Jul 2026 22:13:56 +0100 Subject: [PATCH] fix(api): accept academic-year codes in rankings year filter The rankings endpoint validated year with le=2100, but the database stores academic-year codes like 201819, so any explicit year selection returned a 422 and the rankings page rendered its empty state. Widen the bound to cover the codes and extend the e2e journey to pick a specific year and assert the table stays populated. Co-Authored-By: Claude Fable 5 --- backend/app.py | 5 ++++- e2e/tests/journeys.spec.ts | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/app.py b/backend/app.py index ca77f4e..a1cab3b 100644 --- a/backend/app.py +++ b/backend/app.py @@ -834,7 +834,10 @@ async def get_rankings( request: Request, metric: str = Query("rwm_expected_pct", description="Metric to rank by", max_length=50), year: Optional[int] = Query( - None, description="Specific year (defaults to most recent)", ge=2000, le=2100 + None, + description="Academic year code, e.g. 201819 (defaults to most recent)", + ge=2000, + le=210100, ), limit: int = Query(20, ge=1, le=100, description="Number of schools to return"), local_authority: Optional[str] = Query( diff --git a/e2e/tests/journeys.spec.ts b/e2e/tests/journeys.spec.ts index 1a7e6ac..07c4c4e 100644 --- a/e2e/tests/journeys.spec.ts +++ b/e2e/tests/journeys.spec.ts @@ -73,3 +73,21 @@ test('rankings page loads a populated table', async ({ page }) => { await expect(rows.first()).toBeVisible({ timeout: 15_000 }); expect(await rows.count()).toBeGreaterThan(5); }); + +test('rankings stay populated after picking a specific year', async ({ page }) => { + // Years are academic-year codes (e.g. 201819); the API must accept them + // as the `year` query param rather than rejecting with a 422. + await page.goto('/rankings'); + const yearSelect = page.locator('#year-select'); + await expect(yearSelect).toBeVisible({ timeout: 15_000 }); + + // Pick the first explicit year option (index 0 is the "Latest" default). + const yearValue = await yearSelect.locator('option').nth(1).getAttribute('value'); + expect(yearValue).toBeTruthy(); + await yearSelect.selectOption(yearValue!); + await page.waitForURL(/year=/); + + const rows = page.locator('table tbody tr'); + await expect(rows.first()).toBeVisible({ timeout: 15_000 }); + expect(await rows.count()).toBeGreaterThan(5); +}); -- 2.54.0