Initial commit
This commit is contained in:
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
33
tests/test_body.py
Normal file
33
tests/test_body.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from planetarytime import Body
|
||||
|
||||
|
||||
def test_mars_hours_per_sol() -> None:
|
||||
assert Body.MARS.hours_per_sol == 25
|
||||
|
||||
|
||||
def test_jupiter_hours_per_sol() -> None:
|
||||
assert Body.JUPITER.hours_per_sol == 10
|
||||
|
||||
|
||||
def test_hours_per_sol_equals_rounded_rotation() -> None:
|
||||
for body in Body:
|
||||
assert body.hours_per_sol == round(body.rotation_hours)
|
||||
|
||||
|
||||
def test_all_bodies_have_positive_rotation() -> None:
|
||||
for body in Body:
|
||||
assert body.rotation_hours > 0
|
||||
|
||||
|
||||
def test_mars_sols_per_year() -> None:
|
||||
assert Body.MARS.sols_per_year == 670
|
||||
|
||||
|
||||
def test_all_bodies_have_positive_sols_per_year() -> None:
|
||||
for body in Body:
|
||||
assert body.sols_per_year > 0
|
||||
|
||||
|
||||
def test_sols_per_year_derived_from_orbital_and_rotation() -> None:
|
||||
for body in Body:
|
||||
assert body.sols_per_year == round(body.orbital_hours / body.rotation_hours)
|
||||
114
tests/test_moon.py
Normal file
114
tests/test_moon.py
Normal file
@@ -0,0 +1,114 @@
|
||||
import pytest
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from planetarytime import Body, EpochType, Moon, PlanetaryTime
|
||||
from planetarytime.exceptions import EpochUnavailableError
|
||||
from planetarytime.epoch import get_epoch_date
|
||||
|
||||
|
||||
# ── Moon dataclass ─────────────────────────────────────────────────────────────
|
||||
|
||||
def test_body_getitem_returns_moon() -> None:
|
||||
assert isinstance(Body.MARS[0], Moon)
|
||||
|
||||
|
||||
def test_mars_first_moon_is_phobos() -> None:
|
||||
assert Body.MARS[0].name == "Phobos"
|
||||
|
||||
|
||||
def test_mars_second_moon_is_deimos() -> None:
|
||||
assert Body.MARS[1].name == "Deimos"
|
||||
|
||||
|
||||
def test_mars_index_out_of_range_raises() -> None:
|
||||
with pytest.raises(IndexError):
|
||||
Body.MARS[99]
|
||||
|
||||
|
||||
def test_mercury_has_no_moons() -> None:
|
||||
with pytest.raises(IndexError):
|
||||
Body.MERCURY[0]
|
||||
|
||||
|
||||
def test_tidally_locked_sols_per_year_is_one() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
assert phobos.is_tidally_locked is True
|
||||
assert phobos.sols_per_year == 1
|
||||
|
||||
|
||||
def test_moon_hours_per_sol_equals_rounded_rotation() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
assert phobos.hours_per_sol == round(phobos.rotation_hours)
|
||||
|
||||
|
||||
def test_titan_has_contact_date() -> None:
|
||||
titan = Body.SATURN[0]
|
||||
assert titan.name == "Titan"
|
||||
assert titan.contact_date is not None
|
||||
|
||||
|
||||
def test_display_name_returns_moon_name() -> None:
|
||||
assert Body.MARS[0].display_name == "Phobos"
|
||||
|
||||
|
||||
# ── Epoch ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
def test_moon_discovery_epoch_date() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
assert get_epoch_date(phobos, EpochType.DISCOVERY) == datetime(1877, 8, 18, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_moon_contact_epoch_date() -> None:
|
||||
titan = Body.SATURN[0]
|
||||
assert get_epoch_date(titan, EpochType.CONTACT) == datetime(2005, 1, 14, tzinfo=timezone.utc)
|
||||
|
||||
|
||||
def test_moon_contact_epoch_unavailable_raises() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
with pytest.raises(EpochUnavailableError):
|
||||
get_epoch_date(phobos, EpochType.CONTACT)
|
||||
|
||||
|
||||
# ── PlanetaryTime with Moon ────────────────────────────────────────────────────
|
||||
|
||||
def test_planetary_time_from_earth_with_moon() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
epoch_dt = get_epoch_date(phobos, EpochType.DISCOVERY)
|
||||
pt = PlanetaryTime.from_earth(epoch_dt, phobos, EpochType.DISCOVERY)
|
||||
assert pt.year == 0
|
||||
assert pt.sol == 0
|
||||
assert pt.hour == 0
|
||||
|
||||
|
||||
def test_planetary_time_moon_one_sol_later_tidally_locked() -> None:
|
||||
# Phobos is tidally locked: sols_per_year == 1, so after 1 sol the year rolls over.
|
||||
phobos = Body.MARS[0]
|
||||
assert phobos.sols_per_year == 1
|
||||
epoch_dt = get_epoch_date(phobos, EpochType.DISCOVERY)
|
||||
one_sol_later = epoch_dt + timedelta(hours=phobos.hours_per_sol)
|
||||
pt = PlanetaryTime.from_earth(one_sol_later, phobos, EpochType.DISCOVERY)
|
||||
assert pt.year == 1
|
||||
assert pt.sol == 0
|
||||
|
||||
|
||||
def test_planetary_time_moon_one_year_later() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
epoch_dt = get_epoch_date(phobos, EpochType.DISCOVERY)
|
||||
one_year_seconds = phobos.sols_per_year * phobos.hours_per_sol * 3600
|
||||
pt = PlanetaryTime.from_earth(epoch_dt + timedelta(seconds=one_year_seconds), phobos, EpochType.DISCOVERY)
|
||||
assert pt.year == 1
|
||||
assert pt.sol == 0
|
||||
|
||||
|
||||
def test_str_contains_moon_name() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
epoch_dt = get_epoch_date(phobos, EpochType.DISCOVERY)
|
||||
pt = PlanetaryTime.from_earth(epoch_dt, phobos, EpochType.DISCOVERY)
|
||||
assert "Phobos" in str(pt)
|
||||
|
||||
|
||||
def test_repr_contains_moon_name() -> None:
|
||||
phobos = Body.MARS[0]
|
||||
epoch_dt = get_epoch_date(phobos, EpochType.DISCOVERY)
|
||||
pt = PlanetaryTime.from_earth(epoch_dt, phobos, EpochType.DISCOVERY)
|
||||
assert "Phobos" in repr(pt)
|
||||
82
tests/test_planetary_time.py
Normal file
82
tests/test_planetary_time.py
Normal file
@@ -0,0 +1,82 @@
|
||||
import pytest
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
from planetarytime import Body, EpochType, PlanetaryTime
|
||||
from planetarytime.exceptions import DatetimePrecedesEpochError, EpochUnavailableError
|
||||
from planetarytime.epoch import get_epoch_date
|
||||
|
||||
|
||||
def test_from_earth_at_epoch_is_year_zero_sol_zero() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
pt = PlanetaryTime.from_earth(epoch_dt, Body.MARS, EpochType.DISCOVERY)
|
||||
assert pt.year == 0
|
||||
assert pt.sol == 0
|
||||
assert pt.hour == 0
|
||||
assert pt.minute == 0
|
||||
assert pt.second == 0
|
||||
|
||||
|
||||
def test_from_earth_one_sol_later() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
one_sol_later = epoch_dt + timedelta(hours=Body.MARS.hours_per_sol)
|
||||
pt = PlanetaryTime.from_earth(one_sol_later, Body.MARS, EpochType.DISCOVERY)
|
||||
assert pt.year == 0
|
||||
assert pt.sol == 1
|
||||
assert pt.hour == 0
|
||||
|
||||
|
||||
def test_from_earth_one_year_later() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
one_year_seconds = Body.MARS.sols_per_year * Body.MARS.hours_per_sol * 3600
|
||||
one_year_later = epoch_dt + timedelta(seconds=one_year_seconds)
|
||||
pt = PlanetaryTime.from_earth(one_year_later, Body.MARS, EpochType.DISCOVERY)
|
||||
assert pt.year == 1
|
||||
assert pt.sol == 0
|
||||
assert pt.hour == 0
|
||||
|
||||
|
||||
def test_from_earth_one_hour_later() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
one_hour_later = epoch_dt + timedelta(hours=1)
|
||||
pt = PlanetaryTime.from_earth(one_hour_later, Body.MARS, EpochType.DISCOVERY)
|
||||
assert pt.year == 0
|
||||
assert pt.sol == 0
|
||||
assert pt.hour == 1
|
||||
assert pt.minute == 0
|
||||
|
||||
|
||||
def test_from_earth_naive_datetime_treated_as_utc() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
naive = epoch_dt.replace(tzinfo=None)
|
||||
pt = PlanetaryTime.from_earth(naive, Body.MARS, EpochType.DISCOVERY)
|
||||
assert pt.year == 0
|
||||
assert pt.sol == 0
|
||||
|
||||
|
||||
def test_from_earth_before_epoch_raises() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
before_epoch = epoch_dt - timedelta(days=1)
|
||||
with pytest.raises(DatetimePrecedesEpochError):
|
||||
PlanetaryTime.from_earth(before_epoch, Body.MARS, EpochType.DISCOVERY)
|
||||
|
||||
|
||||
def test_contact_epoch_unavailable_raises() -> None:
|
||||
with pytest.raises(EpochUnavailableError):
|
||||
PlanetaryTime.from_earth(datetime(2024, 1, 1, tzinfo=timezone.utc), Body.JUPITER, EpochType.CONTACT)
|
||||
|
||||
|
||||
def test_str_contains_body_name_and_sol() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
pt = PlanetaryTime.from_earth(epoch_dt, Body.MARS, EpochType.DISCOVERY)
|
||||
assert "Mars" in str(pt)
|
||||
assert "Sol" in str(pt)
|
||||
assert "Year" in str(pt)
|
||||
|
||||
|
||||
def test_repr_contains_year_and_sol() -> None:
|
||||
epoch_dt = get_epoch_date(Body.MARS, EpochType.DISCOVERY)
|
||||
pt = PlanetaryTime.from_earth(epoch_dt, Body.MARS, EpochType.DISCOVERY)
|
||||
r = repr(pt)
|
||||
assert "PlanetaryTime(" in r
|
||||
assert "year=" in r
|
||||
assert "sol=" in r
|
||||
Reference in New Issue
Block a user