Files
Jyotisha/references/open_source_sources/jyotishganit/tests/test_panchanga.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

219 lines
7.3 KiB
Python

"""
Test suite for Panchanga calculations in jyotishganit.
Tests tithi, nakshatra, yoga, karana, and vaara calculations
across various dates to ensure accuracy and verify ordering.
"""
from datetime import datetime
import pytest
from jyotishganit.components.panchanga import (
calculate_karana,
calculate_nakshatra,
calculate_tithi,
calculate_vaara,
calculate_yoga,
create_panchanga,
get_lunar_phase,
)
from jyotishganit.core.constants import NAKSHATRAS
from jyotishganit.main import calculate_birth_chart
class TestPanchangaCalculations:
"""Test Panchanga calculations for accuracy and consistency."""
@pytest.fixture
def sample_date_delhi(self):
"""Sample date with Delhi timezone."""
return datetime(2025, 1, 1, 10, 10, 0), 5.5, 19.9993, 73.7900
@pytest.fixture
def chart_delhi(self, sample_date_delhi):
"""Full birth chart for testing."""
dt, tz, lat, lon = sample_date_delhi
return calculate_birth_chart(dt, lat, lon, tz)
def test_panchanga_structure(self, chart_delhi):
"""Test that panchanga has all required components."""
panchanga = chart_delhi.panchanga
required_fields = ["tithi", "nakshatra", "yoga", "karana", "vaara"]
for field in required_fields:
assert hasattr(panchanga, field)
assert getattr(panchanga, field) is not None
def test_karana_calculation_sample_date(self, sample_date_delhi):
"""Test karana calculation for the sample date that showed Kaulava."""
dt, tz, lat, lon = sample_date_delhi
karana = calculate_karana(dt, tz)
print(f"Karana for {dt}: {karana}")
# Current calculation gives 'Kaulava', but user says should be 'Balava'
# Let's compute the lunar phase to see what's happening
from datetime import timedelta
from jyotishganit.core.astronomical import get_timescale
utc_dt = dt - timedelta(hours=tz)
ts = get_timescale()
t = ts.utc(
utc_dt.year,
utc_dt.month,
utc_dt.day,
utc_dt.hour,
utc_dt.minute,
utc_dt.second,
)
moon_phase = get_lunar_phase(t)
karana_index = int(moon_phase / 6) % 60
print(f"Lunar Phase: {moon_phase:.2f}°")
print(f"Karana Index: {karana_index}")
print(f"Karana Name: {karana}")
# If user is correct that it should be Balava, then this will help debug
assert isinstance(karana, str)
assert len(karana) > 0
def test_karana_ordering_consistency(self, sample_date_delhi):
"""Test karana ordering across multiple nearby dates."""
dt, tz, lat, lon = sample_date_delhi
from datetime import timedelta
karanas = []
for hour in range(0, 48, 2): # Every 2 hours over 2 days
test_dt = dt + timedelta(hours=hour)
try:
karana = calculate_karana(test_dt, tz)
karanas.append(karana)
except Exception as e:
print(f"Error at hour {hour}: {e}")
pass
# Should get a sequence that makes sense (repeating every ~6 hours)
assert len(karanas) > 0
print(f"Karana sequence over 2 days: {karanas[:10]}")
def test_lunar_phase_range(self, sample_date_delhi):
"""Test lunar phase values are within 0-360 degrees."""
dt, tz, lat, lon = sample_date_delhi
from datetime import timedelta
from jyotishganit.core.astronomical import get_timescale
utc_dt = dt - timedelta(hours=tz)
ts = get_timescale()
t = ts.utc(
utc_dt.year,
utc_dt.month,
utc_dt.day,
utc_dt.hour,
utc_dt.minute,
utc_dt.second,
)
moon_phase = get_lunar_phase(t)
assert 0.0 <= moon_phase <= 360.0
print(f"Lunar phase: {moon_phase:.2f}°")
def test_karana_names_are_valid(self, sample_date_delhi):
"""Test that karana names are from the official list."""
dt, tz, lat, lon = sample_date_delhi
from jyotishganit.core.constants import FIXED_KARANAS, MOVABLE_KARANAS
# All valid karana names
valid_karanas = MOVABLE_KARANAS + FIXED_KARANAS
karanas = []
for hour in range(0, 24, 1): # Every hour
from datetime import timedelta
test_dt = dt + timedelta(hours=hour)
karana = calculate_karana(test_dt, tz)
karanas.append(karana)
for karana in karanas:
assert karana in valid_karanas, f"{karana} not in valid karana names"
def test_tithi_sequence(self):
"""Test tithi calculation over a lunar month."""
base_dt = datetime(2025, 1, 1, 12, 0, 0)
tz = 5.5
tithis = []
for day in range(30):
test_dt = base_dt.replace(day=1 + day) if day > 0 else base_dt
tithi = calculate_tithi(test_dt, tz)
tithis.append(tithi)
# Should see progression through tithi names
print(f"Tithi sequence over month: {tithis[:10]}")
assert len(set(tithis)) > 1 # Should vary
def test_nakshatra_sequence(self):
"""Test nakshatra calculation over multiple days."""
base_dt = datetime(2025, 1, 1, 12, 0, 0)
tz = 5.5
nakshatras = []
for day in range(14): # Half lunar month for nakshatras
test_dt = base_dt.replace(day=1 + day) if day > 0 else base_dt
nakshatra = calculate_nakshatra(test_dt, tz, 24.0) # Approximate ayanamsa
nakshatras.append(nakshatra)
print(f"Nakshatra sequence over 2 weeks: {nakshatras}")
assert all(n in NAKSHATRAS for n in nakshatras)
def test_yoga_sequence(self):
"""Test yoga calculation over multiple days."""
base_dt = datetime(2025, 1, 1, 12, 0, 0)
tz = 5.5
from jyotishganit.core.constants import YOGA_NAMES
yogas = []
for day in range(14):
test_dt = base_dt.replace(day=1 + day) if day > 0 else base_dt
yoga = calculate_yoga(test_dt, tz, 24.0)
yogas.append(yoga)
print(f"Yoga sequence over 2 weeks: {yogas}")
assert all(y in YOGA_NAMES for y in yogas)
def test_vaara_calculation(self):
"""Test weekday calculation."""
test_dates = [
(
datetime(2025, 1, 1, 12, 0, 0),
"Wednesday",
), # January 1, 2025 is Wednesday
(datetime(2025, 1, 2, 12, 0, 0), "Thursday"),
(datetime(2025, 1, 3, 12, 0, 0), "Friday"),
]
for dt, expected in test_dates:
vaara = calculate_vaara(dt)
print(f"{dt.date()}: {vaara} (expected {expected})")
# Just test that it returns a valid day name
from jyotishganit.core.constants import VAARA_NAMES
assert vaara in VAARA_NAMES
def test_complete_panchanga_creation(self):
"""Test creating a complete panchanga with all components."""
from jyotishganit.core.astronomical import calculate_ayanamsa, get_timescale
dt = datetime(2025, 1, 15, 12, 0, 0)
tz = 5.5
# Calculate ayanamsa
t = get_timescale().utc(
dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second
)
ayanamsa_value = calculate_ayanamsa(t) # Already returns a float
create_panchanga(dt, tz, ayanamsa_value)