Files
vrobbler/tests/scrobbles_tests/test_book_enrichment.py

241 lines
7.4 KiB
Python

from unittest.mock import patch
import pytest
from books.models import Author, Book
from scrobbles.tasks import enrich_book_metadata
@pytest.mark.django_db
@patch("books.models.lookup_book_from_ol")
def test_enrich_book_metadata_tags_success(mock_lookup):
mock_lookup.return_value = {
"title": "Test Book",
"openlibrary_id": "OL12345",
"summary": "A test summary",
"authors": ["Test Author"],
}
book = Book.objects.create(title="Test Book", pages=100)
enrich_book_metadata(book.id)
book.refresh_from_db()
assert book.openlibrary_id == "OL12345"
assert book.summary == "A test summary"
assert book.authors.filter(name="Test Author").exists()
assert "book-enriched" in book.tags.names()
assert "enrichment-failed" not in book.tags.names()
@pytest.mark.django_db
@patch("books.models.lookup_book_from_ol", return_value={})
@patch("books.models.lookup_comic_from_locg", return_value={})
@patch("books.models.lookup_book_from_google", return_value={})
def test_enrich_book_metadata_tags_failure_when_no_match(
mock_google, mock_locg, mock_ol
):
book = Book.objects.create(title="Unknown Book", pages=100)
enrich_book_metadata(book.id)
book.refresh_from_db()
assert "book-enriched" not in book.tags.names()
assert "enrichment-failed" in book.tags.names()
@pytest.mark.django_db
@patch("books.models.lookup_book_from_ol", side_effect=Exception("boom"))
@patch("books.models.lookup_comic_from_locg", side_effect=Exception("boom"))
@patch("books.models.lookup_book_from_google", side_effect=Exception("boom"))
def test_enrich_book_metadata_tags_failure_on_exception(
mock_google, mock_locg, mock_ol
):
book = Book.objects.create(title="Test Book", pages=100)
enrich_book_metadata(book.id)
book.refresh_from_db()
assert "book-enriched" not in book.tags.names()
assert "enrichment-failed" in book.tags.names()
@pytest.mark.django_db
def test_enrich_book_metadata_skips_missing_book():
enrich_book_metadata(99999)
@pytest.mark.django_db
def test_enrich_book_metadata_preserves_koreader_title_on_mismatch():
from unittest.mock import Mock
with patch("books.models.lookup_book_from_ol") as mock_lookup:
mock_lookup.return_value = {
"title": "Completely Different Title",
"openlibrary_id": "OL999",
}
book = Book.objects.create(title="Test Book", pages=100)
enrich_book_metadata(book.id)
book.refresh_from_db()
assert book.title == "Test Book"
assert book.openlibrary_id == "OL999"
assert "book-enriched" in book.tags.names()
@pytest.mark.django_db
def test_create_book_from_row_dispatches_enrichment():
from books.koreader import create_book_from_row
row = [
1,
"Test Book - Author Name",
"Test Author",
0,
0,
0,
300,
"",
"",
"abc123def456",
3600,
120,
]
with patch("scrobbles.tasks.enrich_book_metadata.delay") as mock_delay:
book = create_book_from_row(row)
mock_delay.assert_called_once_with(book.id)
@pytest.mark.django_db
def test_fix_metadata_does_not_crash_on_locg_data_with_isbn():
from unittest.mock import Mock
with patch("books.models.lookup_book_from_ol", return_value={}):
with patch("books.models.lookup_comic_from_locg") as mock_locg:
mock_locg.return_value = {
"title": "Test Book",
"summary": "A comic summary",
"isbn": "1234567890",
}
book = Book.objects.create(title="Test Book", pages=100)
enriched = book.fix_metadata()
assert enriched is True
book.refresh_from_db()
assert book.summary == "A comic summary"
@pytest.mark.django_db
def test_find_or_create_returns_existing_by_title():
book = Book.objects.create(original_title="Dune", title="Dune")
with patch("books.models.lookup_book_from_google") as mock_google:
found = Book.find_or_create("Dune")
assert found.id == book.id
mock_google.assert_not_called()
@pytest.mark.django_db
@patch("books.models.lookup_comic_from_comicvine", return_value={})
@patch("books.models.lookup_book_from_ol", return_value={})
@patch("books.models.lookup_book_from_google")
def test_find_or_create_reuses_existing_by_google_books_id(
mock_google, mock_ol, mock_comicvine
):
existing = Book.objects.create(
original_title="Dune - 50th Anniversary",
title="Dune",
google_books_id="gbooks_123",
)
mock_google.return_value = {
"title": "Dune",
"google_books_id": "gbooks_123",
"authors": ["Frank Herbert"],
"pages": 412,
}
found = Book.find_or_create("Dune")
assert found.id == existing.id
assert Book.objects.filter(original_title="Dune").count() == 0
assert Book.objects.count() == 1
assert found.authors.filter(name="Frank Herbert").exists()
@pytest.mark.django_db
@patch("books.models.lookup_comic_from_comicvine", return_value={})
@patch("books.models.lookup_book_from_ol", return_value={})
@patch("books.models.lookup_book_from_google")
def test_find_or_create_matches_by_title_and_author(
mock_google, mock_ol, mock_comicvine
):
author = Author.objects.create(name="Frank Herbert")
existing = Book.objects.create(original_title="Dune - Part 1", title="Dune")
existing.authors.add(author)
mock_google.return_value = {
"title": "Dune",
"authors": ["Frank Herbert"],
"pages": 412,
}
found = Book.find_or_create("Dune", author="Frank Herbert")
assert found.id == existing.id
assert Book.objects.count() == 1
@pytest.mark.django_db
@patch("books.models.lookup_comic_from_comicvine", return_value={})
@patch("books.models.lookup_book_from_ol", return_value={})
@patch("books.models.lookup_book_from_google")
def test_find_or_create_stores_google_books_id(mock_google, mock_ol, mock_comicvine):
mock_google.return_value = {
"title": "Dune",
"google_books_id": "gbooks_456",
"authors": ["Frank Herbert"],
"pages": 412,
}
book = Book.find_or_create("Dune")
assert book.google_books_id == "gbooks_456"
assert book.authors.filter(name="Frank Herbert").exists()
@patch("books.sources.google.requests.get")
def test_lookup_book_from_google_captures_volume_id(mock_get):
from books.sources.google import lookup_book_from_google
mock_response = mock_get.return_value
mock_response.status_code = 200
mock_response.content = b"""
{"items": [
{
"id": "gbooks_789",
"volumeInfo": {
"title": "Dune",
"authors": ["Frank Herbert"],
"publishedDate": "1965",
"pageCount": 412
}
}
]}
"""
result = lookup_book_from_google("Dune")
assert result["google_books_id"] == "gbooks_789"
assert result["authors"] == ["Frank Herbert"]
assert result["first_publish_year"] == 1965
@patch("books.sources.google.requests.get")
def test_lookup_book_from_google_includes_author_in_query(mock_get):
from books.sources.google import lookup_book_from_google
mock_response = mock_get.return_value
mock_response.status_code = 200
mock_response.content = b'{"items": []}'
lookup_book_from_google("Dune", author="Frank Herbert")
args, kwargs = mock_get.call_args
assert "inauthor:Frank Herbert" in kwargs["params"]["q"]