""" Unit tests for the populate_ephemeris management command. pyswisseph calls are mocked — these tests verify date iteration, snapshot persistence, and idempotency without touching the ephemeris. Run: pyswiss/.venv/Scripts/python pyswiss/manage.py test pyswiss/apps/charts """ from datetime import datetime, timezone from unittest.mock import patch from django.core.management import call_command from django.test import TestCase from apps.charts.models import EphemerisSnapshot # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- # 10 planets covering Fire×3, Earth×3, Air×2, Water×2 (one per sign) # Expected: fire=3, water=2, earth=3, air=2, time=0, space=9 FAKE_PLANETS = { 'Sun': {'sign': 'Aries', 'degree': 10.0, 'retrograde': False}, 'Moon': {'sign': 'Leo', 'degree': 130.0, 'retrograde': False}, 'Mercury': {'sign': 'Sagittarius', 'degree': 250.0, 'retrograde': False}, 'Venus': {'sign': 'Taurus', 'degree': 40.0, 'retrograde': False}, 'Mars': {'sign': 'Virgo', 'degree': 160.0, 'retrograde': False}, 'Jupiter': {'sign': 'Capricorn', 'degree': 280.0, 'retrograde': False}, 'Saturn': {'sign': 'Gemini', 'degree': 70.0, 'retrograde': False}, 'Uranus': {'sign': 'Aquarius', 'degree': 310.0, 'retrograde': False}, 'Neptune': {'sign': 'Cancer', 'degree': 100.0, 'retrograde': False}, 'Pluto': {'sign': 'Pisces', 'degree': 340.0, 'retrograde': False}, } PATCH_TARGET = ( 'apps.charts.management.commands.populate_ephemeris.get_planet_positions' ) # --------------------------------------------------------------------------- # Tests # --------------------------------------------------------------------------- class PopulateEphemerisCommandTest(TestCase): def _run(self, date_from, date_to): with patch(PATCH_TARGET, return_value=FAKE_PLANETS): call_command('populate_ephemeris', date_from=date_from, date_to=date_to, verbosity=0) # ── date iteration ──────────────────────────────────────────────────── def test_creates_one_snapshot_per_day(self): self._run('2000-01-01', '2000-01-03') self.assertEqual(EphemerisSnapshot.objects.count(), 3) def test_single_day_range_creates_one_snapshot(self): self._run('2000-01-01', '2000-01-01') self.assertEqual(EphemerisSnapshot.objects.count(), 1) def test_snapshots_are_at_noon_utc(self): self._run('2000-01-01', '2000-01-01') snap = EphemerisSnapshot.objects.get() self.assertEqual(snap.dt, datetime(2000, 1, 1, 12, 0, 0, tzinfo=timezone.utc)) # ── idempotency ─────────────────────────────────────────────────────── def test_rerunning_does_not_create_duplicates(self): self._run('2000-01-01', '2000-01-03') self._run('2000-01-01', '2000-01-03') self.assertEqual(EphemerisSnapshot.objects.count(), 3) def test_overlapping_ranges_do_not_duplicate(self): self._run('2000-01-01', '2000-01-03') self._run('2000-01-02', '2000-01-05') self.assertEqual(EphemerisSnapshot.objects.count(), 5) # ── element counts ──────────────────────────────────────────────────── def test_element_counts_are_persisted(self): self._run('2000-01-01', '2000-01-01') snap = EphemerisSnapshot.objects.get() self.assertEqual(snap.fire, 3) self.assertEqual(snap.water, 2) self.assertEqual(snap.earth, 3) self.assertEqual(snap.air, 2) self.assertEqual(snap.time_el, 0) self.assertEqual(snap.space_el, 9) # ── chart_data payload ──────────────────────────────────────────────── def test_chart_data_contains_planets(self): self._run('2000-01-01', '2000-01-01') snap = EphemerisSnapshot.objects.get() self.assertEqual(snap.chart_data['planets'], FAKE_PLANETS)