216 lines
6.3 KiB
Python
216 lines
6.3 KiB
Python
"""@file
|
|
@brief Tests of the command line with a fake source and injected input and output.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
|
|
from conftest import FakeChartSource, FakeMusicService
|
|
|
|
from musical_time_machine.constants import CHART_SIZE, DATE_PROMPT
|
|
from musical_time_machine.domain.errors import (
|
|
BrowserFileMissingError,
|
|
ChartParseError,
|
|
ChartUnavailableError,
|
|
PlaylistCreationError,
|
|
)
|
|
from musical_time_machine.domain.playlist import Playlist
|
|
from musical_time_machine.domain.song import Song
|
|
from musical_time_machine.presentation.cli import main
|
|
|
|
|
|
def _run(answer: str, source: FakeChartSource) -> tuple[int, list[str], list[str]]:
|
|
prompts: list[str] = []
|
|
lines: list[str] = []
|
|
|
|
def read(prompt: str) -> str:
|
|
prompts.append(prompt)
|
|
return answer
|
|
|
|
code = main(["--list-only"], source=source, read=read, write=lines.append)
|
|
return code, lines, prompts
|
|
|
|
|
|
def test_main_prints_numbered_songs_and_returns_zero() -> None:
|
|
songs = [Song(f"Title {n}", f"Artist {n}") for n in range(1, CHART_SIZE + 1)]
|
|
|
|
code, lines, prompts = _run("2026-04-18", FakeChartSource(songs))
|
|
|
|
assert code == 0
|
|
assert prompts == [DATE_PROMPT]
|
|
assert len(lines) == CHART_SIZE
|
|
assert lines[0] == "1. Title 1 - Artist 1"
|
|
assert lines[-1] == "100. Title 100 - Artist 100"
|
|
|
|
|
|
def test_main_returns_one_with_message_for_invalid_date() -> None:
|
|
source = FakeChartSource([Song("x", "y")])
|
|
|
|
code, lines, _ = _run("18/04/2026", source)
|
|
|
|
assert code == 1
|
|
assert len(lines) == 1
|
|
assert "YYYY-MM-DD" in lines[0]
|
|
assert source.requested == []
|
|
|
|
|
|
def test_main_returns_one_when_chart_unavailable() -> None:
|
|
code, lines, _ = _run("2026-04-18", FakeChartSource(error=ChartUnavailableError("404")))
|
|
|
|
assert code == 1
|
|
assert lines == ["404"]
|
|
|
|
|
|
def test_main_returns_one_when_chart_has_no_songs() -> None:
|
|
code, lines, _ = _run("2026-04-18", FakeChartSource(error=ChartParseError("empty")))
|
|
|
|
assert code == 1
|
|
assert lines == ["empty"]
|
|
|
|
|
|
def test_main_check_login_prints_playlist_count() -> None:
|
|
lines: list[str] = []
|
|
service = FakeMusicService([Playlist("a", "A"), Playlist("b", "B"), Playlist("c", "C")])
|
|
|
|
code = main(["--check-login"], music_service=service, write=lines.append)
|
|
|
|
assert code == 0
|
|
assert lines == ["Found 3 playlists in your library."]
|
|
|
|
|
|
def test_main_check_login_returns_one_when_browser_file_missing() -> None:
|
|
lines: list[str] = []
|
|
service = FakeMusicService(error=BrowserFileMissingError("browser.json was not found."))
|
|
|
|
code = main(["--check-login"], music_service=service, write=lines.append)
|
|
|
|
assert code == 1
|
|
assert lines == ["browser.json was not found."]
|
|
|
|
|
|
def test_main_check_login_with_missing_real_file_returns_one(tmp_path: Path) -> None:
|
|
lines: list[str] = []
|
|
argv = ["--check-login", "--browser-file", str(tmp_path / "none.json")]
|
|
|
|
code = main(argv, write=lines.append)
|
|
|
|
assert code == 1
|
|
assert "ytmusicapi browser" in lines[0]
|
|
|
|
|
|
def test_main_list_only_does_not_use_the_music_service() -> None:
|
|
lines: list[str] = []
|
|
service = FakeMusicService(error=BrowserFileMissingError("must not be called"))
|
|
|
|
code = main(
|
|
["--list-only"],
|
|
source=FakeChartSource([Song("T", "A")]),
|
|
music_service=service,
|
|
read=lambda _prompt: "2026-04-18",
|
|
write=lines.append,
|
|
)
|
|
|
|
assert code == 0
|
|
assert lines == ["1. T - A"]
|
|
|
|
|
|
def _create(answer: str, service: FakeMusicService) -> tuple[int, list[str]]:
|
|
lines: list[str] = []
|
|
code = main(
|
|
["--create-playlist"],
|
|
music_service=service,
|
|
read=lambda _prompt: answer,
|
|
write=lines.append,
|
|
)
|
|
return code, lines
|
|
|
|
|
|
def test_main_create_playlist_reports_creation() -> None:
|
|
service = FakeMusicService()
|
|
|
|
code, lines = _create("2026-04-18", service)
|
|
|
|
assert code == 0
|
|
assert lines == ['Created playlist "2026-04-18 Billboard 100".']
|
|
assert len(service.created) == 1
|
|
|
|
|
|
def test_main_create_playlist_reports_existing_playlist() -> None:
|
|
service = FakeMusicService([Playlist("a", "2026-04-18 Billboard 100")])
|
|
|
|
code, lines = _create("2026-04-18", service)
|
|
|
|
assert code == 0
|
|
assert lines == ['Playlist "2026-04-18 Billboard 100" already exists; nothing was created.']
|
|
assert service.created == []
|
|
|
|
|
|
def test_main_create_playlist_returns_one_for_invalid_date() -> None:
|
|
service = FakeMusicService()
|
|
|
|
code, lines = _create("nope", service)
|
|
|
|
assert code == 1
|
|
assert "YYYY-MM-DD" in lines[0]
|
|
assert service.created == []
|
|
|
|
|
|
def test_main_create_playlist_returns_one_on_domain_error() -> None:
|
|
code, lines = _create("2026-04-18", FakeMusicService(error=PlaylistCreationError("failed")))
|
|
|
|
assert code == 1
|
|
assert lines == ["failed"]
|
|
|
|
|
|
def _full(
|
|
source: FakeChartSource, service: FakeMusicService, answer: str = "2026-04-18"
|
|
) -> tuple[int, list[str], list[float]]:
|
|
lines: list[str] = []
|
|
pauses: list[float] = []
|
|
code = main(
|
|
[],
|
|
source=source,
|
|
music_service=service,
|
|
read=lambda _prompt: answer,
|
|
write=lines.append,
|
|
pause=pauses.append,
|
|
)
|
|
return code, lines, pauses
|
|
|
|
|
|
def test_main_full_run_adds_songs_and_prints_summary_with_skipped() -> None:
|
|
source = FakeChartSource([Song("One", "A"), Song("Two", "B"), Song("Three", "C")])
|
|
service = FakeMusicService(video_ids={"One": "v1", "Three": "v3"})
|
|
|
|
code, lines, pauses = _full(source, service)
|
|
|
|
assert code == 0
|
|
assert lines == [
|
|
'Added 2 of 3 songs to "2026-04-18 Billboard 100"; skipped 1.',
|
|
"Skipped: Two - B",
|
|
]
|
|
assert len(service.created) == 1
|
|
assert len(pauses) == 2
|
|
|
|
|
|
def test_main_full_run_without_skips_prints_only_summary() -> None:
|
|
code, lines, _ = _full(FakeChartSource([Song("One", "A")]), FakeMusicService())
|
|
|
|
assert code == 0
|
|
assert lines == ['Added 1 of 1 songs to "2026-04-18 Billboard 100"; skipped 0.']
|
|
|
|
|
|
def test_main_full_run_returns_one_for_invalid_date() -> None:
|
|
code, lines, _ = _full(FakeChartSource([Song("x", "y")]), FakeMusicService(), "nope")
|
|
|
|
assert code == 1
|
|
assert "YYYY-MM-DD" in lines[0]
|
|
|
|
|
|
def test_main_full_run_returns_one_on_login_error() -> None:
|
|
service = FakeMusicService(error=BrowserFileMissingError("browser.json was not found."))
|
|
|
|
code, lines, _ = _full(FakeChartSource([Song("x", "y")]), service)
|
|
|
|
assert code == 1
|
|
assert lines == ["browser.json was not found."]
|