from __future__ import annotations import io from pathlib import Path import pytest from sticker_downloader.progress import ConsoleProgress, NullProgress from sticker_downloader.results import ( FileOutcome, FileStatus, PackResult, RunTotals, format_size, ) def outcome(name: str, status: FileStatus, size: int = 0) -> FileOutcome: return FileOutcome(Path("downloads/Pack") / name, status, size=size) def reporter(**kwargs) -> tuple[ConsoleProgress, io.StringIO]: stream = io.StringIO() return ConsoleProgress(stream, use_ansi=False, **kwargs), stream def test_pack_lifecycle_is_reported(): progress, stream = reporter() progress.pack_started("Pack", "Pack", 2, 3) progress.file_finished(outcome("001.webp", FileStatus.DOWNLOADED, 1024)) result = PackResult( reference="Pack", name="Pack", directory=Path("downloads/Pack"), total_stickers=2, outcomes=[outcome("001.webp", FileStatus.DOWNLOADED, 1024)], ) progress.pack_finished(result) text = stream.getvalue() assert "Pack: 2 stickers, 3 files" in text assert "1 downloaded" in text assert "downloads/Pack" in text def test_quiet_files_are_not_listed_without_verbose(): progress, stream = reporter() progress.file_finished(outcome("001.webp", FileStatus.DOWNLOADED, 10)) assert stream.getvalue() == "" def test_verbose_lists_each_file_with_its_size(): progress, stream = reporter(verbose=True) progress.file_finished(outcome("001.webp", FileStatus.DOWNLOADED, 2048)) assert "downloaded: 001.webp (2.0 KiB)" in stream.getvalue() def test_failures_are_always_reported(): progress, stream = reporter() progress.file_finished( FileOutcome(Path("001.webp"), FileStatus.FAILED, error="HTTP 404") ) assert "error: 001.webp: HTTP 404" in stream.getvalue() def test_pack_error_is_reported_instead_of_a_summary(): progress, stream = reporter() progress.pack_finished(PackResult(reference="Nope", error="sticker pack not found")) assert "error: Nope: sticker pack not found" in stream.getvalue() def test_run_summary_lists_failing_packs(): progress, stream = reporter() good = PackResult( reference="A", name="A", outcomes=[outcome("001.webp", FileStatus.DOWNLOADED, 1024)], ) bad = PackResult(reference="B", name="B", error="sticker pack not found") progress.run_finished([good, bad]) text = stream.getvalue() assert "Done: 1/2 packs" in text assert "files downloaded : 1 (1.0 KiB)" in text assert "packs failed : 1" in text assert "- B: sticker pack not found" in text def test_empty_run_says_so(): progress, stream = reporter() progress.run_finished([]) assert "Nothing to do." in stream.getvalue() def test_live_line_is_redrawn_on_a_terminal(): stream = io.StringIO() progress = ConsoleProgress(stream, use_ansi=True) progress.pack_started("Pack", "Pack", 1, 2) progress.file_finished(outcome("001.webp", FileStatus.DOWNLOADED, 1)) progress.file_finished(outcome("002.webp", FileStatus.DOWNLOADED, 1)) assert "\r 1/2 files" in stream.getvalue() assert "\r 2/2 files" in stream.getvalue() def test_null_progress_accepts_every_call(): progress = NullProgress() progress.info("x") progress.warn("x") progress.error("x") progress.pack_started("a", "b", 1, 1) progress.file_finished(outcome("001.webp", FileStatus.DOWNLOADED)) progress.pack_finished(PackResult(reference="a")) progress.run_finished([]) def test_run_totals_aggregate_every_status(): results = [ PackResult( reference="A", outcomes=[ outcome("001.webp", FileStatus.DOWNLOADED, 100), outcome("002.webp", FileStatus.SKIPPED_EXISTING), outcome("003.webp", FileStatus.FAILED), ], ), PackResult(reference="B", outcomes=[outcome("001.webp", FileStatus.PLANNED)]), ] totals = RunTotals.from_results(results) assert (totals.packs, totals.packs_ok, totals.packs_failed) == (2, 1, 1) assert (totals.downloaded, totals.skipped, totals.planned, totals.failed) == ( 1, 1, 1, 1, ) assert totals.bytes_written == 100 @pytest.mark.parametrize( ("num_bytes", "expected"), [(0, "0 B"), (512, "512 B"), (2048, "2.0 KiB"), (5 * 1024**2, "5.0 MiB")], ) def test_format_size(num_bytes: int, expected: str): assert format_size(num_bytes) == expected