c140191357
API birth payloads store hour/minute as floats, and datetime() rejected them after sensitivity was wired into every consultation_workflow call. Co-authored-by: Cursor <cursoragent@cursor.com>
188 lines
6.0 KiB
Python
188 lines
6.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Regression: API float hour/minute must not crash consultation_workflow."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
import os
|
|
import sys
|
|
|
|
import pytest
|
|
|
|
SCRIPTS = os.path.join(os.path.dirname(__file__), "..", "scripts")
|
|
if SCRIPTS not in sys.path:
|
|
sys.path.insert(0, SCRIPTS)
|
|
|
|
import flexible_birth_time_profile as profile_module # noqa: E402
|
|
from jyotish_engine import _birth_datetime_from_args # noqa: E402
|
|
from scripts.consultation_domain_registry import CANONICAL_DOMAINS # noqa: E402
|
|
from scripts.jyotish_api_server import JyotishAPIHandler, _load_local_module # noqa: E402
|
|
|
|
|
|
_SMOKE_BIRTH = {
|
|
"year": 1993,
|
|
"month": 6,
|
|
"day": 15,
|
|
"hour": 10.0,
|
|
"minute": 30.0,
|
|
"lat": 36.42,
|
|
"lon": 114.21,
|
|
"tz": 8,
|
|
}
|
|
|
|
|
|
def _handler() -> JyotishAPIHandler:
|
|
return JyotishAPIHandler.__new__(JyotishAPIHandler)
|
|
|
|
|
|
def _stub_consultation_runtime(monkeypatch, handler: JyotishAPIHandler) -> None:
|
|
chart = {
|
|
"success": True,
|
|
"birth_info": {"date": "1993-06-15", "time": "10:30", "tz": 8},
|
|
"planets": {},
|
|
"ascendant": {},
|
|
"modules": {},
|
|
"ai_prompt_pack": {
|
|
"evidence_snapshot": {
|
|
"strict_workflow_contracts": {
|
|
domain: {"status": "available", "domain": domain}
|
|
for domain in CANONICAL_DOMAINS
|
|
}
|
|
}
|
|
},
|
|
}
|
|
monkeypatch.setattr(handler, "_compute_chart", lambda body: chart)
|
|
monkeypatch.setattr(
|
|
handler,
|
|
"_compute_rectification_gate",
|
|
lambda body: {
|
|
"success": True,
|
|
"endpoint": "rectification_gate",
|
|
"summary": {"recommended_events": [], "warned": [], "disabled": []},
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
handler,
|
|
"_compute_muhurta_panchanga",
|
|
lambda body: {"status": "ok", "scope": "muhurta_panchanga"},
|
|
)
|
|
monkeypatch.setattr(
|
|
handler,
|
|
"_compute_vedastro_gateway_run",
|
|
lambda body: {
|
|
"scope": "vedastro_gateway_run",
|
|
"status": "official_blocked",
|
|
"official_closure_state": "official_blocked",
|
|
"official_closure_reason": "test_stub",
|
|
},
|
|
)
|
|
monkeypatch.setattr(
|
|
profile_module,
|
|
"_recast_candidate_layers",
|
|
lambda candidate, **_kwargs: {
|
|
"ascendant": {"sign": "Aries" if candidate.minute < 30 else "Taurus"},
|
|
"varga_lagna": {"D9": {"sign": "Gemini"}, "D10": {"sign": "Cancer"}},
|
|
"arudha": {"A7": {"sign": "Leo"}, "A10": {"sign": "Virgo"}, "UL": {"sign": "Libra"}},
|
|
"kp_cusps": {"house_10": {"sub_lord": "Saturn"}},
|
|
},
|
|
)
|
|
|
|
|
|
def _provisional_body(theme: str) -> dict:
|
|
return {
|
|
"entry_mode": "direct_chart",
|
|
"question": theme,
|
|
"theme": [theme],
|
|
**_SMOKE_BIRTH,
|
|
"birth_time_accuracy": "provisional",
|
|
"representative_time": "10:30",
|
|
"western_mode": False,
|
|
"defer_optional_external_evidence": True,
|
|
}
|
|
|
|
|
|
def test_quality_gate_runs_this_file() -> None:
|
|
from scripts.run_quality_gate import CORE_PYTEST_TARGETS
|
|
|
|
assert "tests/test_consultation_workflow_birth_time_sensitivity.py" in CORE_PYTEST_TARGETS
|
|
|
|
|
|
def test_high_rigor_birth_payload_keeps_hour_minute_as_float() -> None:
|
|
payload = _handler()._high_rigor_birth_payload(_SMOKE_BIRTH)
|
|
assert isinstance(payload["hour"], float)
|
|
assert isinstance(payload["minute"], float)
|
|
assert payload["hour"] == 10.0
|
|
assert payload["minute"] == 30.0
|
|
|
|
|
|
def test_birth_datetime_from_args_accepts_float_clock_fields() -> None:
|
|
args = type("Args", (), {
|
|
"year": 1993,
|
|
"month": 6,
|
|
"day": 15,
|
|
"hour": 10.0,
|
|
"minute": 30.0,
|
|
"second": 0.0,
|
|
})()
|
|
assert _birth_datetime_from_args(args) == datetime(1993, 6, 15, 10, 30, 0)
|
|
|
|
|
|
@pytest.mark.parametrize("theme", ["career", "marriage", "wealth", "timing", "health"])
|
|
def test_consultation_workflow_accepts_float_hour_minute_with_provisional_accuracy(
|
|
monkeypatch,
|
|
theme: str,
|
|
) -> None:
|
|
handler = _handler()
|
|
_stub_consultation_runtime(monkeypatch, handler)
|
|
|
|
result = handler._compute_consultation_workflow(_provisional_body(theme))
|
|
|
|
assert result["success"] is True
|
|
packet = result["birth_time_sensitivity"]
|
|
assert packet["schema"] == "jyotish.report_birth_time_sensitivity.v1"
|
|
assert packet["status"] == "candidate_window_only"
|
|
assert packet["accuracy"] == "provisional"
|
|
assert packet["window"]["representative_time"] == "10:30"
|
|
|
|
|
|
def test_consultation_workflow_without_sensitivity_fields_still_succeeds(monkeypatch) -> None:
|
|
handler = _handler()
|
|
_stub_consultation_runtime(monkeypatch, handler)
|
|
|
|
result = handler._compute_consultation_workflow({
|
|
"entry_mode": "direct_chart",
|
|
"question": "career",
|
|
"theme": ["career"],
|
|
**_SMOKE_BIRTH,
|
|
"western_mode": False,
|
|
"defer_optional_external_evidence": True,
|
|
})
|
|
|
|
assert result["success"] is True
|
|
packet = result["birth_time_sensitivity"]
|
|
assert packet["schema"] == "jyotish.report_birth_time_sensitivity.v1"
|
|
assert packet["status"] in {"not_applicable", "candidate_window_only"}
|
|
|
|
|
|
def test_consultation_workflow_degrades_sensitivity_failure_instead_of_raising(monkeypatch) -> None:
|
|
handler = _handler()
|
|
_stub_consultation_runtime(monkeypatch, handler)
|
|
engine = _load_local_module("jyotish_engine")
|
|
|
|
def _boom(_args):
|
|
raise TypeError("'float' object cannot be interpreted as an integer")
|
|
|
|
monkeypatch.setattr(engine, "_build_birth_time_sensitivity", _boom)
|
|
|
|
result = handler._compute_consultation_workflow(_provisional_body("career"))
|
|
|
|
assert result["success"] is True
|
|
assert result["birth_time_sensitivity"] == {
|
|
"schema": "jyotish.report_birth_time_sensitivity.v1",
|
|
"status": "not_applicable",
|
|
"availability": "not_available",
|
|
"blocked": True,
|
|
"reason": "birth_time_sensitivity_unavailable",
|
|
"error_type": "TypeError",
|
|
}
|