Files
shelfmark/tests/download/test_zip_stream.py
T
CaliBrain 10710abd4e fix(download): send every file of a multi-file download to the browser (#1489)
Download to Browser only ever sent the first file of a multi-file
release
(audiobook chapters, packs): the folder output returned final_paths[0]
and
/api/localdownload served that one path.

The folder output now keeps every final path on the in-memory task
(download_paths), and /api/localdownload streams them as one
uncompressed
ZIP named after the book when there is more than one. The archive is
never
staged on disk or held in memory, and ZIP64 covers large audiobooks.
History still stores only the first path, so a re-download from history
after a dismiss or restart serves a single file, as before.

Also brings every linter back to green on all files:
- pre-commit: bump the ruff hook to v0.17.1 to match uv.lock;
ruff-format
  the Python examples in docs/dev and skills
- ruff: fix the remaining issues in skills/shelfmark/download_books.py;
  format base_handler.py
- basedpyright: Optional and attrs errors in databazeknih.py
- knip: drop 31 unused exports, reuse ColumnColorHint in colorMaps
- oxfmt 0.72: styles.css
- direct download: fix the "Welib, mirrors" typo and the test that still
  expected the pre-OceanofPDF message

Fixes #1467
2026-10-20 02:33:53 -04:00

42 lines
1.5 KiB
Python

import io
import zipfile
from shelfmark.download.zip_stream import stream_zip, zip_entry_names
def test_stream_zip_holds_every_file_uncompressed(tmp_path):
book = tmp_path / "Author" / "Title"
book.mkdir(parents=True)
(book / "Part 01.m4b").write_bytes(b"chapter one")
(book / "Part 02.m4b").write_bytes(b"chapter two" * 1000)
data = b"".join(stream_zip([str(book / "Part 01.m4b"), str(book / "Part 02.m4b")]))
with zipfile.ZipFile(io.BytesIO(data)) as archive:
assert archive.testzip() is None
assert archive.namelist() == ["Part 01.m4b", "Part 02.m4b"]
assert archive.read("Part 01.m4b") == b"chapter one"
assert archive.read("Part 02.m4b") == b"chapter two" * 1000
assert {info.compress_type for info in archive.infolist()} == {zipfile.ZIP_STORED}
def test_stream_zip_yields_while_reading_rather_than_at_the_end(tmp_path, monkeypatch):
import shelfmark.download.zip_stream as zip_stream
monkeypatch.setattr(zip_stream, "_ZIP_STREAM_CHUNK_SIZE", 16)
big = tmp_path / "big.mp3"
big.write_bytes(b"x" * 160)
chunks = list(stream_zip([str(big)]))
assert len(chunks) > 10
def test_zip_entry_names_keep_folders_below_the_common_parent(tmp_path):
first = tmp_path / "library" / "Book One" / "a.mp3"
second = tmp_path / "library" / "Book Two" / "b.mp3"
names = [name for _, name in zip_entry_names([str(first), str(second)])]
assert names == ["Book One/a.mp3", "Book Two/b.mp3"]