"""HTTP contracts for POST /api/western and POST /api/ephemeris_events.""" from __future__ import annotations import json from io import BytesIO import pytest from scripts.api_heavy_compute_gate import reset_heavy_compute_gate from scripts.jyotish_api_server import ( DEFAULT_ALLOWED_HOSTS, DEFAULT_ALLOWED_ORIGINS, JyotishAPIHandler, ) SAMPLE = { "year": 1990, "month": 4, "day": 9, "hour": 13, "minute": 24, "lat": 31.19, "lon": 121.44, "tz": 8, } class _FakeHeaders(dict): def get(self, key, default=None): return super().get(key, default) class _FakeServer: allowed_origins = DEFAULT_ALLOWED_ORIGINS allowed_hosts = DEFAULT_ALLOWED_HOSTS class _PostCaptureHandler(JyotishAPIHandler): def __init__(self, path: str, payload: dict) -> None: raw = json.dumps(payload).encode("utf-8") self.headers = _FakeHeaders( { "Content-Length": str(len(raw)), "Content-Type": "application/json", } ) self.server = _FakeServer() self.path = path self.rfile = BytesIO(raw) self.wfile = BytesIO() self.status_code = None self.response_headers = [] self.client_address = ("test-readonly-chart", 0) def send_response(self, code, message=None): # noqa: ANN001 self.status_code = code def send_header(self, key, value): # noqa: ANN001 self.response_headers.append((key, value)) def end_headers(self): return None def payload(self) -> dict: return json.loads(self.wfile.getvalue().decode("utf-8")) @pytest.fixture def api_env(monkeypatch): monkeypatch.setenv("JYOTISH_API_RATE_LIMIT_PER_MINUTE", "0") reset_heavy_compute_gate() yield reset_heavy_compute_gate() def test_western_returns_planets_cusps_aspects_and_distribution(api_env) -> None: handler = _PostCaptureHandler("/api/western", SAMPLE) handler.do_POST() assert handler.status_code == 200 payload = handler.payload() natal = payload["natal"] assert payload["zodiac"] == "tropical" assert payload["house_system"] == "P" assert payload["coordinate_system"] == "tropical_ecliptic" assert len(natal["planets"]) == 11 assert len(natal["houses"]) == 12 assert natal["aspects"] assert set(natal["distribution"]["elements"]) == {"fire", "earth", "air", "water"} assert "岁差" in payload["boundary"] or "ayanamsa" in json.dumps(payload["sidereal_offset_degrees"]) def test_western_house_system_can_be_overridden_and_echoed(api_env) -> None: handler = _PostCaptureHandler("/api/western", {**SAMPLE, "house_system": "K"}) handler.do_POST() assert handler.status_code == 200 payload = handler.payload() assert payload["house_system"] == "K" placidus = _PostCaptureHandler("/api/western", SAMPLE) placidus.do_POST() assert placidus.payload()["house_system"] == "P" assert ( placidus.payload()["natal"]["houses"][2]["cusp"]["longitude"] != payload["natal"]["houses"][2]["cusp"]["longitude"] ) def test_ephemeris_events_90_day_window_has_sorted_ingress_and_station(api_env) -> None: handler = _PostCaptureHandler( "/api/ephemeris_events", { "start_date": "2026-09-15", "end_date": "2026-12-14", "ayanamsa": "raman", "node_mode": "mean", }, ) handler.do_POST() assert handler.status_code == 200 payload = handler.payload() events = payload["events"] kinds = {item["kind"] for item in events} assert "ingress" in kinds assert "station" in kinds dates = [item["date"] for item in events] assert dates == sorted(dates) assert payload["calculation"]["ayanamsa"] == "raman" assert len(events) == 14 def test_ephemeris_events_rejects_range_over_730_days(api_env) -> None: handler = _PostCaptureHandler( "/api/ephemeris_events", {"start_date": "2026-01-01", "end_date": "2028-01-02", "ayanamsa": "raman"}, ) handler.do_POST() assert handler.status_code == 400 payload = handler.payload() assert payload["success"] is False assert "730" in payload["error"] def test_ephemeris_ayanamsa_changes_the_event_list(api_env) -> None: raman = _PostCaptureHandler( "/api/ephemeris_events", {"start_date": "2026-09-15", "end_date": "2026-12-14", "ayanamsa": "raman"}, ) lahiri = _PostCaptureHandler( "/api/ephemeris_events", {"start_date": "2026-09-15", "end_date": "2026-12-14", "ayanamsa": "lahiri"}, ) raman.do_POST() lahiri.do_POST() assert raman.status_code == 200 assert lahiri.status_code == 200 assert raman.payload()["events"] != lahiri.payload()["events"] assert raman.payload()["calculation"]["ayanamsa"] == "raman" assert lahiri.payload()["calculation"]["ayanamsa"] == "lahiri"