fix: keep embedded metadata and group formats on directory upload

The filepath extractor was merged on the right, so a parent folder's name
overrode the title inside the file. Grouping also split a book's formats when
its own folder was the upload root.
This commit is contained in:
2026-08-12 15:17:48 -04:00
parent bd8d68b9ba
commit 510306f24d
3 changed files with 74 additions and 7 deletions
+55
View File
@@ -369,6 +369,61 @@ async def test_create_multiple_books_from_directory(
assert len(data.get("items") or data.get("data")) >= 1
async def test_create_books_from_parent_directory_keeps_embedded_title(
authenticated_client: AsyncClient,
) -> None:
"""A folder name in the upload path must not override the file's own metadata.
The browser sends webkitRelativePath, so picking the shelf above a book's folder
submits one more path component than picking the folder itself. That extra level
used to make the directory name win over the title inside the EPUB.
"""
source = Path("tests/data_files/Metamorphosis - Franz Kafka.epub")
files = [
(
"files",
(
"Shelf/Metamorphosis - Franz Kafka/Metamorphosis.epub",
source.read_bytes(),
"application/epub+zip",
),
)
]
response = await authenticated_client.post(
"/books/fromFiles?library_id=1", files=files, data={"library_id": 1}
)
assert response.status_code == 201
books = response.json()["items"]
assert len(books) == 1
assert books[0]["title"] == "Metamorphosis"
async def test_create_books_groups_formats_within_one_folder(
authenticated_client: AsyncClient,
) -> None:
"""Picking a book's own folder yields one book with both formats, not two books."""
epub = Path("tests/data_files/Metamorphosis - Franz Kafka.epub").read_bytes()
pdf = Path("tests/data_files/Calculus Made Easy - Silvanus Thompson.pdf").read_bytes()
files = [
("files", ("Metamorphosis/Metamorphosis.epub", epub, "application/epub+zip")),
("files", ("Metamorphosis/Metamorphosis.pdf", pdf, "application/pdf")),
]
response = await authenticated_client.post(
"/books/fromFiles?library_id=1", files=files, data={"library_id": 1}
)
assert response.status_code == 201
books = response.json()["items"]
assert len(books) == 1
assert len(books[0]["files"]) == 2
# NOTE: the multi-book ZIP download is covered at the service level, in
# tests/unit/test_services/test_book_service.py. Driving `/books/download` through
# AsyncTestClient hangs in fixture teardown: it is the only `Stream` endpoint in the