f83db2fac1
- 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
279 lines
11 KiB
Python
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
|