casebench
test cases straight onto the board.
git clone https://owenrusk.dev/casebench.git
commit e12a67868ce846f0e8b72e67262a405d9c8810af parent 434ced1b47718423bc8daa51ee23350e5913f348 author Owen Rusk <owen@papermothgames.com> date 2025-04-23 12:14:58 -0500
casefile: say which item is broken a KeyError from yaml halfway down tess's case wasn't telling her anything.
| casebench/casefile.py | +29 | -12 |
diff --git a/casebench/casefile.py b/casebench/casefile.py index b28061c..243b26f 100644 --- a/casebench/casefile.py +++ b/casebench/casefile.py @@ -44,30 +44,47 @@ def load(folder: Path) -> Case: if not path.is_file(): raise CaseError(f"{folder}: no case.yaml") raw = yaml.safe_load(path.read_text(encoding="utf-8")) or {} + if not isinstance(raw, dict): + raise CaseError(f"{path}: not a mapping") items = []- for entry in raw.get("items") or []:- text = path.parent / entry["text"]+ seen = set() + for n, entry in enumerate(raw.get("items") or []): + where = f"{path}: items[{n}]" + if not isinstance(entry, dict): + raise CaseError(f"{where}: not a mapping") + item_id = _id(entry.get("id"), f"{where}.id") + if item_id in seen: + raise CaseError(f"{where}: {item_id} twice") + seen.add(item_id) + text = path.parent / _text(entry.get("text"), f"{where}.text") if not text.is_file():- raise CaseError(f"{path}: no file {entry['text']}")- images = tuple(_image(path.parent / name, path) for name in entry.get("images") or [])- items.append(- Item(_id(entry.get("id"), path), entry["title"], text.read_text(encoding="utf-8"), images)+ raise CaseError(f"{where}: no file {text.name}") + images = tuple( + _image(path.parent / _text(name, f"{where}.images"), where) for name in entry.get("images") or [] ) + title = _text(entry.get("title"), f"{where}.title") + items.append(Item(item_id, title, text.read_text(encoding="utf-8"), images)) if not items: raise CaseError(f"{path}: no items")- return Case(_id(raw.get("id"), path), raw["title"], tuple(items))+ return Case(_id(raw.get("id"), f"{path}: id"), _text(raw.get("title"), f"{path}: title"), tuple(items))-def _id(value: object, path: Path) -> str:+def _id(value: object, where: str) -> str: if not isinstance(value, str) or not ID.fullmatch(value):- raise CaseError(f"{path}: bad id {value!r}")+ raise CaseError(f"{where}: bad id {value!r}") return value-def _image(file: Path, path: Path) -> Image:+def _text(value: object, where: str) -> str: + if not isinstance(value, str) or not value.strip(): + raise CaseError(f"{where}: missing") + return value + + +def _image(file: Path, where: str) -> Image: mime = MEDIA.get(file.suffix.lower()) if mime is None:- raise CaseError(f"{path}: can't serve {file.name}")+ raise CaseError(f"{where}: can't serve {file.name}") if not file.is_file():- raise CaseError(f"{path}: no file {file.name}")+ raise CaseError(f"{where}: no file {file.name}") return Image(file.name, mime, file.read_bytes())