Files
Jyotisha/references/open_source_sources/jyotishganit/tests/test_dashas.py
T
732642856 f83db2fac1 Enhance Jyotish validation and Jaimini modules
- add external validation reports and open-source comparison references

- integrate Jaimini arudha/graha pada, enhanced argala, and additional synastry kutas

- update skill docs and capability matrices

- add smoke tests for open-source integrations
2026-06-10 20:50:52 +08:00

408 lines
14 KiB
Python

"""
Test suite for Vimshottari Dasha calculations in jyotishganit.
Tests dasha period calculations, Mahadasha start dates, sub-period divisions,
and consistency with classical references.
"""
from datetime import datetime, timedelta
from jyotishganit.core.constants import (
HUMAN_LIFE_SPAN_FOR_VIMSHOTTARI,
VIMSHOTTARI_DASHA_DURATIONS,
)
from jyotishganit.dasha.vimshottari import (
calculate_dasha_start_date,
calculate_vimshottari_dashas,
get_next_adhipati,
)
from jyotishganit.main import calculate_birth_chart
class TestDashaConstants:
"""Test Vimshottari constants and fundamental values."""
def test_dasha_durations_sum_correct(self):
"""Verify total dasha durations sum to human lifespan."""
total_years = sum(VIMSHOTTARI_DASHA_DURATIONS.values())
expected_total = HUMAN_LIFE_SPAN_FOR_VIMSHOTTARI
assert total_years == expected_total, (
f"Total dasha years {total_years} != {expected_total}"
)
def test_all_planets_have_durations(self):
"""Verify all 9 Vimshottari planets have duration assignments."""
expected_planets = [
"Sun",
"Moon",
"Mars",
"Rahu",
"Jupiter",
"Saturn",
"Mercury",
"Ketu",
"Venus",
]
actual_planets = list(VIMSHOTTARI_DASHA_DURATIONS.keys())
assert set(actual_planets) == set(expected_planets)
def test_dasha_durations_positive(self):
"""Verify all dasha durations are positive."""
for planet, years in VIMSHOTTARI_DASHA_DURATIONS.items():
assert years > 0, f"{planet} has non-positive duration: {years}"
class TestMoonNakshatraCalculation:
"""Test Moon nakshatra calculation for dasha determination."""
def test_nakshatra_index_range(self):
"""Test nakshatra calculation returns valid index."""
birth = datetime(2020, 5, 15, 12, 0, 0)
chart = calculate_birth_chart(birth, 28.6139, 77.2090, 5.5)
# Extract Moon position for nakshatra calc
moon = next(
(p for p in chart.d1_chart.planets if p.celestial_body == "Moon"), None
)
assert moon is not None
# Mock the calculation (would use Skyfield in real implementation)
moon_longitude = [
"Aries",
"Taurus",
"Gemini",
"Cancer",
"Leo",
"Virgo",
"Libra",
"Scorpio",
"Sagittarius",
"Capricorn",
"Aquarius",
"Pisces",
].index(moon.sign) * 30 + moon.sign_degrees
nakshatra_span = 360.0 / 27.0
nak_index = int(moon_longitude / nakshatra_span)
remainder_degs = moon_longitude % nakshatra_span
# Valid range checks
assert 0 <= nak_index <= 26, f"Nakshatra index {nak_index} out of range 0-26"
assert 0 <= remainder_degs <= nakshatra_span, (
f"Remainder {remainder_degs} out of range"
)
class TestDashaStartCalculation:
"""Test calculation of dasha start dates from birth."""
def test_start_date_before_birth(self):
"""Verify dasha start date is always before birth date."""
birth = datetime(1994, 10, 23, 10, 20, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5)
_, dasha_start = calculate_dasha_start_date(birth, 5.5, chart.ayanamsa.value)
assert dasha_start < birth, (
f"Dasha start {dasha_start} should be before birth {birth}"
)
def test_birth_within_dasha_period(self):
"""Verify birth date falls within calculated dasha period."""
birth = datetime(1994, 10, 23, 10, 20, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5)
lord, start_date = calculate_dasha_start_date(birth, 5.5, chart.ayanamsa.value)
# Calculate period end
duration_years = VIMSHOTTARI_DASHA_DURATIONS[lord]
end_date = start_date + timedelta(days=duration_years * 365.25)
# Birth should be within this period
assert start_date <= birth <= end_date, (
f"Birth {birth} not within period {start_date} to {end_date}"
)
def test_valid_planet_returned(self):
"""Verify returned lord is a valid Vimshottari planet."""
birth = datetime(1994, 10, 23, 10, 20, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5)
lord, _ = calculate_dasha_start_date(birth, 5.5, chart.ayanamsa.value)
assert lord in VIMSHOTTARI_DASHA_DURATIONS, f"Invalid lord returned: {lord}"
class TestSequenceLogic:
"""Test the Vimshottari sequence progression."""
def test_next_adhipati_cyclic(self):
"""Test that next lord cycles through complete sequence."""
# Start with Sun and get full cycle
current = "Sun"
sequence = [current]
for _ in range(8): # Should get all planets except starting one
current = get_next_adhipati(current)
sequence.append(current)
# Should return to Sun
next_after_last = get_next_adhipati(sequence[-1])
assert next_after_last == "Sun"
def test_sequence_order_consistent(self):
"""Test Vimshottari sequence matches classical order."""
# Classical Vimshottari sequence
expected_sequence = [
"Sun",
"Moon",
"Mars",
"Rahu",
"Jupiter",
"Saturn",
"Mercury",
"Ketu",
"Venus",
]
current = "Sun"
actual_sequence = [current]
for i in range(8):
current = get_next_adhipati(current)
actual_sequence.append(current)
# Check each step matches expected
assert current == expected_sequence[i + 1], (
f"Sequence mismatch at position {i + 1}: expected {expected_sequence[i + 1]}, got {current}"
)
assert actual_sequence == expected_sequence
class TestCompleteDashaCalculation:
"""Test full Vimshottari dasha calculations."""
def test_dashas_all_periods_structure(self, sample_vedic_chart_delhi):
"""Test structure of complete dasha periods."""
chart = sample_vedic_chart_delhi
dashas = calculate_vimshottari_dashas(
chart.person.birth_datetime,
chart.person.timezone_offset,
chart.person.latitude,
chart.person.longitude,
chart.ayanamsa.value,
)
# Should have all major sections
assert hasattr(dashas, "all")
assert hasattr(dashas, "current")
assert hasattr(dashas, "upcoming")
assert hasattr(dashas, "balance")
# Should have mahadashas
assert "mahadashas" in dashas.all
mahadashas = dashas.all["mahadashas"]
assert len(mahadashas) == 9, f"Expected 9 mahadashas, got {len(mahadashas)}"
def test_mahadasha_coverage(self):
"""Test that mahadashas cover human lifespan."""
birth = datetime(1994, 10, 23, 10, 20, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5)
dashas = calculate_vimshottari_dashas(
birth, 5.5, 19.0760, 72.8777, chart.ayanamsa.value
)
total_span = timedelta(0)
for _lord, data in dashas.all["mahadashas"].items():
duration = data["end"] - data["start"]
total_span += duration
# Should cover expected lifespan (with some tolerance for calculation)
expected_days = HUMAN_LIFE_SPAN_FOR_VIMSHOTTARI * 365.25
actual_days = total_span.total_seconds() / (24 * 3600)
tolerance_days = 10 # Allow small calculation differences
assert abs(actual_days - expected_days) < tolerance_days
def test_subperiods_exist(self, sample_vedic_chart_delhi):
"""Test that antardashas and pratyantardashas are calculated."""
chart = sample_vedic_chart_delhi
dashas = calculate_vimshottari_dashas(
chart.person.birth_datetime,
chart.person.timezone_offset,
chart.person.latitude,
chart.person.longitude,
chart.ayanamsa.value,
max_depth=3, # Include pratyantardashas
)
# Check first mahadasha
first_md = list(dashas.all["mahadashas"].keys())[0]
ma_data = dashas.all["mahadashas"][first_md]
# Should have antardashas
assert "antardashas" in ma_data
antardashas = ma_data["antardashas"]
# Should have 9 antardashas
assert len(antardashas) == 9
# Check first antardasha has pratyantardashas
first_ad = list(antardashas.keys())[0]
ad_data = antardashas[first_ad]
assert "pratyantardashas" in ad_data
assert len(ad_data["pratyantardashas"]) == 9
def test_balance_calculation(self):
"""Test dasha balance at birth calculation."""
birth = datetime(1994, 10, 23, 10, 20, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5)
dashas = calculate_vimshottari_dashas(
birth, 5.5, 19.0760, 72.8777, chart.ayanamsa.value
)
# Should have balance for one planet
assert len(dashas.balance) == 1
balance_planet = list(dashas.balance.keys())[0]
balance_years = dashas.balance[balance_planet]
# Balance should be positive (remaining years in current dasha)
assert balance_years > 0, f"Balance years should be positive: {balance_years}"
# Should be less than or equal to total duration of that planet's dasha
total_duration = VIMSHOTTARI_DASHA_DURATIONS[balance_planet]
assert balance_years <= total_duration, (
f"Balance {balance_years} exceeds total duration {total_duration}"
)
def test_current_dasha_identification(self, sample_vedic_chart_delhi):
"""Test identification of current running dasha."""
chart = sample_vedic_chart_delhi
dashas = calculate_vimshottari_dashas(
chart.person.birth_datetime,
chart.person.timezone_offset,
chart.person.latitude,
chart.person.longitude,
chart.ayanamsa.value,
)
# Current should exist and have proper structure
assert "mahadashas" in dashas.current
assert len(dashas.current["mahadashas"]) == 1 # Only one current mahadasha
current_md = list(dashas.current["mahadashas"].keys())[0]
current_data = dashas.current["mahadashas"][current_md]
# Should have start/end dates and antardashas
assert "start" in current_data
assert "end" in current_data
assert "antardashas" in current_data
# Current time should be within current mahadasha period
now = datetime.now()
assert current_data["start"] <= now <= current_data["end"]
def test_upcoming_dashas(self, sample_vedic_chart_delhi):
"""Test upcoming dasha identification."""
chart = sample_vedic_chart_delhi
dashas = calculate_vimshottari_dashas(
chart.person.birth_datetime,
chart.person.timezone_offset,
chart.person.latitude,
chart.person.longitude,
chart.ayanamsa.value,
)
# Should have upcoming periods
assert "mahadashas" in dashas.upcoming
upcoming_count = sum(
len(md_data.get("antardashas", {}))
for md_data in dashas.upcoming["mahadashas"].values()
)
# Should have 3 upcoming antardashas as mentioned in implementation
assert upcoming_count <= 3, f"Too many upcoming periods: {upcoming_count}"
class TestConsistency:
"""Test calculation consistency and edge cases."""
def test_deterministic_calculations(self):
"""Test that repeated calculations give same results."""
birth = datetime(1994, 10, 23, 10, 20, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5)
dashas1 = calculate_vimshottari_dashas(
birth, 5.5, 19.0760, 72.8777, chart.ayanamsa.value
)
dashas2 = calculate_vimshottari_dashas(
birth, 5.5, 19.0760, 72.8777, chart.ayanamsa.value
)
# Balance should be identical
assert dashas1.balance == dashas2.balance
# Check few dates are same (avoid full nested comparison)
first_md_1 = list(dashas1.all["mahadashas"].keys())[0]
first_md_2 = list(dashas2.all["mahadashas"].keys())[0]
assert first_md_1 == first_md_2
date1 = dashas1.all["mahadashas"][first_md_1]["start"]
date2 = dashas2.all["mahadashas"][first_md_2]["start"]
assert date1 == date2
def test_different_ayanamsa_effect(self):
"""Test that different ayanamsa values affect moon nakshatra."""
birth = datetime(2020, 5, 15, 12, 0, 0)
# Calculate with two different ayanamsa values
dashas_lahiri = calculate_vimshottari_dashas(
birth, 5.5, 19.0760, 72.8777, 23.85
) # Lahiri
dashas_pushya = calculate_vimshottari_dashas(
birth, 5.5, 19.0760, 72.8777, 21.21
) # Pushya
# Starting dasha lord might differ (moon nakshatra depends on ayanamsa)
lord_lahiri = list(dashas_lahiri.balance.keys())[0]
lord_pushya = list(dashas_pushya.balance.keys())[0]
# Might be different due to ayanamsa difference
# Just verify both return valid lords
assert lord_lahiri in VIMSHOTTARI_DASHA_DURATIONS
assert lord_pushya in VIMSHOTTARI_DASHA_DURATIONS
def test_edge_dates(self):
"""Test dasha calculation for edge dates."""
edge_dates = [
datetime(1900, 1, 1, 0, 0, 0), # Century start
datetime(2000, 1, 1, 0, 0, 0), # Millennium
datetime(2020, 2, 29, 12, 0, 0), # Leap year leap day
datetime(2023, 12, 31, 23, 59, 59), # Year end
]
for birth_date in edge_dates:
chart = calculate_birth_chart(birth_date, 28.6139, 77.2090, 0.0)
dashas = calculate_vimshottari_dashas(
birth_date, 0.0, 28.6139, 77.2090, chart.ayanamsa.value
)
# Should not raise exceptions and have valid structure
assert len(dashas.all["mahadashas"]) == 9
assert len(dashas.balance) == 1