# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """The pope ladder, and the one fixed point the whole scheme exists for.""" from __future__ import annotations import itertools import json import tomllib from pathlib import Path import pytest from signal_bot.versioning import ( POPE, POPES, RELEASE_ORDINAL, __version__, ordinal_for, pope_for, version_for, ) REPO_ROOT = Path(__file__).resolve().parent.parent JOHN_PAUL_II = 264 def test_john_paul_ii_is_exactly_2_13_7() -> None: """The requirement the two ladder constants were solved for.""" assert version_for(JOHN_PAUL_II) == "2.13.7" assert pope_for(JOHN_PAUL_II).name == "Jan Paweł II" @pytest.mark.parametrize( ("ordinal", "version", "name"), [ (1, "0.0.0", "Święty Piotr Apostoł"), (2, "0.0.1", "Linus"), (32, "0.3.7", "Milcjades"), (33, "1.0.0", "Sylwester I"), (152, "1.14.7", "Leon IX"), (153, "2.0.0", "Wiktor II"), (265, "2.14.0", "Benedykt XVI"), (267, "2.14.2", "Leon XIV"), ], ) def test_the_anchors_and_the_boundaries_either_side(ordinal: int, version: str, name: str) -> None: assert version_for(ordinal) == version assert pope_for(ordinal).name == name def test_versions_never_go_backwards() -> None: """A ladder that is not monotonic is not a version scheme.""" ladder = [ tuple(int(part) for part in version_for(n).split(".")) for n in range(1, len(POPES) + 1) ] assert all(earlier < later for earlier, later in itertools.pairwise(ladder)) @pytest.mark.parametrize("ordinal", [0, -1, 268, 9999]) def test_an_ordinal_off_the_list_is_refused(ordinal: int) -> None: with pytest.raises(ValueError, match="no pope with ordinal"): version_for(ordinal) with pytest.raises(ValueError, match="no pope with ordinal"): pope_for(ordinal) def test_ordinal_for_is_the_inverse_of_version_for() -> None: for ordinal in (1, 2, 33, 153, JOHN_PAUL_II, len(POPES)): assert ordinal_for(version_for(ordinal)) == ordinal def test_a_version_no_release_carries_is_refused() -> None: with pytest.raises(ValueError, match="no release carries version"): ordinal_for("9.9.9") def test_this_build_names_itself() -> None: assert __version__ == version_for(RELEASE_ORDINAL) assert pope_for(RELEASE_ORDINAL) == POPE def test_pyproject_agrees_with_the_ladder() -> None: """Two version sources with nothing reconciling them would drift silently.""" pyproject = tomllib.loads((REPO_ROOT / "pyproject.toml").read_text(encoding="utf-8")) assert pyproject["project"]["version"] == version_for(RELEASE_ORDINAL) def test_the_committed_pope_list_is_whole() -> None: """popes.json is generated; this is what stops a bad regeneration shipping.""" document = json.loads((REPO_ROOT / "signal_bot" / "popes.json").read_text(encoding="utf-8")) entries = document["popes"] assert len(entries) == 267 assert [entry["ordinal"] for entry in entries] == list(range(1, 268)) assert all(entry["name"] and entry["portrait"] and entry["reign"] for entry in entries) assert all(entry["latin"] and entry["title"] for entry in entries) def test_the_article_title_keeps_its_disambiguator() -> None: """title is the URL slug; name is not, and the difference is a 404. "Hygin" alone is a different pl.wikipedia article entirely, while "Klemens I (papież)" does not exist -- so neither appending nor omitting the suffix works as a rule. Only the wikitext link target is right. """ assert pope_for(9).title == "Hygin (papież)" assert pope_for(4).title == "Klemens I" assert pope_for(9).latin == "Hyginus"