149 lines
5.6 KiB
Python
149 lines
5.6 KiB
Python
import unittest
|
|
from contextlib import redirect_stdout
|
|
from io import StringIO
|
|
from pathlib import Path
|
|
|
|
from radioeins_top100 import (
|
|
POINTS,
|
|
IncompleteJuryListError,
|
|
build_argument_parser,
|
|
decide_incomplete_list,
|
|
derive_output_dir,
|
|
parse_jury_page,
|
|
)
|
|
|
|
|
|
class RadioeinsTop100Tests(unittest.TestCase):
|
|
def parse_fixture(self, name):
|
|
fixture = Path(__file__).parent / "fixtures" / name
|
|
return parse_jury_page(
|
|
fixture.read_text(encoding="utf-8"),
|
|
fallback_name="Fallback",
|
|
source_url=f"https://example.test/{name}",
|
|
)
|
|
|
|
def test_output_directory_is_derived_from_url_path(self):
|
|
base = "https://www.radioeins.de/musik/top_100/2026"
|
|
self.assertEqual(derive_output_dir(f"{base}/zeit/"), Path("output_zeit"))
|
|
self.assertEqual(
|
|
derive_output_dir(f"{base}/deutscher-hip-hop"),
|
|
Path("output_deutscher-hip-hop"),
|
|
)
|
|
|
|
def test_point_mapping(self):
|
|
self.assertEqual(
|
|
[POINTS[rank] for rank in range(1, 11)],
|
|
[12, 10, 8, 7, 6, 5, 4, 3, 2, 1],
|
|
)
|
|
|
|
def test_parse_existing_jury_fixture(self):
|
|
votes = self.parse_fixture("aditya-sharma.html")
|
|
|
|
self.assertEqual(len(votes), 10)
|
|
self.assertEqual(votes[0].juror, "Aditya Sharma")
|
|
self.assertEqual((votes[0].rank, votes[0].points), (1, 12))
|
|
self.assertEqual((votes[1].artist, votes[1].title), ("Samy Deluxe", "Weck Mich Auf"))
|
|
self.assertEqual((votes[-1].rank, votes[-1].points), (10, 1))
|
|
|
|
def test_parse_historical_page_without_heading(self):
|
|
votes = self.parse_fixture("andreas-loeffler.html")
|
|
|
|
self.assertEqual(len(votes), 10)
|
|
self.assertEqual(votes[0].juror, "Andreas Löffler")
|
|
self.assertEqual((votes[0].artist, votes[0].title), ("Die Ärzte", "Schrei nach Liebe"))
|
|
self.assertEqual((votes[-1].rank, votes[-1].points), (10, 1))
|
|
|
|
def test_parse_historical_page_with_html_spacing(self):
|
|
votes = self.parse_fixture("andreas-ulrich.html")
|
|
|
|
self.assertEqual(len(votes), 10)
|
|
self.assertEqual(votes[5].title, "Pocahontas")
|
|
self.assertEqual(votes[-1].artist, "Wir Sind Helden")
|
|
|
|
def test_historical_title_preserves_additional_colons(self):
|
|
fixture = Path(__file__).parent / "fixtures" / "andreas-loeffler.html"
|
|
html = fixture.read_text(encoding="utf-8").replace(
|
|
"Schrei nach Liebe", "Live: Schrei nach Liebe"
|
|
)
|
|
votes = parse_jury_page(html, "Fallback", "https://example.test/list.html")
|
|
|
|
self.assertEqual(votes[0].title, "Live: Schrei nach Liebe")
|
|
|
|
def test_historical_page_accepts_spaced_hyphen_separator(self):
|
|
votes = self.parse_fixture("aydo-abay.html")
|
|
|
|
self.assertEqual(len(votes), 10)
|
|
self.assertEqual(votes[7].artist, "Boxhmasters")
|
|
self.assertEqual(votes[7].title, "Mogli")
|
|
|
|
def test_historical_rank_without_dot(self):
|
|
votes = self.parse_fixture("love-aditya-sharma.html")
|
|
|
|
self.assertEqual((votes[4].rank, votes[4].artist, votes[4].title),
|
|
(5, "Nick Drake", "Northern Sky"))
|
|
|
|
def test_historical_period_separator(self):
|
|
christiane = self.parse_fixture("love-christiane-falk.html")
|
|
sven = self.parse_fixture("love-sven-erik-stephan.html")
|
|
|
|
self.assertEqual((christiane[3].artist, christiane[3].title),
|
|
("Jeff Buckley", "Hallelujah"))
|
|
self.assertEqual((sven[6].artist, sven[6].title),
|
|
("Cyndi Lauper", "Time After Time"))
|
|
self.assertEqual(christiane[2].artist, "R.E.M.")
|
|
|
|
def test_incomplete_historical_list_is_rejected(self):
|
|
with self.assertRaises(IncompleteJuryListError) as caught:
|
|
self.parse_fixture("love-dagobert.html")
|
|
|
|
output = StringIO()
|
|
with redirect_stdout(output):
|
|
accepted = decide_incomplete_list(
|
|
caught.exception,
|
|
accept_incomplete=False,
|
|
interactive=False,
|
|
decision_helper=lambda _: self.fail("must not prompt"),
|
|
)
|
|
|
|
self.assertFalse(accepted)
|
|
self.assertIn("Dagobert", output.getvalue())
|
|
self.assertEqual(caught.exception.missing_ranks, [10])
|
|
|
|
def test_incomplete_historical_list_is_accepted_interactively(self):
|
|
with self.assertRaises(IncompleteJuryListError) as caught:
|
|
self.parse_fixture("love-dagobert.html")
|
|
|
|
with redirect_stdout(StringIO()):
|
|
accepted = decide_incomplete_list(
|
|
caught.exception,
|
|
accept_incomplete=False,
|
|
interactive=True,
|
|
decision_helper=lambda prompt: "yes",
|
|
)
|
|
|
|
self.assertTrue(accepted)
|
|
self.assertEqual(
|
|
[(vote.rank, vote.points) for vote in caught.exception.votes],
|
|
[(1, 12), (2, 10), (3, 8), (4, 7), (5, 6),
|
|
(6, 5), (7, 4), (8, 3), (9, 2)],
|
|
)
|
|
|
|
def test_accept_incomplete_option(self):
|
|
args = build_argument_parser().parse_args(["--accept-incomplete"])
|
|
self.assertTrue(args.accept_incomplete)
|
|
|
|
with self.assertRaises(IncompleteJuryListError) as caught:
|
|
self.parse_fixture("love-dagobert.html")
|
|
with redirect_stdout(StringIO()):
|
|
accepted = decide_incomplete_list(
|
|
caught.exception,
|
|
accept_incomplete=args.accept_incomplete,
|
|
interactive=False,
|
|
decision_helper=lambda _: self.fail("must not prompt"),
|
|
)
|
|
self.assertTrue(accepted)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|