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

279 lines
11 KiB
Python

"""
Test suite for Vedic birth chart calculations in jyotishganit.
Tests basic chart generation, timezone handling, geographical variations,
and validates both planetary positions and house systems.
"""
from datetime import datetime
import pytest
from jyotishganit.core.constants import ZODIAC_SIGNS
from jyotishganit.main import calculate_birth_chart, get_birth_chart_json_string
class TestBirthChartBasic:
"""Basic birth chart generation tests."""
def test_chart_creation_delhi(self, sample_vedic_chart_delhi):
"""Test basic chart creation for Delhi location."""
chart = sample_vedic_chart_delhi
# Validate chart structure
assert hasattr(chart, "person")
assert hasattr(chart, "d1_chart")
assert hasattr(chart, "panchanga")
assert hasattr(chart, "divisional_charts")
assert hasattr(chart, "ashtakavarga")
assert hasattr(chart, "dashas")
# Validate person info
assert chart.person.latitude == 19.9993
assert chart.person.longitude == 73.7900
assert chart.person.timezone_offset == 5.5
def test_chart_creation_london(self, sample_vedic_chart_london):
"""Test basic chart creation for London location."""
chart = sample_vedic_chart_london
assert chart.person.latitude == pytest.approx(51.5074, abs=1e-3)
assert chart.person.longitude == pytest.approx(-0.1278, abs=1e-3)
assert chart.person.timezone_offset == 1.0
# Verify required chart components exist
assert len(chart.d1_chart.planets) == 9 # All planets (including Rahu/Ketu)
assert len(chart.d1_chart.houses) == 12
def test_chart_creation_nyc(self, sample_vedic_chart_nyc):
"""Test basic chart creation for NYC location."""
chart = sample_vedic_chart_nyc
assert chart.person.latitude == pytest.approx(40.7128, abs=1e-3)
assert chart.person.longitude == pytest.approx(-74.0060, abs=1e-3)
assert chart.person.timezone_offset == -4.0
def test_chart_jsonld_output(self, sample_vedic_chart_delhi):
"""Test JSON-LD output generation."""
chart = sample_vedic_chart_delhi
json_output = get_birth_chart_json_string(chart, indent=2)
assert isinstance(json_output, str)
assert '"@type": "VedicBirthChart"' in json_output
assert '"planets"' in json_output or '"d1Chart"' in json_output
assert '"houses"' in json_output
assert "jyotishganit" in json_output
class TestTimezoneHandling:
"""Test timezone offset handling and UTC conversion."""
@pytest.mark.parametrize(
"tz_offset",
[
0.0, # GMT
5.5, # IST
-4.0, # EDT
9.0, # JST
-8.0, # PST
3.0, # EET
],
)
def test_timezone_offsets(self, tz_offset):
"""Test chart calculation with various timezone offsets."""
birth = datetime(2020, 5, 15, 12, 0, 0) # Noon local time
chart = calculate_birth_chart(birth, 28.6139, 77.2090, tz_offset)
# Should not raise exceptions and create valid chart
assert isinstance(chart, type(chart)) # Any chart object
assert chart.person.timezone_offset == tz_offset
def test_fractional_timezone_offset(self):
"""Test with fractional timezone offsets (like IST 5.5)."""
birth = datetime(2020, 5, 15, 12, 0, 0)
chart = calculate_birth_chart(birth, 28.6139, 77.2090, 5.5)
assert chart.person.timezone_offset == 5.5
# Verify that astronomical positions were calculated (basic sanity check)
assert len(chart.d1_chart.planets) > 0
def test_timezone_edge_cases(self):
"""Test edge cases like midnight crossings and DST."""
test_cases = [
# Midnight transition
(datetime(2020, 1, 1, 0, 0, 0), 0.0, "GMT Midnight"),
# Near daylight saving transitions
(datetime(2020, 3, 8, 1, 0, 0), -5.0, "EST to EDT boundary"),
(datetime(2020, 11, 1, 1, 0, 0), -4.0, "EDT to EST boundary"),
]
for birth_time, tz, description in test_cases:
chart = calculate_birth_chart(birth_time, 40.7128, -74.0060, tz)
assert chart is not None, f"Failed for {description}"
class TestGeographicDiversity:
"""Test calculations across different geographic locations."""
def test_northern_hemisphere(self):
"""Test calculations north of equator."""
birth = datetime(2020, 5, 15, 12, 0, 0)
chart = calculate_birth_chart(birth, 51.5074, -0.1278, 1.0) # London
# Basic validations
assert chart.person.latitude > 0
assert len(chart.d1_chart.planets) == 9
def test_southern_hemisphere(self):
"""Test calculations south of equator."""
birth = datetime(2020, 5, 15, 12, 0, 0)
chart = calculate_birth_chart(birth, -33.8688, 151.2093, 10.0) # Sydney
assert chart.person.latitude < 0
assert abs(chart.person.latitude) == 33.8688
def test_antarctica_chart(self):
"""Test high latitude calculations (near pole)."""
birth = datetime(2020, 12, 21, 12, 0, 0)
# Test that extreme latitudes don't crash, even if some calculations may not be perfect
try:
chart = calculate_birth_chart(
birth, -77.85, 166.67, 13.0
) # Antarctica (McMurdo)
# Extreme southern latitude should be handled
assert chart.person.latitude == pytest.approx(-77.85, abs=1e-1)
# Chart should be created even if some strength calculations might have issues
assert chart.d1_chart is not None
except Exception as e:
pytest.skip(f"Extreme latitude calculations not fully supported: {e}")
def test_arctic_chart(self):
"""Test high latitude calculations (Arctic)."""
birth = datetime(2020, 6, 21, 12, 0, 0)
# Test that extreme latitudes don't crash
try:
chart = calculate_birth_chart(birth, 78.22, 15.63, 2.0) # Svalbard
assert chart.person.latitude == pytest.approx(78.22, abs=1e-1)
assert chart.d1_chart is not None
except Exception as e:
pytest.skip(f"Extreme latitude calculations not fully supported: {e}")
class TestTimeframeDiversity:
"""Test calculations across different historical timeframes."""
@pytest.mark.parametrize(
"year,month,day",
[
(1900, 1, 1), # Start of 20th century
(1950, 6, 15), # Mid-century
(1975, 12, 25), # Late 20th century
(2000, 1, 1), # Millennium
(2010, 3, 21), # 21st century
(2020, 2, 29), # Leap year (2020 is leap)
],
)
def test_historical_dates(self, year, month, day):
"""Test chart calculations for various historical dates."""
birth = datetime(year, month, day, 12, 0, 0)
chart = calculate_birth_chart(birth, 19.0760, 72.8777, 5.5) # Mumbai
assert chart is not None
assert chart.person.birth_datetime.year == year
# Verify ephemeris loaded and positions calculated
sun = next(p for p in chart.d1_chart.planets if p.celestial_body == "Sun")
assert sun.sign in ZODIAC_SIGNS
assert sun.sign_degrees is not None
assert 0.0 <= sun.sign_degrees <= 30.0
def test_leap_year_edge_cases(self):
"""Test February 29 dates and leap year boundaries."""
leap_dates = [
datetime(2020, 2, 29, 23, 59, 59), # End of leap day
datetime(1900, 3, 1, 0, 0, 1), # Century non-leap year transition
]
for birth_date in leap_dates:
chart = calculate_birth_chart(birth_date, 51.5074, -0.1278, 0.0)
assert chart is not None
class TestPlanetaryPositions:
"""Test planetary position calculations and zodiac placement."""
def test_all_planets_present(self, sample_vedic_chart_delhi):
"""Verify all classical planets are present in chart."""
chart = sample_vedic_chart_delhi
expected_planets = [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
"Rahu",
"Ketu",
]
actual_planets = [p.celestial_body for p in chart.d1_chart.planets]
assert set(actual_planets) == set(expected_planets)
def test_planet_zodiac_positions(self, sample_vedic_chart_delhi):
"""Verify planet positions are valid zodiac signs with valid degrees."""
chart = sample_vedic_chart_delhi
for planet in chart.d1_chart.planets:
assert planet.sign in ZODIAC_SIGNS
assert 0.0 <= planet.sign_degrees < 30.0
assert planet.house in range(1, 13) # Houses 1-12
def test_planet_house_assignment(self, sample_vedic_chart_delhi):
"""Verify all planets are assigned to valid houses."""
chart = sample_vedic_chart_delhi
for planet in chart.d1_chart.planets:
assert 1 <= planet.house <= 12
def test_ayanamsa_application(self, sample_vedic_chart_delhi):
"""Verify ayanamsa is applied to planetary positions."""
chart = sample_vedic_chart_delhi
for planet in chart.d1_chart.planets:
assert planet.sign_degrees is not None
class TestHouseSystem:
"""Test house system calculations and placements."""
def test_twelve_houses_present(self, sample_vedic_chart_delhi):
"""Verify all 12 houses are created."""
chart = sample_vedic_chart_delhi
assert len(chart.d1_chart.houses) == 12
def test_house_lords_assigned(self, sample_vedic_chart_delhi):
"""Verify all houses have valid lords assigned."""
chart = sample_vedic_chart_delhi
for house in chart.d1_chart.houses:
assert house.lord is not None
assert house.lord in [p.celestial_body for p in chart.d1_chart.planets]
def test_house_occupants_tracking(self, sample_vedic_chart_delhi):
"""Verify planets are correctly placed in houses."""
chart = sample_vedic_chart_delhi
house_occupants = {}
for planet in chart.d1_chart.planets:
house_num = planet.house
if house_num not in house_occupants:
house_occupants[house_num] = []
house_occupants[house_num].append(planet.celestial_body)
# Each planet should be in exactly one house
total_planets_placed = sum(
len(occupants) for occupants in house_occupants.values()
)
assert total_planets_placed == 9 # 9 classical planets
# Some houses may have multiple planets, but all must be accounted for
assert len(house_occupants) <= 12 # Can't have more houses than 12