Harden prashna production entrypoints
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def test_full_reading_derives_saham_datetime_from_standard_chart_args() -> None:
|
||||
source = (Path(__file__).resolve().parents[1] / "scripts" / "jyotish_engine.py").read_text(encoding="utf-8")
|
||||
section = source[source.index("# ── Step 4.8: Tajika Yogas + Sahams"):source.index("# ── Step 4.9:")]
|
||||
assert "birth_dt = _birth_datetime_from_args(args)" in section
|
||||
assert "getattr(args, 'birth_datetime'" not in section
|
||||
|
||||
|
||||
def test_full_reading_supplies_actual_speeds_to_tajika_kernel() -> None:
|
||||
source = (Path(__file__).resolve().parents[1] / "scripts" / "jyotish_engine.py").read_text(encoding="utf-8")
|
||||
section = source[source.index("# ── Step 4.8: Tajika Yogas + Sahams"):source.index("# ── Step 4.9:")]
|
||||
assert '"longitude": item.get("degree_raw", item.get("lon"))' in section
|
||||
assert '"speed": item.get("speed")' in section
|
||||
@@ -0,0 +1,17 @@
|
||||
from datetime import datetime
|
||||
|
||||
from scripts.gulika import GHATIKA_END, calculate_gulika
|
||||
|
||||
|
||||
def test_gulika_uses_prasna_marga_weekday_table() -> None:
|
||||
assert GHATIKA_END[6] == {"day": 26, "night": 10}
|
||||
assert GHATIKA_END[0] == {"day": 22, "night": 6}
|
||||
|
||||
|
||||
def test_gulika_returns_sidereal_segment_ascendant_with_audit_trace() -> None:
|
||||
result = calculate_gulika(datetime(1990, 6, 15, 12, 0), lat=39.9042, lon=116.4074, tz=8)
|
||||
|
||||
assert result["status"] == "partial"
|
||||
assert 0 <= result["longitude"] < 360
|
||||
assert result["ghatika_end"] in range(0, 31)
|
||||
assert result["rule_source"].endswith("#3.5")
|
||||
@@ -0,0 +1,32 @@
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.prashna_context import PrashnaContextError, build_prashna_context
|
||||
|
||||
|
||||
def test_prashna_context_uses_backend_chart_from_question_moment():
|
||||
packet = build_prashna_context({
|
||||
"question_text": "Will this proceed?",
|
||||
"question_timestamp": "2026-07-12T12:00:00+08:00",
|
||||
"lat": 39.9042,
|
||||
"lon": 116.4074,
|
||||
"timezone": 8,
|
||||
"ayanamsa": "lahiri",
|
||||
"node_mode": "mean",
|
||||
"location_convention": "wgs84",
|
||||
})
|
||||
|
||||
assert packet["status"] == "computed"
|
||||
assert packet["chart_source"] == "swiss_ephemeris_backend"
|
||||
assert packet["ascendant"]["degree"] >= 0
|
||||
assert "Sun" in packet["planets"]
|
||||
assert "question_timestamp" in packet
|
||||
|
||||
|
||||
def test_prashna_context_rejects_missing_time_or_non_wgs84_location():
|
||||
base = {"question_text": "x", "lat": 1, "lon": 1, "timezone": 0}
|
||||
with pytest.raises(PrashnaContextError, match="question_timestamp"):
|
||||
build_prashna_context(base)
|
||||
with pytest.raises(PrashnaContextError, match="location_convention"):
|
||||
build_prashna_context({**base, "question_timestamp": "2026-01-01T00:00:00+00:00", "location_convention": "unknown"})
|
||||
@@ -0,0 +1,62 @@
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from scripts.jyotish_api_server import BadRequest, JyotishAPIHandler
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_cli_prashna_uses_question_moment_swiss_context_only():
|
||||
result = subprocess.run([
|
||||
sys.executable, "scripts/jyotish_engine.py", "prashna",
|
||||
"--datetime", "2026-07-12T12:00:00+08:00", "--question-text", "Test question",
|
||||
"--lat", "39.9042", "--lon", "116.4074", "--timezone", "8",
|
||||
], cwd=ROOT, text=True, capture_output=True, timeout=30, check=True)
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert payload["status"] == "computed"
|
||||
assert payload["chart_source"] == "swiss_ephemeris_backend"
|
||||
assert payload["supporting_indicators"]["gulika"]["status"] == "partial"
|
||||
assert payload["supporting_indicators"]["sphuta"]["status"] == "partial"
|
||||
assert "Kunda" in payload["blocked_layers"]
|
||||
|
||||
|
||||
def test_cli_prashna_blocks_legacy_approximation_modes():
|
||||
result = subprocess.run([
|
||||
sys.executable, "scripts/jyotish_engine.py", "prashna",
|
||||
"--datetime", "2026-07-12T12:00:00+08:00", "--question-text", "Test question",
|
||||
"--lat", "39.9042", "--lon", "116.4074", "--timezone", "8", "--mode", "sphutas",
|
||||
], cwd=ROOT, text=True, capture_output=True, timeout=30, check=True)
|
||||
payload = json.loads(result.stdout)
|
||||
|
||||
assert payload["status"] == "blocked"
|
||||
assert "sphutas" in payload["reason"]
|
||||
|
||||
|
||||
def test_api_prashna_rejects_client_planets_and_computes_context():
|
||||
handler = JyotishAPIHandler.__new__(JyotishAPIHandler)
|
||||
body = {
|
||||
"question_text": "Test question", "question_timestamp": "2026-07-12T12:00:00+08:00",
|
||||
"lat": 39.9042, "lon": 116.4074, "timezone": 8,
|
||||
"ayanamsa": "lahiri", "node_mode": "mean", "location_convention": "wgs84",
|
||||
}
|
||||
result = handler._compute_prashna(body)
|
||||
assert result["prashna_context"]["chart_source"] == "swiss_ephemeris_backend"
|
||||
with pytest.raises(BadRequest, match="forbidden"):
|
||||
handler._compute_prashna({**body, "planets": {"Sun": 0}})
|
||||
|
||||
|
||||
def test_web_prashna_collects_question_context_not_natal_chart():
|
||||
source = (ROOT / "jyotish-app" / "main.js").read_text(encoding="utf-8")
|
||||
markup = (ROOT / "jyotish-app" / "index.html").read_text(encoding="utf-8")
|
||||
|
||||
assert "question_timestamp: timestamp.value" in source
|
||||
assert "planets: chartData?.planets" not in source
|
||||
assert "asc_degree: chartData?.ascendant" not in source
|
||||
for field in ("prashna-timestamp", "prashna-lat", "prashna-lon", "prashna-timezone"):
|
||||
assert field in markup
|
||||
@@ -0,0 +1,23 @@
|
||||
from scripts.prashna_sphuta import calculate_sphuta_evidence
|
||||
|
||||
|
||||
def test_sphuta_formula_evidence_uses_exact_gulika_input() -> None:
|
||||
result = calculate_sphuta_evidence(
|
||||
ascendant_longitude=10,
|
||||
planet_longitudes={"Moon": 20, "Sun": 30, "Rahu": 40},
|
||||
gulika_longitude=50,
|
||||
)
|
||||
|
||||
assert result["status"] == "partial"
|
||||
assert result["points"] == {"trisphuta": 80.0, "catusphuta": 110.0, "pancasphuta": 150.0}
|
||||
|
||||
|
||||
def test_sphuta_evidence_blocks_missing_required_planet() -> None:
|
||||
result = calculate_sphuta_evidence(
|
||||
ascendant_longitude=10,
|
||||
planet_longitudes={"Moon": 20, "Sun": 30},
|
||||
gulika_longitude=50,
|
||||
)
|
||||
|
||||
assert result["status"] == "blocked"
|
||||
assert result["missing"] == ["Rahu"]
|
||||
@@ -0,0 +1,13 @@
|
||||
from datetime import datetime
|
||||
|
||||
from scripts.saham_daynight import determine_daytime
|
||||
|
||||
|
||||
def test_swiss_daynight_does_not_use_solar_house_proxy():
|
||||
noon = determine_daytime(datetime(2026, 7, 12, 12, 0), lat=39.9042, lon=116.4074, tz=8)
|
||||
midnight = determine_daytime(datetime(2026, 7, 12, 0, 0), lat=39.9042, lon=116.4074, tz=8)
|
||||
|
||||
assert noon["status"] == "computed"
|
||||
assert noon["is_daytime"] is True
|
||||
assert midnight["is_daytime"] is False
|
||||
assert noon["method"] == "swisseph.rise_trans"
|
||||
@@ -0,0 +1,33 @@
|
||||
from scripts.tajika_kernel import calculate_tajika_interactions
|
||||
|
||||
|
||||
def _seven():
|
||||
return {
|
||||
"Sun": {"longitude": 0, "speed": 1.0}, "Moon": {"longitude": 49, "speed": 13.0},
|
||||
"Mars": {"longitude": 180, "speed": 0.5}, "Mercury": {"longitude": 260, "speed": 1.2},
|
||||
"Jupiter": {"longitude": 310, "speed": 0.08}, "Venus": {"longitude": 130, "speed": 1.0},
|
||||
"Saturn": {"longitude": 220, "speed": 0.03}, "Rahu": {"longitude": 60, "speed": -0.05},
|
||||
}
|
||||
|
||||
|
||||
def test_kernel_detects_cross_sign_aspect_and_excludes_nodes():
|
||||
result = calculate_tajika_interactions(_seven())
|
||||
|
||||
pair = next(row for row in result["interactions"] if row["planets"] == ["Sun", "Moon"])
|
||||
assert pair["aspect"] == 60.0
|
||||
assert pair["motion"] == "applying"
|
||||
candidate = next(row for row in result["candidate_yogas"] if row["planets"] == ["Sun", "Moon"])
|
||||
assert candidate["name"] == "Ithasala_candidate"
|
||||
assert candidate["status"] == "partial"
|
||||
assert result["nodes_excluded"] is True
|
||||
assert all("Rahu" not in row["planets"] for row in result["interactions"])
|
||||
|
||||
|
||||
def test_kernel_blocks_missing_speed_instead_of_guessing_motion():
|
||||
planets = _seven()
|
||||
del planets["Venus"]["speed"]
|
||||
|
||||
result = calculate_tajika_interactions(planets)
|
||||
|
||||
assert result["status"] == "blocked"
|
||||
assert "Venus" in result["missing"]
|
||||
Reference in New Issue
Block a user