GET /api/exam/corpus — read-only view over the seeded eb_specifications + eb_exams catalogue: board → subject → spec → papers grouped by paper_code+session, each showing which of QP/MS/ER exist AND are stored, with per-spec + global rollups (specs, papers, sessions, QP/MS/ER counts). Surfaces the collected exam bank so the app can display its state — e.g. AQA GCSE Physics 8463: 21 QP / 12 MS / 17 ER. Validated against the live catalogue (60 specs, 1178 docs, 535 papers) + a unit test (grouping, QP/MS/ER presence, stored-vs-catalogued). As-user (catalogue is public reference data). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
49 lines
2.4 KiB
Python
49 lines
2.4 KiB
Python
"""Test the exam-bank corpus coverage grouping (/api/exam/corpus)."""
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
from routers.exam.corpus import router
|
|
from routers.exam.dependencies import ExamContext, get_exam_context
|
|
|
|
TEACHER = "00000000-0000-0000-0000-000000000001"
|
|
INST_A = "10000000-0000-0000-0000-000000000001"
|
|
|
|
|
|
class FakeResult:
|
|
def __init__(self, data): self.data = data
|
|
|
|
|
|
class FakeQuery:
|
|
def __init__(self, store, table): self.store, self.table = store, table
|
|
def select(self, *_a, **_k): return self
|
|
def execute(self): return FakeResult(list(self.store.get(self.table, [])))
|
|
|
|
|
|
class FakeSupabase:
|
|
def __init__(self, store): self.store = store
|
|
def table(self, name): return FakeQuery(self.store, name)
|
|
|
|
|
|
def _client(store):
|
|
app = FastAPI(); app.include_router(router, prefix="/api/exam")
|
|
app.dependency_overrides[get_exam_context] = lambda: ExamContext(TEACHER, "tok", FakeSupabase(store), [INST_A])
|
|
return TestClient(app)
|
|
|
|
|
|
def test_corpus_groups_papers_by_session_with_qp_ms_er_coverage():
|
|
store = {
|
|
"eb_specifications": [{"spec_code": "AQA-PHYS-8463", "exam_board_code": "AQA", "subject_code": "PHYSICS", "award_code": "8463", "first_teach": "2016"}],
|
|
"eb_exams": [
|
|
{"exam_code": "a", "spec_code": "AQA-PHYS-8463", "paper_code": "8463/1H", "session": "2022-Jun", "type_code": "QP", "storage_loc": "cc.examboards/a.pdf"},
|
|
{"exam_code": "b", "spec_code": "AQA-PHYS-8463", "paper_code": "8463/1H", "session": "2022-Jun", "type_code": "MS", "storage_loc": "cc.examboards/b.pdf"},
|
|
{"exam_code": "c", "spec_code": "AQA-PHYS-8463", "paper_code": "8463/1H", "session": "2022-Jun", "type_code": "ER", "storage_loc": None},
|
|
{"exam_code": "d", "spec_code": "AQA-PHYS-8463", "paper_code": "8463/2H", "session": "2023-Jun", "type_code": "QP", "storage_loc": "cc.examboards/d.pdf"},
|
|
],
|
|
}
|
|
body = _client(store).get("/api/exam/corpus").json()
|
|
assert body["totals"]["specs"] == 1 and body["totals"]["papers"] == 2
|
|
spec = body["boards"][0]["specs"][0]
|
|
assert spec["level"] == "GCSE" and spec["subject"] == "Physics"
|
|
assert spec["counts"] == {"QP": 2, "MS": 1, "ER": 0} # ER present but not stored → not counted
|
|
p = next(p for p in spec["papers"] if p["paper_code"] == "8463/1H")
|
|
assert p["docs"] == {"QP": True, "MS": True, "ER": False}
|