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

888 lines
29 KiB
Python

"""
Pytest test suite for jyotishganit.components.strengths (Vedic Astrology Strength Calculations)
"""
import datetime
import os
import sys
from unittest.mock import Mock, patch
import pytest
# Add parent directory to path for imports if needed
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from jyotishganit.components.strengths import (
angdiff,
calculate_all_strengths,
calculate_degrees_in_varga_sign,
compute_ayanabala,
compute_bhava_adhipathi_bala,
compute_bhava_dig_bala,
compute_bhava_drik_bala,
compute_chestagbala,
compute_digbala,
compute_drekkanabala,
compute_drikbala,
compute_ishtakashtabalas,
compute_kendradhibala,
compute_naisargikabala,
compute_nathonnatabala,
compute_ojhayugmarashiamsabala,
compute_pakshabala,
compute_saptavargajabala,
compute_shadbala,
compute_sthanabala,
compute_tribhagabala,
compute_uchhabala,
compute_vimshopaka_balas,
compute_yuddhabala,
get_angular_distance_between_planets,
get_planetary_dignity_classification,
get_sputa_drishti_degree,
get_varga_sign,
normalize,
planet_longitude_from_sign,
)
# ============================================================================
# FIXTURES
# ============================================================================
@pytest.fixture
def mock_planet():
"""Create a mock planet object."""
planet = Mock()
planet.celestial_body = "Sun"
planet.sign = "Aries"
planet.sign_degrees = 15.0
planet.house = 1
planet.shadbala = {}
return planet
@pytest.fixture
def mock_chart():
"""Create a mock chart with planets and houses."""
chart = Mock()
chart.planets = []
chart.houses = []
# Create mock planets
sun = Mock(
celestial_body="Sun", sign="Aries", sign_degrees=10.0, house=1, shadbala={}
)
moon = Mock(
celestial_body="Moon", sign="Taurus", sign_degrees=20.0, house=2, shadbala={}
)
mars = Mock(
celestial_body="Mars", sign="Scorpio", sign_degrees=5.0, house=8, shadbala={}
)
mercury = Mock(
celestial_body="Mercury", sign="Gemini", sign_degrees=15.0, house=3, shadbala={}
)
jupiter = Mock(
celestial_body="Jupiter",
sign="Sagittarius",
sign_degrees=12.0,
house=9,
shadbala={},
)
venus = Mock(
celestial_body="Venus", sign="Libra", sign_degrees=18.0, house=7, shadbala={}
)
saturn = Mock(
celestial_body="Saturn",
sign="Capricorn",
sign_degrees=22.0,
house=10,
shadbala={},
)
chart.planets = [sun, moon, mars, mercury, jupiter, venus, saturn]
# Create mock houses
signs = [
"Aries",
"Taurus",
"Gemini",
"Cancer",
"Leo",
"Virgo",
"Libra",
"Scorpio",
"Sagittarius",
"Capricorn",
"Aquarius",
"Pisces",
]
lords = [
"Mars",
"Venus",
"Mercury",
"Moon",
"Sun",
"Mercury",
"Venus",
"Mars",
"Jupiter",
"Saturn",
"Saturn",
"Jupiter",
]
for i in range(12):
house = Mock()
house.sign = signs[i]
house.sign_degrees = 0.0
house.lord = lords[i]
chart.houses.append(house)
return chart
@pytest.fixture
def mock_person():
"""Create a mock person object."""
person = Mock()
person.birth_datetime = datetime.datetime(1990, 1, 1, 12, 0, 0)
person.timezone_offset = 0
person.latitude = 28.6139 # Delhi
person.longitude = 77.2090
return person
# ============================================================================
# HELPER FUNCTION TESTS
# ============================================================================
class TestHelperFunctions:
"""Test basic helper functions."""
def test_normalize_positive(self):
"""Test normalize with positive angles."""
assert normalize(370) == 10
assert normalize(720) == 0
assert normalize(180) == 180
def test_normalize_negative(self):
"""Test normalize with negative angles."""
assert normalize(-10) == 350
assert normalize(-370) == 350
def test_normalize_zero(self):
"""Test normalize with zero."""
assert normalize(0) == 0
def test_angdiff_basic(self):
"""Test angular difference calculation."""
assert angdiff(10, 20) == 10
assert angdiff(350, 10) == 20
assert angdiff(0, 180) == 180
def test_angdiff_wraparound(self):
"""Test angular difference with wraparound."""
assert angdiff(10, 350) == 20
assert angdiff(350, 10) == 20
def test_planet_longitude_from_sign(self):
"""Test planet longitude calculation from sign and degrees."""
assert planet_longitude_from_sign("Aries", 0) == 0
assert planet_longitude_from_sign("Aries", 15) == 15
assert planet_longitude_from_sign("Taurus", 0) == 30
assert planet_longitude_from_sign("Pisces", 29.99) == pytest.approx(359.99)
def test_calculate_degrees_in_varga_sign(self):
"""Test varga degree calculation."""
# D9 (Navamsa) calculation
result = calculate_degrees_in_varga_sign(15.0, 9)
assert isinstance(result, float)
assert 0 <= result < 30
def test_get_varga_sign(self):
"""Test varga sign determination."""
# D1 (Rasi) should return the same sign
assert get_varga_sign(15.0, 1) == "Aries"
assert get_varga_sign(45.0, 1) == "Taurus"
# D9 (Navamsa)
result = get_varga_sign(15.0, 9)
assert result in [
"Aries",
"Taurus",
"Gemini",
"Cancer",
"Leo",
"Virgo",
"Libra",
"Scorpio",
"Sagittarius",
"Capricorn",
"Aquarius",
"Pisces",
]
# ============================================================================
# STHANABALA TESTS
# ============================================================================
class TestSthanabala:
"""Test Sthanabala (Positional Strength) calculations."""
def test_compute_uchhabala(self, mock_chart):
"""Test Uchhabala (Exaltation Strength) calculation."""
compute_uchhabala(mock_chart)
# Check that Uchhabala was calculated for all planets
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Sthanabala" in planet.shadbala
assert "Uchhabala" in planet.shadbala["Sthanabala"]
assert isinstance(planet.shadbala["Sthanabala"]["Uchhabala"], float)
assert 0 <= planet.shadbala["Sthanabala"]["Uchhabala"] <= 60
def test_get_planetary_dignity_classification(self, mock_chart):
"""Test planetary dignity classification."""
# Sun in Leo (own sign)
assert get_planetary_dignity_classification("Sun", "Leo", 15.0) == "own"
# Moon in Taurus (moolatrikona)
assert (
get_planetary_dignity_classification("Moon", "Taurus", 2.0, True) == "mool"
)
# Mars in Aries with moolatrikona degrees
result = get_planetary_dignity_classification("Mars", "Aries", 10.0, True)
assert result in ["mool", "own"]
def test_compute_saptavargajabala(self, mock_chart):
"""Test Saptavargaja Bala (Seven Divisional Chart Strength)."""
compute_saptavargajabala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Sthanabala" in planet.shadbala
assert "Saptavargajabala" in planet.shadbala["Sthanabala"]
# Score should be positive (7 divisions * scores)
assert planet.shadbala["Sthanabala"]["Saptavargajabala"] > 0
def test_compute_ojhayugmarashiamsabala(self, mock_chart):
"""Test Ojhayugmarashiamsa Bala (Odd/Even Sign Strength)."""
compute_ojhayugmarashiamsabala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Sthanabala" in planet.shadbala
assert "Ojhayugmarashiamshabala" in planet.shadbala["Sthanabala"]
bala = planet.shadbala["Sthanabala"]["Ojhayugmarashiamshabala"]
# Should be 0, 15, or 30
assert bala in [0.0, 15.0, 30.0]
def test_compute_kendradhibala(self, mock_chart):
"""Test Kendradi Bala (Angular House Strength)."""
compute_kendradhibala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Sthanabala" in planet.shadbala
assert "Kendradhibala" in planet.shadbala["Sthanabala"]
bala = planet.shadbala["Sthanabala"]["Kendradhibala"]
# Should be 60, 30, 15, or 7.5
assert bala in [60.0, 30.0, 15.0, 7.5]
def test_compute_drekkanabala(self, mock_chart):
"""Test Drekkana Bala (Decanate Strength)."""
compute_drekkanabala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Sthanabala" in planet.shadbala
assert "Drekshanabala" in planet.shadbala["Sthanabala"]
bala = planet.shadbala["Sthanabala"]["Drekshanabala"]
# Should be 0 or 15
assert bala in [0.0, 15.0]
def test_compute_sthanabala_total(self, mock_chart):
"""Test total Sthanabala calculation."""
compute_sthanabala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Sthanabala" in planet.shadbala
assert "Total" in planet.shadbala["Sthanabala"]
assert planet.shadbala["Sthanabala"]["Total"] > 0
# ============================================================================
# DIGBALA TESTS
# ============================================================================
class TestDigbala:
"""Test Digbala (Directional Strength) calculations."""
def test_compute_digbala(self, mock_chart):
"""Test Digbala calculation."""
compute_digbala(mock_chart)
for planet in mock_chart.planets:
if (
planet.celestial_body
in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]
and "Digbala" in planet.shadbala
):
assert isinstance(planet.shadbala["Digbala"], float)
assert 0 <= planet.shadbala["Digbala"] <= 60
# ============================================================================
# KAALABALA TESTS
# ============================================================================
class TestKaalabala:
"""Test Kaalabala (Temporal Strength) calculations."""
@patch("jyotishganit.core.astronomical.get_sunrise_sunset")
def test_compute_nathonnatabala(self, mock_sunrise_sunset, mock_chart, mock_person):
"""Test Natonnata Bala (Day/Night Strength)."""
mock_sunrise_sunset.return_value = (6.0, 18.0)
compute_nathonnatabala(mock_chart, mock_person)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Kaalabala" in planet.shadbala
assert "Natonnatabala" in planet.shadbala["Kaalabala"]
bala = planet.shadbala["Kaalabala"]["Natonnatabala"]
assert 0 <= bala <= 60
def test_compute_pakshabala(self, mock_chart):
"""Test Paksha Bala (Lunar Phase Strength)."""
compute_pakshabala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Kaalabala" in planet.shadbala
assert "Pakshabala" in planet.shadbala["Kaalabala"]
bala = planet.shadbala["Kaalabala"]["Pakshabala"]
assert 0 <= bala <= 60
@patch("jyotishganit.core.astronomical.get_sunrise_sunset")
def test_compute_tribhagabala(self, mock_sunrise_sunset, mock_chart, mock_person):
"""Test Tribhaga Bala (Day/Night Third Division Strength)."""
mock_sunrise_sunset.return_value = (6.0, 18.0)
compute_tribhagabala(mock_chart, mock_person)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Kaalabala" in planet.shadbala
assert "Tribhagabala" in planet.shadbala["Kaalabala"]
bala = planet.shadbala["Kaalabala"]["Tribhagabala"]
assert bala in [0.0, 60.0]
@patch("jyotishganit.components.strengths.get_planet_declination")
@patch("jyotishganit.components.strengths.skyfield_time_from_datetime")
def test_compute_ayanabala(
self, mock_skyfield, mock_declination, mock_chart, mock_person
):
"""Test Ayana Bala (Declination Strength)."""
mock_skyfield.return_value = Mock()
mock_declination.return_value = 10.0 # Sample declination
compute_ayanabala(mock_chart, mock_person)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Kaalabala" in planet.shadbala
assert "Ayanabala" in planet.shadbala["Kaalabala"]
bala = planet.shadbala["Kaalabala"]["Ayanabala"]
# Sun's Ayana Bala can be up to 120
max_bala = 120 if planet.celestial_body == "Sun" else 60
assert 0 <= bala <= max_bala
# ============================================================================
# CHESHTABALA TESTS
# ============================================================================
class TestCheshtabala:
"""Test Cheshtabala (Motional Strength) calculations."""
@patch("jyotishganit.components.strengths._get_mean_longitude_from_skyfield")
@patch("jyotishganit.components.strengths.skyfield_time_from_datetime")
def test_compute_chestagbala(
self, mock_skyfield, mock_mean_long, mock_chart, mock_person
):
"""Test Cheshta Bala calculation."""
mock_skyfield.return_value = Mock()
mock_mean_long.return_value = 100.0
compute_chestagbala(mock_chart, mock_person)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Cheshtabala" in planet.shadbala
assert isinstance(planet.shadbala["Cheshtabala"], float)
assert 0 <= planet.shadbala["Cheshtabala"] <= 60
# ============================================================================
# YUDDHABALA TESTS
# ============================================================================
class TestYuddhabala:
"""Test Yuddhabala (Planetary War Strength) calculations."""
def test_compute_yuddhabala_no_war(self, mock_chart):
"""Test Yuddha Bala when planets are not in war."""
# Ensure planets are far apart - update their positions
for i, planet in enumerate(mock_chart.planets):
planet.sign_degrees = (i * 20.0) % 30 # Spread them out
planet.shadbala = {"Shadbala": {"Total": 100.0}, "Kaalabala": {}}
compute_yuddhabala(mock_chart)
# All Yuddhabala should be 0
for planet in mock_chart.planets:
if planet.celestial_body in [
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert planet.shadbala["Kaalabala"]["Yuddhabala"] == 0.0
def test_compute_yuddhabala_with_war(self, mock_chart):
"""Test Yuddha Bala when planets are in war (within 1 degree)."""
# Put Mars and Jupiter very close
mars = next(p for p in mock_chart.planets if p.celestial_body == "Mars")
jupiter = next(p for p in mock_chart.planets if p.celestial_body == "Jupiter")
mars.sign = "Aries"
mars.sign_degrees = 10.0
jupiter.sign = "Aries"
jupiter.sign_degrees = 10.5 # Within 1 degree
mars.shadbala = {"Shadbala": {"Total": 150.0}, "Kaalabala": {}}
jupiter.shadbala = {"Shadbala": {"Total": 100.0}, "Kaalabala": {}}
compute_yuddhabala(mock_chart)
# Mars should win, Jupiter should lose
assert mars.shadbala["Kaalabala"]["Yuddhabala"] > 0
assert jupiter.shadbala["Kaalabala"]["Yuddhabala"] < 0
# ============================================================================
# NAISARGIKABALA TESTS
# ============================================================================
class TestNaisargikabala:
"""Test Naisargika Bala (Natural Strength) calculations."""
def test_compute_naisargikabala(self, mock_chart):
"""Test Naisargika Bala calculation."""
compute_naisargikabala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Naisargikabala" in planet.shadbala
assert isinstance(planet.shadbala["Naisargikabala"], (int, float))
assert planet.shadbala["Naisargikabala"] > 0
# ============================================================================
# DRIKBALA TESTS
# ============================================================================
class TestDrikbala:
"""Test Drikbala (Aspectual Strength) calculations."""
def test_get_angular_distance_between_planets(self, mock_chart):
"""Test angular distance calculation between planets."""
dist = get_angular_distance_between_planets(mock_chart, "Sun", "Moon")
assert isinstance(dist, float)
assert -180 <= dist <= 180
def test_get_sputa_drishti_degree(self):
"""Test Sputa Drishti calculation."""
# 7th aspect (180°) should be strongest (60)
assert get_sputa_drishti_degree(180, "Sun") == pytest.approx(60, abs=1)
# No aspect at 0° or 360°
assert get_sputa_drishti_degree(0, "Sun") == pytest.approx(0, abs=1)
# Mars special aspects at 90° and 150°
mars_90 = get_sputa_drishti_degree(90, "Mars")
assert mars_90 >= 45 # Should be strong
def test_compute_drikbala(self, mock_chart):
"""Test Drik Bala calculation."""
compute_drikbala(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Drikbala" in planet.shadbala
assert isinstance(planet.shadbala["Drikbala"], float)
# ============================================================================
# SHADBALA TESTS
# ============================================================================
class TestShadbala:
"""Test complete Shadbala calculations."""
@patch("jyotishganit.components.strengths.skyfield_time_from_datetime")
@patch("jyotishganit.components.strengths.get_planet_declination")
@patch("jyotishganit.core.astronomical.get_sunrise_sunset")
def test_compute_shadbala(
self, mock_sunrise, mock_decl, mock_skyfield, mock_chart, mock_person
):
"""Test complete Shadbala calculation."""
mock_sunrise.return_value = (6.0, 18.0)
mock_decl.return_value = 10.0
# Mock time object with tt attribute (Julian date)
mock_time = Mock()
mock_time.tt = 2451545.0 # J2000.0 epoch
mock_skyfield.return_value = mock_time
compute_shadbala(mock_chart, mock_person)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Shadbala" in planet.shadbala
assert "Total" in planet.shadbala["Shadbala"]
assert "Rupas" in planet.shadbala["Shadbala"]
assert planet.shadbala["Shadbala"]["Total"] > 0
assert planet.shadbala["Shadbala"]["Rupas"] > 0
# ============================================================================
# VIMSHOPAKA BALA TESTS
# ============================================================================
class TestVimshopakaБала:
"""Test Vimshopaka Bala calculations."""
def test_compute_vimshopaka_balas(self, mock_chart):
"""Test Vimshopaka Bala calculation."""
compute_vimshopaka_balas(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Vimshopaka" in planet.shadbala
assert isinstance(planet.shadbala["Vimshopaka"], dict)
assert len(planet.shadbala["Vimshopaka"]) > 0
# ============================================================================
# ISHTA/KASHTA BALA TESTS
# ============================================================================
class TestIshtaKashtaBala:
"""Test Ishta and Kashta Bala calculations."""
def test_compute_ishtakashtabalas(self, mock_chart):
"""Test Ishta and Kashta Bala calculation."""
# Setup required prerequisites
for planet in mock_chart.planets:
planet.shadbala = {"Sthanabala": {"Uchhabala": 30.0}, "Cheshtabala": 40.0}
compute_ishtakashtabalas(mock_chart)
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Ishtabala" in planet.shadbala
assert "Kashtabala" in planet.shadbala
assert planet.shadbala["Ishtabala"] + planet.shadbala[
"Kashtabala"
] == pytest.approx(60, abs=0.1)
# ============================================================================
# BHAVA BALA TESTS
# ============================================================================
class TestBhavaBala:
"""Test Bhava Bala (House Strength) calculations."""
def test_compute_bhava_adhipathi_bala(self, mock_chart):
"""Test Bhava Adhipathi Bala (House Lord Strength)."""
# Setup Shadbala for lords
for planet in mock_chart.planets:
planet.shadbala = {"Shadbala": {"Total": 100.0}}
compute_bhava_adhipathi_bala(mock_chart)
for house in mock_chart.houses:
assert hasattr(house, "bhava_bala_adhipathi")
assert house.bhava_bala_adhipathi >= 0
def test_compute_bhava_dig_bala(self, mock_chart):
"""Test Bhava Dig Bala (House Directional Strength)."""
compute_bhava_dig_bala(mock_chart)
for house in mock_chart.houses:
assert hasattr(house, "bhava_dig_bala")
assert isinstance(house.bhava_dig_bala, float)
assert house.bhava_dig_bala >= 0
@patch("jyotishganit.components.strengths.planet_longitude_from_sign")
def test_compute_bhava_drik_bala(self, mock_long, mock_chart):
"""Test Bhava Drik Bala (House Aspectual Strength)."""
mock_long.return_value = 15.0
# Setup houses with sign_degrees
for house in mock_chart.houses:
house.sign_degrees = 0.0
compute_bhava_drik_bala(mock_chart)
for house in mock_chart.houses:
assert hasattr(house, "bhava_drik_bala")
assert isinstance(house.bhava_drik_bala, float)
# ============================================================================
# INTEGRATION TESTS
# ============================================================================
class TestIntegration:
"""Integration tests for complete calculations."""
@patch("jyotishganit.components.strengths.skyfield_time_from_datetime")
@patch("jyotishganit.components.strengths.get_planet_declination")
@patch("jyotishganit.core.astronomical.get_sunrise_sunset")
def test_calculate_all_strengths(
self, mock_sunrise, mock_decl, mock_skyfield, mock_chart, mock_person
):
"""Test complete strength calculation pipeline."""
mock_sunrise.return_value = (6.0, 18.0)
mock_decl.return_value = 10.0
# Mock time object with tt attribute (Julian date)
mock_time = Mock()
mock_time.tt = 2451545.0 # J2000.0 epoch
mock_skyfield.return_value = mock_time
result = calculate_all_strengths(mock_chart, mock_person)
assert result == mock_chart
# Verify all planets have shadbala calculated
for planet in mock_chart.planets:
if planet.celestial_body in [
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
]:
assert "Shadbala" in planet.shadbala
assert "Vimshopaka" in planet.shadbala
assert "Ishtabala" in planet.shadbala
assert "Kashtabala" in planet.shadbala
# Verify houses have bhava bala calculated
for house in mock_chart.houses:
assert hasattr(house, "bhava_bala")
# ============================================================================
# EDGE CASE TESTS
# ============================================================================
class TestEdgeCases:
"""Test edge cases and boundary conditions."""
def test_normalize_large_angles(self):
"""Test normalize with very large angles."""
assert normalize(1000) == normalize(1000 % 360)
assert normalize(-1000) == normalize(-1000 % 360)
def test_angdiff_exact_opposition(self):
"""Test angular difference at exact opposition."""
assert angdiff(0, 180) == 180
assert angdiff(180, 0) == 180
def test_planet_at_sign_boundary(self):
"""Test calculations when planet is at sign boundary."""
long = planet_longitude_from_sign("Aries", 29.99)
assert 29.99 <= long < 30
def test_varga_sign_at_boundaries(self):
"""Test varga sign calculation at boundaries."""
# Test at 0 degrees
assert get_varga_sign(0.0, 1) == "Aries"
# Test at 359.99 degrees
assert get_varga_sign(359.99, 1) == "Pisces"
def test_empty_chart(self):
"""Test handling of chart with no planets."""
empty_chart = Mock()
empty_chart.planets = []
empty_chart.houses = []
# Should not raise errors
compute_naisargikabala(empty_chart)
compute_vimshopaka_balas(empty_chart)
if __name__ == "__main__":
pytest.main([__file__, "-v", "--tb=short"])