Files
vrobbler/vrobbler/apps/books/tests/test_koreader.py
Colin Powell c71b51fdb8
Some checks failed
build / test (push) Has been cancelled
[books] Fix one-extra-scrobble bug on Koreader imports
2026-06-12 09:52:33 -04:00

213 lines
5.3 KiB
Python

import pytest
from unittest import mock
from datetime import datetime
from books.koreader import (
KoReaderBookColumn,
build_book_map,
build_page_data,
build_scrobbles_from_book_map,
get_author_str_from_row,
lookup_or_create_authors_from_author_str,
create_book_from_row,
process_koreader_sqlite_file,
)
@pytest.mark.django_db
@mock.patch("requests.get")
def test_build_book_map(get_mock, koreader_rows, valid_response):
get_mock.return_value = valid_response
book_map = build_book_map(koreader_rows.BOOK_ROWS)
assert len(book_map) == 1
@pytest.mark.django_db
@mock.patch("requests.get")
def test_load_page_data_to_map(get_mock, koreader_rows, valid_response):
get_mock.return_value = valid_response
book_map = build_page_data(
koreader_rows.PAGE_STATS_ROWS,
build_book_map(koreader_rows.BOOK_ROWS),
)
assert (
len(book_map[1]["pages"])
== koreader_rows.BOOK_ROWS[0][KoReaderBookColumn.TOTAL_READ_PAGES.value]
)
@pytest.mark.django_db
@mock.patch("requests.get")
def test_build_scrobbles_from_pages(get_mock, koreader_rows, demo_user, valid_response):
get_mock.return_value = valid_response
book_map = build_book_map(koreader_rows.BOOK_ROWS)
book_map = build_page_data(koreader_rows.PAGE_STATS_ROWS, book_map)
scrobbles = build_scrobbles_from_book_map(book_map, demo_user)
# The test data generator adds the session-gap 3600s AFTER the trigger page
# (not before), so the first session includes 21 pages (1-21), and each
# subsequent session has 20 until the last. The last page is now included
# in the final scrobble instead of being orphaned.
expected_scrobbles = 6 * len(book_map.keys())
assert len(scrobbles) == expected_scrobbles
assert len(scrobbles[0].logdata.page_data.keys()) == 21
assert len(scrobbles[1].logdata.page_data.keys()) == 20
assert len(scrobbles[2].logdata.page_data.keys()) == 20
assert len(scrobbles[3].logdata.page_data.keys()) == 20
assert len(scrobbles[4].logdata.page_data.keys()) == 20
assert len(scrobbles[5].logdata.page_data.keys()) == 19
def test_get_author_str_from_row():
row = [
1,
"Test Book",
"John Smith\nJane Doe",
0,
0,
0,
300,
"",
"",
"abc123",
0,
120,
]
result = get_author_str_from_row(row)
assert result == "John Smith, Jane Doe"
def test_get_author_str_from_row_strips_middle_initials():
row = [
1,
"Test Book",
"John A. Smith",
0,
0,
0,
300,
"",
"",
"abc123",
0,
120,
]
result = get_author_str_from_row(row)
assert result == "John Smith"
@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_creates_new_author():
author = lookup_or_create_authors_from_author_str("New Author")
assert len(author) == 1
assert author[0].name == "New Author"
@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_finds_existing():
from books.models import Author
Author.objects.create(name="Existing Author")
author = lookup_or_create_authors_from_author_str("Existing Author")
assert len(author) == 1
assert author[0].name == "Existing Author"
@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_ignores_na():
author = lookup_or_create_authors_from_author_str("N/A")
assert len(author) == 0
@pytest.mark.django_db
def test_lookup_or_create_authors_from_author_str_handles_multiple():
author = lookup_or_create_authors_from_author_str("Author One, Author Two")
assert len(author) == 2
@pytest.mark.django_db
def test_create_book_from_row():
row = [
1,
"Test Book - Author Name",
"Test Author",
0,
0,
0,
300,
"",
"",
"abc123def456",
3600,
120,
]
book = create_book_from_row(row)
assert book.title == "Test Book"
assert book.pages == 300
assert "abc123def456" in book.koreader_data_by_hash
assert book.authors.count() == 1
@pytest.mark.django_db
def test_create_book_from_row_handles_title_with_author_suffix():
row = [
1,
"Test Book - Author_Name",
"N/A",
0,
0,
0,
300,
"",
"",
"abc123def456",
3600,
120,
]
book = create_book_from_row(row)
assert book.title == "Test Book"
assert book.authors.count() == 1
assert book.authors.first().name == "Author"
@pytest.mark.django_db
def test_create_book_from_row_handles_null_bytes():
row = [
1,
"Test\x00Book",
"Test\x00Author",
0,
0,
0,
300,
"",
"",
"abc123def456",
3600,
120,
]
book = create_book_from_row(row)
assert "TestBook" in book.title
assert "TestAuthor" in book.authors.first().name
@pytest.mark.django_db
def test_build_book_map_ignores_garbage_titles(koreader_rows):
garbage_row = [
999,
"KOReader Quickstart Guide",
"N/A",
0,
0,
0,
0,
"",
"",
"garbage123",
0,
0,
]
book_map = build_book_map(koreader_rows.BOOK_ROWS + [garbage_row])
assert len(book_map) == 1
assert 999 not in book_map