2026-09-18 09:09:32 -04:00
|
|
|
"""Pinned against published calendars and against dates whose answer is not
|
|
|
|
|
open to interpretation."""
|
|
|
|
|
|
|
|
|
|
import datetime as dt
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from typikon import describe, gregorian_pascha, julian_pascha, resolve
|
|
|
|
|
from typikon.calendars import from_julian, julian_offset, to_julian
|
2026-09-18 10:29:25 -04:00
|
|
|
from typikon.day import week, year_of
|
2026-09-18 09:09:32 -04:00
|
|
|
from typikon.export import to_ics, to_json
|
|
|
|
|
from typikon.fasting import (DAIRY, FISH, NONE, OBLIGATORY, STRICT, WINE_OIL,
|
2026-09-18 10:29:25 -04:00
|
|
|
XEROPHAGY, stricter)
|
2026-09-18 09:09:32 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ───────────────────────────── the paschalion ─────────────────────────────
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("year,expected", [
|
|
|
|
|
(2024, "2024-05-05"), (2025, "2025-04-20"), (2026, "2026-04-12"),
|
|
|
|
|
(2027, "2027-05-02"), (2028, "2028-04-16"), (2030, "2030-04-28"),
|
|
|
|
|
])
|
|
|
|
|
def test_julian_pascha(year, expected):
|
|
|
|
|
assert julian_pascha(year).isoformat() == expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("year,expected", [
|
|
|
|
|
(2024, "2024-03-31"), (2025, "2025-04-20"), (2026, "2026-04-05"),
|
|
|
|
|
(2027, "2027-03-28"), (2028, "2028-04-16"),
|
|
|
|
|
])
|
|
|
|
|
def test_gregorian_pascha(year, expected):
|
|
|
|
|
assert gregorian_pascha(year).isoformat() == expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_two_paschalions_agree_in_2025_and_differ_in_2026():
|
|
|
|
|
assert julian_pascha(2025) == gregorian_pascha(2025)
|
|
|
|
|
assert (julian_pascha(2026) - gregorian_pascha(2026)).days == 7
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ──────────────────────────── calendar drift ──────────────────────────────
|
|
|
|
|
|
|
|
|
|
def test_julian_lags_thirteen_days_now_and_fourteen_after_2100():
|
|
|
|
|
assert julian_offset(dt.date(2026, 9, 18)) == 13
|
|
|
|
|
assert to_julian(dt.date(2026, 9, 18)) == (2026, 9, 5)
|
|
|
|
|
# Julian 2100 is a leap year and Gregorian 2100 is not, so the gap
|
|
|
|
|
# widens the day after the Julian 29 February — 15 March by the civil
|
|
|
|
|
# calendar. Nothing here hardcodes it.
|
|
|
|
|
assert julian_offset(dt.date(2100, 3, 14)) == 13
|
|
|
|
|
assert julian_offset(dt.date(2100, 3, 15)) == 14
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_old_calendar_nativity_falls_on_7_january():
|
|
|
|
|
assert from_julian(2026, 12, 25) == dt.date(2027, 1, 7)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_old_and_new_disagree_about_the_day_not_the_date():
|
|
|
|
|
old = describe(dt.date(2027, 1, 7), "rocor")
|
|
|
|
|
new = describe(dt.date(2026, 12, 25), "goarch")
|
|
|
|
|
assert old.church_date == new.church_date == "25 December"
|
|
|
|
|
assert old.feast == new.feast
|
|
|
|
|
assert old.rank == new.rank == "great"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ───────────────────────── the Orthodox fast ──────────────────────────────
|
|
|
|
|
|
|
|
|
|
def test_great_fast_weekdays_are_xerophagy_and_weekends_wine_and_oil():
|
|
|
|
|
# 2026: Orthodox Pascha 12 April, so Clean Monday is 23 February.
|
|
|
|
|
assert describe(dt.date(2026, 2, 25), "new").fast == XEROPHAGY # Wednesday
|
|
|
|
|
assert describe(dt.date(2026, 2, 28), "new").fast == WINE_OIL # Saturday
|
|
|
|
|
assert describe(dt.date(2026, 3, 1), "new").fast == WINE_OIL # Sunday
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_two_strictest_days_of_the_year():
|
|
|
|
|
assert describe(dt.date(2026, 2, 23), "new").fast == STRICT # Clean Monday
|
|
|
|
|
assert describe(dt.date(2026, 4, 10), "new").fast == STRICT # Great Friday
|
|
|
|
|
# The one Saturday kept without oil.
|
|
|
|
|
assert describe(dt.date(2026, 4, 11), "new").fast == XEROPHAGY
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_annunciation_and_palm_sunday_admit_fish_inside_the_great_fast():
|
|
|
|
|
ann = describe(dt.date(2026, 3, 25), "new")
|
|
|
|
|
assert ann.season == "Great Fast" and ann.fast == FISH
|
|
|
|
|
assert describe(dt.date(2026, 4, 5), "new").fast == FISH # Palm Sunday
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_cheesefare_week_keeps_dairy_on_wednesday_and_friday():
|
|
|
|
|
d = describe(dt.date(2026, 2, 18), "new") # Wednesday
|
|
|
|
|
assert d.season == "Cheesefare Week" and d.fast == DAIRY
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_fast_free_stretches():
|
|
|
|
|
assert describe(dt.date(2026, 4, 17), "new").fast == NONE # Bright Friday
|
|
|
|
|
assert describe(dt.date(2026, 1, 2), "new").fast == NONE # Christmastide
|
|
|
|
|
assert describe(dt.date(2026, 6, 3), "new").fast == NONE # week after Pentecost
|
|
|
|
|
# The week after the Publican and the Pharisee, kept fast-free precisely
|
|
|
|
|
# because the Pharisee boasted of fasting twice a week.
|
|
|
|
|
assert describe(dt.date(2026, 2, 6), "new").fast == NONE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_nativity_fast_tightens_for_its_last_five_days():
|
|
|
|
|
# A Saturday before 20 December admits fish; one after it does not.
|
|
|
|
|
assert describe(dt.date(2026, 12, 12), "new").fast == FISH
|
|
|
|
|
assert describe(dt.date(2026, 12, 26), "new").season != "Nativity Fast"
|
|
|
|
|
late = describe(dt.date(2026, 12, 19), "new")
|
|
|
|
|
assert late.fast == FISH
|
|
|
|
|
assert describe(dt.date(2026, 12, 22), "new").fast == WINE_OIL # Tuesday
|
|
|
|
|
assert describe(dt.date(2026, 12, 24), "new").fast == XEROPHAGY # the eve
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_great_feast_lifts_an_ordinary_friday_to_fish():
|
|
|
|
|
d = describe(dt.date(2026, 8, 6), "goarch") # Transfiguration
|
|
|
|
|
assert d.weekday == "Thursday" and d.fast == FISH
|
|
|
|
|
# 2026-01-01, the Circumcision, is a Thursday; take a great feast on a
|
|
|
|
|
# Wednesday instead — the Meeting of our Lord, 2 February 2028.
|
|
|
|
|
m = describe(dt.date(2028, 2, 2), "goarch")
|
|
|
|
|
assert m.weekday == "Wednesday" and m.rank == "great" and m.fast == FISH
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ordinary_wednesday_and_friday_are_xerophagy():
|
|
|
|
|
d = describe(dt.date(2026, 9, 4), "new") # a Friday
|
|
|
|
|
assert d.season is None and d.rank is None and d.fast == XEROPHAGY
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_polyeleos_saint_lifts_an_ordinary_friday_to_wine_and_oil():
|
|
|
|
|
# 9 October 2026 is a Friday and the Apostle James son of Alphaeus. The
|
|
|
|
|
# Typikon gives wine and oil for a saint of this rank, which is why the
|
|
|
|
|
# published calendars mark so many Fridays that way.
|
|
|
|
|
d = describe(dt.date(2026, 10, 9), "new")
|
|
|
|
|
assert d.weekday == "Friday" and d.rank == "polyeleos"
|
|
|
|
|
assert d.fast == WINE_OIL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_orthodox_rule_never_reports_an_obligation():
|
|
|
|
|
days = week(dt.date(2026, 1, 1), 365, "new")
|
|
|
|
|
assert not any(d.obligation == OBLIGATORY for d in days)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ──────────────────── Byzantine Catholic particular law ───────────────────
|
|
|
|
|
|
|
|
|
|
def test_ruthenian_pascha_is_the_gregorian_one():
|
|
|
|
|
assert describe(dt.date(2026, 6, 1), "ruthenian").pascha == dt.date(2026, 4, 5)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ruthenian_clean_monday_is_16_february_2026():
|
|
|
|
|
# As published by the Metropolitan Cantor Institute.
|
|
|
|
|
assert describe(dt.date(2026, 2, 16), "ruthenian").reason.startswith(
|
|
|
|
|
"the first day")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_only_four_kinds_of_day_bind_under_ruthenian_law():
|
|
|
|
|
obligatory = [d for d in week(dt.date(2026, 1, 1), 365, "ruthenian")
|
|
|
|
|
if d.obligation == OBLIGATORY]
|
|
|
|
|
# Clean Monday, Great Friday, and the Wednesdays and Fridays of the Fast.
|
|
|
|
|
assert dt.date(2026, 2, 16) in [d.date for d in obligatory]
|
|
|
|
|
assert dt.date(2026, 4, 3) in [d.date for d in obligatory]
|
|
|
|
|
assert all(d.season in ("Great Fast", "Great and Holy Week")
|
|
|
|
|
for d in obligatory)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_voluntary_seasons_never_report_as_binding():
|
|
|
|
|
for when in (dt.date(2026, 11, 20), dt.date(2026, 8, 5)):
|
|
|
|
|
assert describe(when, "ruthenian").obligation == "customary"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_christmas_on_a_friday_does_not_ask_you_to_abstain():
|
|
|
|
|
d = describe(dt.date(2026, 12, 25), "ruthenian")
|
|
|
|
|
assert d.weekday == "Friday" and d.fast == NONE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ruthenian_strict_days_bind_strictly_and_the_rest_bind_to_meat():
|
|
|
|
|
assert describe(dt.date(2026, 2, 16), "ruthenian").binds == WINE_OIL
|
|
|
|
|
assert describe(dt.date(2026, 4, 3), "ruthenian").binds == WINE_OIL
|
|
|
|
|
assert describe(dt.date(2026, 2, 18), "ruthenian").binds == DAIRY
|
|
|
|
|
assert describe(dt.date(2026, 10, 9), "ruthenian").binds == NONE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ──────────────────────────── the menologion ──────────────────────────────
|
|
|
|
|
|
|
|
|
|
def test_every_day_of_a_leap_year_has_a_commemoration():
|
|
|
|
|
for d in week(dt.date(2024, 1, 1), 366, "new"):
|
|
|
|
|
assert d.commemorations, d.date
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_twelve_great_feasts_are_all_present():
|
|
|
|
|
fixed = [(9, 8), (9, 14), (11, 21), (12, 25), (1, 6), (2, 2), (3, 25),
|
|
|
|
|
(8, 6), (8, 15)]
|
|
|
|
|
for month, day in fixed:
|
|
|
|
|
d = describe(dt.date(2026, month, day), "goarch")
|
|
|
|
|
assert d.rank == "great", (month, day)
|
|
|
|
|
for when in (dt.date(2026, 4, 5), dt.date(2026, 5, 21), dt.date(2026, 5, 31)):
|
|
|
|
|
assert describe(when, "goarch").rank == "great", when
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_movable_feast_outranks_the_fixed_calendar():
|
|
|
|
|
d = describe(dt.date(2026, 4, 12), "new")
|
|
|
|
|
assert "Pascha" in d.feast
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ──────────────────────────────── output ──────────────────────────────────
|
|
|
|
|
|
|
|
|
|
def test_json_round_trips():
|
|
|
|
|
import json
|
|
|
|
|
days = week(dt.date(2026, 4, 10), 3, "new")
|
|
|
|
|
parsed = json.loads(to_json(days))
|
|
|
|
|
assert len(parsed["days"]) == 3
|
|
|
|
|
assert parsed["days"][0]["fast"] == STRICT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ics_is_well_formed_and_folds_long_lines():
|
|
|
|
|
text = to_ics(week(dt.date(2026, 4, 10), 7, "new"))
|
|
|
|
|
assert text.startswith("BEGIN:VCALENDAR")
|
|
|
|
|
assert text.rstrip().endswith("END:VCALENDAR")
|
|
|
|
|
assert text.count("BEGIN:VEVENT") == text.count("END:VEVENT")
|
|
|
|
|
for line in text.split("\r\n"):
|
|
|
|
|
assert len(line.encode()) < 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────────────── every jurisdiction ───────────────────────────
|
|
|
|
|
|
|
|
|
|
@pytest.mark.parametrize("key", list(__import__("typikon").JURISDICTIONS))
|
|
|
|
|
def test_every_jurisdiction_reckons_a_whole_year_without_raising(key):
|
|
|
|
|
days = week(dt.date(2026, 1, 1), 365, key)
|
|
|
|
|
assert len(days) == 365
|
|
|
|
|
assert all(d.fast for d in days)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_calendar_name_resolves_without_naming_a_church():
|
|
|
|
|
assert resolve("old").menologion == "julian"
|
|
|
|
|
assert resolve("new").menologion == "revised_julian"
|
|
|
|
|
assert resolve("western").paschalion == "gregorian"
|
|
|
|
|
assert resolve(None).key == "new"
|
2026-09-18 10:29:25 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
# ─────────────────── regressions, each from a real defect ──────────────────
|
|
|
|
|
|
|
|
|
|
def test_the_annunciation_is_never_lost_to_great_friday():
|
|
|
|
|
"""It can fall on Great Friday or Great Saturday, and when it does the
|
|
|
|
|
feast lifts the total fast rather than disappearing into it."""
|
|
|
|
|
for when, jurisdiction in ((dt.date(2016, 3, 25), "western"),
|
|
|
|
|
(dt.date(2005, 3, 25), "western"),
|
|
|
|
|
(dt.date(2029, 4, 7), "rocor"),
|
|
|
|
|
(dt.date(2018, 4, 7), "rocor")):
|
|
|
|
|
d = describe(when, jurisdiction)
|
|
|
|
|
assert d.church_date == "25 March", when
|
|
|
|
|
assert "Annunciation" in d.feast, when
|
|
|
|
|
assert d.rank == "great", when
|
|
|
|
|
assert d.fast == WINE_OIL, (when, d.fast)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_kyriopascha_and_the_annunciation_in_bright_week_stay_fast_free():
|
|
|
|
|
assert describe(dt.date(2035, 3, 25), "western").fast == NONE
|
|
|
|
|
assert describe(dt.date(2075, 4, 7), "rocor").fast == NONE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_palm_sunday_and_lazarus_saturday_are_still_the_fast():
|
|
|
|
|
"""Both fall between the Forty Days and Great Week. Leaving that gap
|
|
|
|
|
outside every season made the Byzantine Catholic rule report no fast at
|
|
|
|
|
all on Palm Sunday."""
|
|
|
|
|
for jurisdiction in ("ruthenian", "ugcc", "melkite", "goarch", "rocor"):
|
|
|
|
|
for year in (2025, 2026, 2027):
|
|
|
|
|
mv = year_of(year, jurisdiction)
|
|
|
|
|
for when in (mv["lazarus_saturday"], mv["palm_sunday"]):
|
|
|
|
|
d = describe(when, jurisdiction)
|
|
|
|
|
assert d.season is not None, (jurisdiction, when)
|
|
|
|
|
assert d.fast != NONE, (jurisdiction, when, d.fast)
|
|
|
|
|
assert d.reason, (jurisdiction, when)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_a_ranked_saint_relaxes_a_weekday_of_a_fasting_season():
|
|
|
|
|
"""The rank is why the printed calendars mark so many Wednesdays in the
|
|
|
|
|
Nativity Fast for wine and oil."""
|
|
|
|
|
# The Nativity of the Forerunner, a great feast inside the Apostles' Fast.
|
|
|
|
|
assert describe(dt.date(2020, 6, 24), "goarch").fast == FISH
|
|
|
|
|
# The Forty Martyrs of Sebaste, the textbook relaxation of a Lenten weekday.
|
|
|
|
|
assert describe(dt.date(2022, 3, 9), "goarch").fast == WINE_OIL
|
|
|
|
|
# St Nicholas, a vigil saint on a Wednesday of the Nativity Fast.
|
|
|
|
|
assert describe(dt.date(2023, 12, 6), "goarch").fast == WINE_OIL
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_no_saint_brings_fish_into_the_great_fast_or_lifts_great_week():
|
|
|
|
|
for d in week(dt.date(2022, 3, 7), 40, "goarch"):
|
|
|
|
|
if d.season == "Great Fast" and d.church_date != "25 March":
|
|
|
|
|
assert not stricter(FISH, d.fast) or d.fast != FISH, d.date
|
|
|
|
|
if d.season == "Great and Holy Week" and d.church_date != "25 March":
|
|
|
|
|
assert d.fast in (STRICT, XEROPHAGY, WINE_OIL), (d.date, d.fast)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_eves_relax_when_they_fall_on_a_weekend():
|
|
|
|
|
"""The Liturgy of St Basil is served with Vespers, so the eve is kept at
|
|
|
|
|
wine and oil rather than xerophagy."""
|
|
|
|
|
assert describe(dt.date(2022, 12, 24), "goarch").fast == WINE_OIL
|
|
|
|
|
assert describe(dt.date(2023, 12, 24), "goarch").fast == WINE_OIL
|
|
|
|
|
assert describe(dt.date(2025, 1, 5), "goarch").fast == WINE_OIL
|
|
|
|
|
# A weekday eve is still xerophagy.
|
|
|
|
|
assert describe(dt.date(2024, 12, 24), "goarch").fast == XEROPHAGY
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_apostles_fast_is_empty_when_pascha_is_late():
|
|
|
|
|
"""A late Pascha can leave the fast with no days at all, which is correct
|
|
|
|
|
rather than a bug -- and the old calendar never has that problem."""
|
|
|
|
|
goarch = [d for d in week(dt.date(2024, 5, 1), 120, "goarch")
|
|
|
|
|
if d.season == "Apostles' Fast"]
|
|
|
|
|
rocor = [d for d in week(dt.date(2024, 5, 1), 120, "rocor")
|
|
|
|
|
if d.season == "Apostles' Fast"]
|
|
|
|
|
assert goarch == []
|
|
|
|
|
assert len(rocor) > 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ──────────────────────── invariants, not samples ──────────────────────────
|
|
|
|
|
|
|
|
|
|
def test_the_paschalion_invariants_hold_for_two_centuries():
|
|
|
|
|
for year in range(1900, 2101):
|
|
|
|
|
j, g = julian_pascha(year), gregorian_pascha(year)
|
|
|
|
|
assert j.weekday() == 6, year
|
|
|
|
|
assert g.weekday() == 6, year
|
|
|
|
|
assert dt.date(year, 4, 4) <= j <= dt.date(year, 5, 8), (year, j)
|
|
|
|
|
assert dt.date(year, 3, 22) <= g <= dt.date(year, 4, 25), (year, g)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_every_civil_date_round_trips_through_the_julian_calendar():
|
|
|
|
|
day, last = dt.date(1800, 1, 1), dt.date(2200, 12, 31)
|
|
|
|
|
while day <= last:
|
|
|
|
|
y, m, d = to_julian(day)
|
|
|
|
|
assert from_julian(y, m, d) == day, day
|
|
|
|
|
day += dt.timedelta(1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_julian_offset_widens_the_day_after_a_julian_leap_day():
|
|
|
|
|
for before, after in ((dt.date(1900, 3, 13), dt.date(1900, 3, 14)),
|
|
|
|
|
(dt.date(2100, 3, 14), dt.date(2100, 3, 15)),
|
|
|
|
|
(dt.date(2200, 3, 15), dt.date(2200, 3, 16))):
|
|
|
|
|
assert julian_offset(after) == julian_offset(before) + 1, after
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_ics_content_lines_stay_within_seventy_five_octets():
|
|
|
|
|
for jurisdiction in ("goarch", "rocor", "ruthenian"):
|
|
|
|
|
text = to_ics(week(dt.date(2026, 1, 1), 365, jurisdiction))
|
|
|
|
|
for line in text.split("\r\n"):
|
|
|
|
|
assert len(line.encode()) <= 75, (jurisdiction, line)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_every_jurisdiction_holds_together_for_twenty_years():
|
|
|
|
|
"""The regression net. Replaces a loop that asserted only truthiness and
|
|
|
|
|
so could not fail: "none" is a truthy string."""
|
|
|
|
|
from typikon import JURISDICTIONS
|
|
|
|
|
from typikon.fasting import LEVELS
|
|
|
|
|
for key in JURISDICTIONS:
|
|
|
|
|
for d in week(dt.date(2020, 1, 1), 366 * 21 // 4, key):
|
|
|
|
|
assert d.fast in LEVELS, (key, d.date, d.fast)
|
|
|
|
|
assert d.binds in LEVELS, (key, d.date, d.binds)
|
|
|
|
|
assert d.commemorations, (key, d.date)
|
|
|
|
|
assert not stricter(d.binds, d.fast), (key, d.date)
|
|
|
|
|
assert d.title, (key, d.date)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ──────────────────────────────── the CLI ──────────────────────────────────
|
|
|
|
|
|
|
|
|
|
def test_the_cli_rejects_a_malformed_date_instead_of_traversing_a_traceback():
|
|
|
|
|
from typikon.cli import main
|
|
|
|
|
assert main(["-d", "tomorrow"]) == 2
|
|
|
|
|
assert main(["-d", "2026-02-30"]) == 2
|
|
|
|
|
assert main(["--year", "99999"]) == 2
|
|
|
|
|
assert main(["--jurisdiction", "atlantis"]) == 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_the_cli_elides_on_a_word():
|
|
|
|
|
from typikon.cli import _elide
|
|
|
|
|
assert _elide("The Radiant and Life-giving Resurrection", 24) == "The Radiant and…"
|
|
|
|
|
assert _elide("Short", 24) == "Short"
|