"""Mock FormFav responses for tests — no live API needed. Includes realistic race forms (matching the observed FormFav shape) and designed edge cases: scratched runners, abandoned races, missing fields, x/spell form strings, and incomplete data. """ from __future__ import annotations from pathlib import Path def meetings_payload(date: str = "2026-08-11") -> dict: return { "date": date, "raceType": "gallops", "meetings": [ {"track": "Dubbo", "slug": "dubbo", "country": "au", "abandoned": False, "races": [{"raceNumber": 1}, {"raceNumber": 2}]}, {"track": "Randwick", "slug": "randwick", "country": "au", "abandoned": False, "races": [{"raceNumber": 7}]}, # non-AU meeting (must be filtered out) {"track": "Riccarton", "slug": "riccarton", "country": "nz", "abandoned": False, "races": [{"raceNumber": 1}]}, ], } def race_form_full(date: str = "2026-08-11", track: str = "dubbo", race: int = 1) -> dict: return { "date": date, "track": track, "raceNumber": race, "raceName": "Maiden Plate", "distance": "1200m", "condition": "Good 4", "weather": "Fine", "raceClass": "MDN", "abandoned": False, "startTime": "2026-08-11T03:25:00Z", "timezone": "Australia/Sydney", "prizeMoney": "30000", "numberOfRunners": 3, "trackSlug": track, "runners": [ {"number": 1, "name": "Speedy", "jockey": "J. Doe", "trainer": "A. Smith", "weight": 57.0, "barrier": 2, "age": 4, "sex": "Gelding", "careerPrizeMoney": "$25000", "form": "1x231", "last20Starts": "1x231", "scratched": False, "stats": {"overall": {"starts": 5, "wins": 1, "places": 3, "seconds": 1, "thirds": 1, "winPercent": 20.0, "placePercent": 60.0}, "track": {"starts": 2, "places": 1}, "distance": {"starts": 1, "places": 1}, "conditions": {"good": {"starts": 3, "places": 2}}}}, {"number": 2, "name": "Stayer", "jockey": "K. Lee", "trainer": "B. Cole", "weight": 58.5, "barrier": 7, "age": 5, "sex": "Mare", "careerPrizeMoney": "$10000", "form": "45340", "last20Starts": "45340", "scratched": False, "stats": {"overall": {"starts": 4, "wins": 0, "places": 1, "seconds": 0, "thirds": 1, "winPercent": 0.0, "placePercent": 25.0}, "track": {"starts": 1, "places": 0}, "distance": {"starts": 2, "places": 0}, "conditions": {"good": {"starts": 2, "places": 1}}}}, {"number": 3, "name": "Sprinter", "jockey": "M. Ray", "trainer": "C. Vu", "weight": 55.0, "barrier": 1, "age": 3, "sex": "Filly", "careerPrizeMoney": "$5000", "form": "22211", "last20Starts": "22211", "scratched": False, "stats": {"overall": {"starts": 6, "wins": 2, "places": 4, "seconds": 2, "thirds": 0, "winPercent": 33.3, "placePercent": 66.6}, "track": {"starts": 1, "places": 1}, "distance": {"starts": 2, "places": 2}, "conditions": {"good": {"starts": 3, "places": 2}}}}, ], } def race_form_with_scratch_and_missing(date: str = "2026-08-11", track: str = "randwick", race: int = 7) -> dict: return { "date": date, "track": track, "raceNumber": race, "raceName": "Group 1 Sprint", "distance": "1600m", "condition": "Soft 5", "raceClass": "G1", "abandoned": False, "startTime": "2026-08-11T05:00:00Z", "timezone": "Australia/Sydney", "numberOfRunners": 2, "trackSlug": track, "runners": [ {"number": 4, "name": "NoForm", "jockey": "J. X", "trainer": "T. Y", "weight": 59.0, "barrier": 9, # no 'form' field (falls back to last20Starts) "last20Starts": "7777x", "scratched": True, # scratched — must be excluded "stats": {"overall": {"starts": 1, "wins": 0, "places": 0, "winPercent": 0.0, "placePercent": 0.0}}}, {"number": 8, "name": "OnlyOne", "jockey": "K. Z", "trainer": "L. W", "weight": 56.0, "barrier": 3, "age": 4, "sex": "Gelding", "careerPrizeMoney": "$40000", "form": "11122", "scratched": False, "stats": {"overall": {"starts": 8, "wins": 3, "places": 6, "winPercent": 37.5, "placePercent": 75.0}, "track": {"starts": 3, "places": 2}, "distance": {"starts": 4, "places": 3}, "conditions": {"soft": {"starts": 2, "places": 1}}}}, # a third runner but with almost no data (tests graceful degradation) {"number": 2, "name": "Ghost", "jockey": "P. Q", "trainer": "R. S", "barrier": 12, "scratched": False, "stats": {"overall": {"winPercent": 0.0, "placePercent": 0.0}}}, ], } def race_form_abandoned(date: str = "2026-08-11", track: str = "dubbo", race: int = 2) -> dict: return {"date": date, "track": track, "raceNumber": race, "abandoned": True, "runners": []}