Files
Jyotisha/tests/test_tajika.py

283 lines
12 KiB
Python

#!/usr/bin/env python3
"""Tests for tajika.py — Varshaphala annual chart, Tajika Yogas, Muntha."""
from __future__ import annotations
import sys, os, pytest
from datetime import datetime
SCRIPTS = os.path.join(os.path.dirname(__file__), '..', 'scripts')
if SCRIPTS not in sys.path:
sys.path.insert(0, SCRIPTS)
from tajika import (
calc_muntha, calc_year_lord, calc_mudda_dasha,
calc_tri_pataka, calc_tajika_yogas, detect_tajika_yogas,
detect_vedha, calc_all_sahams, calc_tajika_strength_layers, _is_faster,
SAHAM_RULES_PATH,
SIGNS, SIGN_LORDS,
)
# ── Muntha Tests ────────────────────────────────────────────────────
class TestMuntha:
def test_muntha_at_birth_age0(self):
result = calc_muntha(0, 0) # Aries asc, age 0
assert result['muntha_sign'] == 'Aries'
def test_muntha_advances_one_per_year(self):
result = calc_muntha(0, 1) # Aries + 1 year = Taurus
assert result['muntha_sign'] == 'Taurus'
def test_muntha_wraps_12(self):
result = calc_muntha(0, 12) # Back to Aries
assert result['muntha_sign'] == 'Aries'
def test_muntha_lord_correct(self):
result = calc_muntha(0, 1) # Taurus
assert result['muntha_lord'] == 'Venus'
def test_muntha_interpretation_exists(self):
result = calc_muntha(5, 10)
assert 'interpretation' in result
assert len(result['interpretation']) > 0
@pytest.mark.parametrize("asc_idx,age,expected_sign_idx",
[(0, 5, 5), (3, 7, 10), (11, 1, 0)])
def test_muntha_parametrized(self, asc_idx, age, expected_sign_idx):
result = calc_muntha(asc_idx, age)
assert result['muntha_sign_idx'] == expected_sign_idx
# ── Year Lord Tests ─────────────────────────────────────────────────
class TestYearLord:
def test_year_lord_matches_muntha_lord(self):
result = calc_year_lord(0, 1) # Taurus → Venus
assert result['year_lord'] == 'Venus'
def test_auxiliary_lords_present(self):
result = calc_year_lord(0, 5)
assert len(result['auxiliary_lords']) == 4
def test_year_theme_exists(self):
result = calc_year_lord(0, 5)
assert 'year_theme' in result
assert len(result['year_theme']) > 0
# ── Mudda Dasha Tests ──────────────────────────────────────────────
class TestMuddaDasha:
def test_starts_with_varsha_lord(self):
result = calc_mudda_dasha(0, 'Jupiter', 6)
assert result['dasha_sequence'][0]['lord'] == 'Jupiter'
def test_total_months_twelve(self):
result = calc_mudda_dasha(0, 'Sun', 1)
total = sum(d['months'] for d in result['dasha_sequence'])
assert abs(total - 12.0) < 0.5
def test_all_9_planets_covered(self):
result = calc_mudda_dasha(0, 'Jupiter', 1)
assert len(result['dasha_sequence']) <= 9
# ── Tri-Pataka Tests ───────────────────────────────────────────────
class TestTriPataka:
def test_all_strong_excellent(self):
# Put all lords in kendra houses from muntha
planet_lons = {
'Jupiter': 90.0, 'Saturn': 90.0, # Same sign as muntha = kendra
}
result = calc_tri_pataka(planet_lons, 'Jupiter', 3)
assert result['verdict'] in ('excellent', 'mixed', 'challenging')
def test_verdict_values(self):
planet_lons = {'Sun': 45.0, 'Moon': 135.0}
result = calc_tri_pataka(planet_lons, 'Sun', 0)
assert result['verdict'] in ('excellent', 'mixed', 'challenging')
def test_three_components_present(self):
planet_lons = {'Sun': 90.0, 'Moon': 180.0, 'Jupiter': 270.0}
result = calc_tri_pataka(planet_lons, 'Jupiter', 0)
assert 'dasha_lord' in result
assert 'muntha_lord' in result
assert 'year_lord' in result
# ── Harsha / Panchavargiya Bala Tests ─────────────────────────────
class TestTajikaStrengthLayers:
def test_harsha_and_panchavargiya_strength_layers(self):
planet_lons = {
'Sun': 10.0,
'Moon': 45.0,
'Mars': 92.0,
'Mercury': 152.0,
'Jupiter': 244.0,
'Venus': 330.0,
'Saturn': 300.0,
}
result = calc_tajika_strength_layers(planet_lons, asc_lon=15.0, year_lord='Jupiter')
assert result['status'] == 'partial'
assert result['method'] == 'Tajika Harsha/Panchavargiya Bala'
assert 'Panchavargiya Bala' in result['blocked_layers']
assert result['usable_layers'] == ['Harsha Bala']
assert result['summary']['next_action']
for planet in ('Sun', 'Moon', 'Mars', 'Mercury', 'Jupiter', 'Venus', 'Saturn'):
assert planet in result['harsha_bala']
assert result['harsha_bala'][planet]['status'] == 'usable'
assert planet in result['panchavargiya_bala']
assert result['panchavargiya_bala'][planet]['status'] == 'blocked'
assert result['combined_strength'][planet]['status'] == 'blocked'
assert result['combined_strength'][planet]['max_score'] > 0
# ── Tajika Yogas Tests ─────────────────────────────────────────────
class TestTajikaYogas:
def test_legacy_adapter_blocks_without_speeds(self):
# Moon(15.0) and Mercury(14.5) in same sign, close degrees
planet_lons = {'Moon': 15.0, 'Mercury': 14.5, 'Sun': 45.0,
'Mars': 90.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
result = calc_tajika_yogas(planet_lons)
assert result['status'] == 'blocked'
assert result['ithasala'] == []
assert len(result['ithasala']) >= 0 # May or may not detect based on rules
def test_graha_yuddha_is_not_inferred_by_legacy_adapter(self):
# Mercury and Venus very close
planet_lons = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 100.0, 'Venus': 100.5, 'Jupiter': 180.0, 'Saturn': 300.0}
result = calc_tajika_yogas(planet_lons)
assert result['graha_yuddha'] == []
def test_nakta_is_not_inferred_from_sun_moon_co_sign(self):
planet_lons = {'Sun': 15.0, 'Moon': 18.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
result = calc_tajika_yogas(planet_lons)
assert result['nakta'] == []
def test_summary_present(self):
planet_lons = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
result = calc_tajika_yogas(planet_lons)
assert 'summary' in result
assert 'Blocked' in result['summary']
class TestDetectTajikaYogas:
def test_legacy_detector_is_not_a_golden_case_oracle(self):
# Use close-degree planets to force Ithasala detection
planets = {'Sun': 14.0, 'Moon': 15.0, 'Mars': 90.0,
'Mercury': 14.8, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
yogas = detect_tajika_yogas(planets)
types = set(y['type'] for y in yogas)
assert isinstance(yogas, list)
def test_legacy_detector_does_not_prove_itasala(self):
# Moon(15) fast, Sun(14) slow → same sign, close
planets = {'Sun': 14.0, 'Moon': 15.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
yogas = detect_tajika_yogas(planets)
types = set(y['type'] for y in yogas)
assert isinstance(types, set)
def test_kuta_yoga_three_planets_same_sign(self):
planets = {'Sun': 5.0, 'Moon': 8.0, 'Mars': 12.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
yogas = detect_tajika_yogas(planets)
types = set(y['type'] for y in yogas)
assert 'Kuta' in types
# ── Vedha Detection Tests ──────────────────────────────────────────
class TestVedhaDetection:
def test_vedha_returns_list(self):
planets = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
result = detect_vedha(planets)
assert isinstance(result, list)
def test_vedha_structure(self):
planets = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0, 'Saturn': 300.0}
result = detect_vedha(planets)
if result:
assert 'planets' in result[0]
assert 'description' in result[0]
# ── Sahams Tests ────────────────────────────────────────────────────
class TestSahams:
def test_sahams_block_without_location_context(self):
result = calc_all_sahams({'Sun': 45.0, 'Moon': 120.0}, 10.0, datetime(1990, 6, 15, 12, 0))
assert result['status'] == 'blocked'
def test_tajika_module_exposes_saham_rules_reference_path(self):
assert SAHAM_RULES_PATH.endswith('references/saham_rules.json')
assert os.path.exists(SAHAM_RULES_PATH)
def test_all_sahams_calculated(self):
planet_lons = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0,
'Saturn': 300.0, 'Rahu': 150.0, 'Ketu': 330.0}
asc_lon = 10.0
birth_dt = datetime(1990, 6, 15, 10, 30)
result = calc_all_sahams(planet_lons, asc_lon, birth_dt, lat=39.9042, lon=116.4074, tz=8)
assert 'punya_saham' in result
assert 'karya_saham' in result
assert 'vivah_saham' in result
def test_saham_longitude_in_range(self):
planet_lons = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0,
'Saturn': 300.0, 'Rahu': 150.0, 'Ketu': 330.0}
result = calc_all_sahams(planet_lons, 10.0, datetime(1990, 6, 15, 10, 30))
for key, val in result.items():
if isinstance(val, dict) and 'longitude' in val:
assert 0 <= val['longitude'] < 360
def test_punya_saham_formula(self):
# Punya = Asc + (Moon - Sun), sourced from references/saham_rules.json
sun, moon, asc = 45.0, 120.0, 10.0
expected = (asc + (moon - sun)) % 360 # = 10 + 75 = 85
planet_lons = {'Sun': sun, 'Moon': moon, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0,
'Saturn': 300.0, 'Rahu': 150.0, 'Ketu': 330.0}
result = calc_all_sahams(planet_lons, asc, datetime(1990, 6, 15, 12, 0), lat=39.9042, lon=116.4074, tz=8)
assert abs(result['punya_saham']['longitude'] - expected) < 0.01
def test_karma_saham_uses_reference_json_day_formula(self):
# Karma_Saham day rule = Mars - Mercury + Asc, sourced from references/saham_rules.json
asc = 10.0
planet_lons = {'Sun': 45.0, 'Moon': 120.0, 'Mars': 90.0,
'Mercury': 60.0, 'Jupiter': 180.0, 'Venus': 210.0,
'Saturn': 300.0, 'Rahu': 150.0, 'Ketu': 330.0}
expected = (asc + (90.0 - 60.0)) % 360
result = calc_all_sahams(planet_lons, asc, datetime(1990, 6, 15, 12, 0), lat=39.9042, lon=116.4074, tz=8)
assert abs(result['karma_saham']['longitude'] - expected) < 0.01
def test_formula_saham_applies_reference_add_30_exception(self):
from tajika import _calc_formula_saham
# Asc 50 is outside the forward 100 -> 200 zodiacal arc.
result = _calc_formula_saham(
'Punya_Saham',
{'Sun': 200.0, 'Moon': 100.0},
50.0,
True,
{},
)
assert result == 340.0
def test_is_faster_moon_vs_sun(self):
assert _is_faster('Moon', 'Sun')
assert _is_faster('Mercury', 'Jupiter')