fix: ignore duplicate matches whose file is gone

The hash lives in the database and the file does not, so a file deleted behind
the app's back went on refusing its own replacement. Matches are now checked
against disk, and re-adding a book's own missing file writes it back into the
row that already describes it.
This commit is contained in:
2026-08-14 14:18:08 -04:00
parent b124a65d6e
commit d321315acf
3 changed files with 121 additions and 18 deletions
@@ -595,3 +595,48 @@ class TestBookPathCollisions:
assert len(book.files) == 2
for file in book.files:
assert (Path(book.path) / file.path).is_file()
@pytest.mark.asyncio
class TestMissingFiles:
"""A row whose bytes are gone must not stand in for the file itself."""
async def test_a_missing_file_is_not_a_duplicate(
self, books_service: BookService, test_library: m.Library
) -> None:
"""Otherwise the library refuses to take back a file it can no longer open."""
created = await books_service.create_many_from_files(
BooksCreateFromFiles(files=[upload(EPUB)]), test_library
)
book = await books_service.get(created.books[0].id)
(Path(book.path) / book.files[0].path).unlink()
result = await books_service.create_many_from_files(
BooksCreateFromFiles(files=[upload(EPUB)]), test_library
)
assert len(result.books) == 1
assert result.duplicates == []
restored = await books_service.get(result.books[0].id)
assert (Path(restored.path) / restored.files[0].path).is_file()
async def test_re_adding_a_missing_file_puts_it_back(
self, books_service: BookService, test_library: m.Library
) -> None:
"""The book already has a row for it, so the bytes go back where it says."""
created = await books_service.create_many_from_files(
BooksCreateFromFiles(files=[upload(EPUB)]), test_library
)
book_id = created.books[0].id
book = await books_service.get(book_id)
path = Path(book.path) / book.files[0].path
path.unlink()
await books_service.add_files(book_id, [upload(EPUB)], test_library)
book = await books_service.get(book_id)
assert len(book.files) == 1
assert path.is_file()
assert path.read_bytes() == EPUB.read_bytes()