221 lines
8.3 KiB
Python
221 lines
8.3 KiB
Python
from __future__ import annotations
|
|
import sys
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
import pytest
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SCRIPTS = ROOT / "scripts"
|
|
if str(SCRIPTS) not in sys.path:
|
|
sys.path.insert(0, str(SCRIPTS))
|
|
|
|
import domain_calculation_service as calculation_service # noqa: E402
|
|
import jyotish_api_server # noqa: E402
|
|
from jyotish_api_server import JyotishAPIHandler # noqa: E402
|
|
|
|
BIRTH = {
|
|
"year": 1990,
|
|
"month": 1,
|
|
"day": 1,
|
|
"hour": 12,
|
|
"minute": 0,
|
|
"second": 0,
|
|
"lat": 28.6139,
|
|
"lon": 77.2090,
|
|
"tz": 5.5,
|
|
"ayanamsa": "lahiri",
|
|
"node_mode": "true",
|
|
}
|
|
|
|
def test_domain_chart_exposes_effective_parameters_and_result_hash() -> None:
|
|
from domain_calculation_service import compute_chart
|
|
|
|
result = compute_chart(BIRTH)
|
|
|
|
assert result["calculation_contract"]["effective"]["node_mode"] == "true"
|
|
assert result["calculation_contract"]["effective"]["ayanamsa"] == "lahiri"
|
|
assert result["result_hash"]
|
|
|
|
|
|
def test_historical_timezone_resolution_distinguishes_new_york_dst() -> None:
|
|
winter = calculation_service.resolve_timezone_context(
|
|
lat=40.7128,
|
|
lon=-74.006,
|
|
local_datetime=datetime(1990, 1, 15, 12, 0),
|
|
)
|
|
summer = calculation_service.resolve_timezone_context(
|
|
lat=40.7128,
|
|
lon=-74.006,
|
|
local_datetime=datetime(1990, 7, 15, 12, 0),
|
|
)
|
|
|
|
assert winter == {
|
|
"timezone_id": "America/New_York",
|
|
"timezone_offset": -5.0,
|
|
"local_time_status": "resolved",
|
|
}
|
|
assert summer["timezone_id"] == "America/New_York"
|
|
assert summer["timezone_offset"] == -4.0
|
|
|
|
|
|
def test_timezone_resolution_refuses_dst_fold_and_gap_offsets() -> None:
|
|
folded = calculation_service.resolve_timezone_context(
|
|
lat=40.7128,
|
|
lon=-74.006,
|
|
local_datetime=datetime(2020, 11, 1, 1, 30),
|
|
)
|
|
gap = calculation_service.resolve_timezone_context(
|
|
lat=40.7128,
|
|
lon=-74.006,
|
|
local_datetime=datetime(2020, 3, 8, 2, 30),
|
|
)
|
|
|
|
assert folded["timezone_offset"] is None
|
|
assert folded["local_time_status"] == "ambiguous"
|
|
assert gap["timezone_offset"] is None
|
|
assert gap["local_time_status"] == "nonexistent"
|
|
|
|
|
|
def test_domain_chart_exposes_inferred_timezone_id() -> None:
|
|
payload = {key: value for key, value in BIRTH.items() if key != "tz"}
|
|
result = calculation_service.compute_chart(payload)
|
|
|
|
assert result["calculation_contract"]["effective"]["timezone_id"] == "Asia/Kolkata"
|
|
assert result["calculation_contract"]["effective"]["timezone_source"] == "iana_inferred"
|
|
|
|
|
|
def test_location_timezone_api_contract_preserves_unknown_local_time() -> None:
|
|
result = jyotish_api_server.resolve_location_timezone_payload({
|
|
"latitude": 51.5074,
|
|
"longitude": -0.1278,
|
|
"birthDate": "1990-01-01",
|
|
})
|
|
|
|
assert result["timezoneId"] == "Europe/London"
|
|
assert result["timezoneOffset"] is None
|
|
assert result["localTimeStatus"] == "not_provided"
|
|
|
|
def test_api_chart_uses_same_domain_contract_and_preserves_shape(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
import domain_calculation_service
|
|
import jyotish_api_server
|
|
from jyotish_api_server import JyotishAPIHandler
|
|
|
|
monkeypatch.setenv("JYOTISH_API_CHART_CACHE_TTL_SECONDS", "0")
|
|
monkeypatch.setenv("VEDASTRO_ENABLE_NETWORK", "0")
|
|
monkeypatch.setattr(
|
|
jyotish_api_server,
|
|
"_attach_vedastro_main_entry_overview",
|
|
lambda result, _birth: result,
|
|
)
|
|
|
|
expected = domain_calculation_service.compute_chart(BIRTH)
|
|
result = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(
|
|
{**BIRTH, "transit_date": "2026-07-11"}
|
|
)
|
|
|
|
assert result["result_hash"] == expected["result_hash"]
|
|
assert result["calculation_contract"] == expected["calculation_contract"]
|
|
assert result["birth"]["node_mode"] == "true"
|
|
assert result["planets"]
|
|
assert result["ascendant"]
|
|
assert "houses" in result
|
|
|
|
def test_domain_chart_exposes_effective_params_and_result_hash() -> None:
|
|
mean = calculation_service.compute_chart({**BIRTH, "node_mode": "mean"})
|
|
true = calculation_service.compute_chart({**BIRTH, "node_mode": "true"})
|
|
|
|
assert mean["calculation_contract"]["effective"]["ayanamsa"] == "lahiri"
|
|
assert true["calculation_contract"]["effective"]["node_mode"] == "true"
|
|
assert mean["planets"]["Rahu"]["lon"] != pytest.approx(true["planets"]["Rahu"]["lon"], abs=1e-8)
|
|
assert mean["result_hash"] != true["result_hash"]
|
|
|
|
def test_api_chart_response_uses_domain_contract_hash(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("JYOTISH_API_CHART_CACHE_TTL_SECONDS", "0")
|
|
monkeypatch.setenv("VEDASTRO_ENABLE_NETWORK", "0")
|
|
monkeypatch.setattr(
|
|
jyotish_api_server,
|
|
"_attach_vedastro_main_entry_overview",
|
|
lambda result, _birth: result,
|
|
)
|
|
expected = calculation_service.compute_chart({**BIRTH, "node_mode": "true"})
|
|
|
|
rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(
|
|
{**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"}
|
|
)
|
|
|
|
assert rest["result_hash"] == expected["result_hash"]
|
|
assert rest["birth"]["node_mode"] == "true"
|
|
assert rest["calculation_contract"]["effective"]["node_mode"] == "true"
|
|
|
|
def test_api_visible_chart_values_come_from_domain_service(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("JYOTISH_API_CHART_CACHE_TTL_SECONDS", "0")
|
|
monkeypatch.setenv("VEDASTRO_ENABLE_NETWORK", "0")
|
|
monkeypatch.setattr(
|
|
jyotish_api_server,
|
|
"_attach_vedastro_main_entry_overview",
|
|
lambda result, _birth: result,
|
|
)
|
|
request = {**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"}
|
|
expected = calculation_service.compute_chart(request)
|
|
|
|
rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(request)
|
|
|
|
assert rest["ascendant"]["lon"] == pytest.approx(expected["ascendant"]["lon"], abs=1e-8)
|
|
for planet in ("Sun", "Moon", "Rahu", "Ketu"):
|
|
assert rest["planets"][planet]["sign"] == expected["planets"][planet]["sign"]
|
|
assert rest["planets"][planet]["lon"] == pytest.approx(
|
|
expected["planets"][planet]["lon"], abs=1e-8
|
|
)
|
|
|
|
def test_api_sade_sati_uses_domain_true_saturn_transit(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("JYOTISH_API_CHART_CACHE_TTL_SECONDS", "0")
|
|
monkeypatch.setenv("VEDASTRO_ENABLE_NETWORK", "0")
|
|
monkeypatch.setattr(
|
|
jyotish_api_server,
|
|
"_attach_vedastro_main_entry_overview",
|
|
lambda result, _birth: result,
|
|
)
|
|
request = {**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"}
|
|
chart = calculation_service.compute_chart(request)
|
|
expected = calculation_service.compute_sade_sati(
|
|
moon_degree=chart["planets"]["Moon"]["lon"],
|
|
asc_degree=chart["ascendant"]["lon"],
|
|
reference_date="2026-07-11",
|
|
tz=BIRTH["tz"],
|
|
ayanamsa=BIRTH["ayanamsa"],
|
|
)
|
|
|
|
rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(request)
|
|
|
|
assert rest["sade_sati"]["transit_saturn_lon"] == pytest.approx(
|
|
expected["transit_saturn_lon"], abs=1e-8
|
|
)
|
|
assert rest["sade_sati"]["provenance"]["data_layer"] == "true_transit_positions"
|
|
assert rest["sade_sati"]["calculation_contract"]["algorithm"] == "sade_sati_true_saturn_transit"
|
|
|
|
def test_api_dasha_boundary_comes_from_domain_service(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
monkeypatch.setenv("JYOTISH_API_CHART_CACHE_TTL_SECONDS", "0")
|
|
monkeypatch.setenv("VEDASTRO_ENABLE_NETWORK", "0")
|
|
monkeypatch.setattr(
|
|
jyotish_api_server,
|
|
"_attach_vedastro_main_entry_overview",
|
|
lambda result, _birth: result,
|
|
)
|
|
request = {**BIRTH, "node_mode": "true", "transit_date": "2026-07-11"}
|
|
chart = calculation_service.compute_chart(request)
|
|
expected = calculation_service.compute_vimshottari_timeline(
|
|
birth_dt=datetime(1990, 1, 1, 12, 0),
|
|
moon_lon=chart["planets"]["Moon"]["lon"],
|
|
)
|
|
|
|
rest = JyotishAPIHandler.__new__(JyotishAPIHandler)._compute_chart_sync(request)
|
|
|
|
assert rest["dasha"]["current_md"] == expected["birth_balance"]["lord"]
|
|
assert rest["dasha"]["remaining_years"] == pytest.approx(
|
|
expected["birth_balance"]["remaining_years"], abs=1e-8
|
|
)
|
|
assert rest["dasha"]["start_date"] == expected["periods"][0]["start"]
|
|
assert rest["dasha"]["result_hash"] == expected["result_hash"]
|