bookwyrm/bookwyrm/tests/importers/test_importer.py

386 lines
15 KiB
Python
Raw Normal View History

""" testing import """
2022-11-07 17:50:05 +00:00
from collections import namedtuple
import pathlib
from unittest.mock import patch
import datetime
import pytz
from django.test import TestCase
import responses
from bookwyrm import models
from bookwyrm.importers import Importer
2022-11-07 17:50:05 +00:00
from bookwyrm.models.import_job import start_import_task, import_item_task
from bookwyrm.models.import_job import handle_imported_book
def make_date(*args):
"""helper function to easily generate a date obj"""
return datetime.datetime(*args, tzinfo=pytz.UTC)
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
@patch("bookwyrm.activitystreams.add_book_statuses_task.delay")
class GenericImporter(TestCase):
"""importing from csv"""
def setUp(self):
"""use a test csv"""
self.importer = Importer()
datafile = pathlib.Path(__file__).parent.joinpath("../data/generic.csv")
# pylint: disable-next=consider-using-with
self.csv = open(datafile, "r", encoding=self.importer.encoding)
def tearDown(self):
"""close test csv"""
self.csv.close()
@classmethod
def setUpTestData(cls):
"""populate database"""
with (
patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"),
patch("bookwyrm.activitystreams.populate_stream_task.delay"),
patch("bookwyrm.lists_stream.populate_lists_task.delay"),
):
cls.local_user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "password", local=True
)
2022-12-06 22:11:03 +00:00
models.SiteSettings.objects.create()
work = models.Work.objects.create(title="Test Work")
cls.book = models.Edition.objects.create(
title="Example Edition",
remote_id="https://example.com/book/1",
parent_work=work,
)
def test_create_job(self, *_):
"""creates the import job entry and checks csv"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "public"
)
self.assertEqual(import_job.user, self.local_user)
self.assertEqual(import_job.include_reviews, False)
self.assertEqual(import_job.privacy, "public")
import_items = models.ImportItem.objects.filter(job=import_job).all()
self.assertEqual(len(import_items), 4)
self.assertEqual(import_items[0].index, 0)
self.assertEqual(import_items[0].normalized_data["id"], "38")
self.assertEqual(import_items[0].normalized_data["title"], "Gideon the Ninth")
self.assertEqual(import_items[0].normalized_data["authors"], "Tamsyn Muir")
self.assertEqual(import_items[0].normalized_data["isbn_13"], "9781250313195")
self.assertIsNone(import_items[0].normalized_data["isbn_10"])
self.assertEqual(import_items[0].normalized_data["shelf"], "read")
self.assertEqual(import_items[1].index, 1)
self.assertEqual(import_items[1].normalized_data["id"], "48")
self.assertEqual(import_items[1].normalized_data["title"], "Harrow the Ninth")
self.assertEqual(import_items[2].index, 2)
self.assertEqual(import_items[2].normalized_data["id"], "23")
self.assertEqual(import_items[2].normalized_data["title"], "Subcutanean")
self.assertEqual(import_items[3].index, 3)
self.assertEqual(import_items[3].normalized_data["id"], "10")
self.assertEqual(import_items[3].normalized_data["title"], "Patisserie at Home")
def test_create_retry_job(self, *_):
"""trying again with items that didn't import"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "unlisted"
)
import_items = models.ImportItem.objects.filter(job=import_job).all()[:2]
retry = self.importer.create_retry_job(
self.local_user, import_job, import_items
)
self.assertNotEqual(import_job, retry)
self.assertEqual(retry.user, self.local_user)
self.assertEqual(retry.include_reviews, False)
self.assertEqual(retry.privacy, "unlisted")
retry_items = models.ImportItem.objects.filter(job=retry).all()
self.assertEqual(len(retry_items), 2)
self.assertEqual(retry_items[0].index, 0)
self.assertEqual(retry_items[0].normalized_data["id"], "38")
self.assertEqual(retry_items[1].index, 1)
self.assertEqual(retry_items[1].normalized_data["id"], "48")
def test_start_import(self, *_):
"""check that a task was created"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "unlisted"
)
2022-11-07 17:50:05 +00:00
MockTask = namedtuple("Task", ("id"))
with patch("bookwyrm.models.import_job.start_import_task.delay") as mock:
mock.return_value = MockTask(123)
import_job.start_job()
2021-11-14 19:31:47 +00:00
self.assertEqual(mock.call_count, 1)
2022-11-07 17:50:05 +00:00
import_job.refresh_from_db()
self.assertEqual(import_job.task_id, "123")
@responses.activate
2021-11-11 23:17:32 +00:00
def test_start_import_task(self, *_):
"""resolve entry"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "unlisted"
)
2022-11-07 17:50:05 +00:00
MockTask = namedtuple("Task", ("id"))
with patch("bookwyrm.models.import_job.import_item_task.delay") as mock:
mock.return_value = MockTask(123)
2021-11-13 17:02:42 +00:00
start_import_task(import_job.id)
2021-11-11 23:17:32 +00:00
self.assertEqual(mock.call_count, 4)
@responses.activate
def test_import_item_task(self, *_):
"""resolve entry"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "unlisted"
)
import_item = models.ImportItem.objects.get(job=import_job, index=0)
with patch(
2021-12-14 22:19:27 +00:00
"bookwyrm.models.import_job.ImportItem.get_book_from_identifier"
) as resolve:
2021-11-11 23:17:32 +00:00
resolve.return_value = self.book
2021-11-12 16:55:47 +00:00
with patch(
"bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"
) as mock:
2021-11-13 19:22:07 +00:00
import_item_task(import_item.id)
2021-11-12 16:55:47 +00:00
kwargs = mock.call_args.kwargs
self.assertEqual(kwargs["queue"], "import_triggered")
2021-11-11 23:17:32 +00:00
import_item.refresh_from_db()
2021-11-14 17:56:23 +00:00
def test_complete_job(self, *_):
"""test notification"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "unlisted"
)
2021-11-14 19:31:47 +00:00
items = import_job.items.all()
for item in items[:3]:
item.fail_reason = "hello"
item.save()
item.update_job()
self.assertFalse(
models.Notification.objects.filter(
user=self.local_user,
related_import=import_job,
notification_type="IMPORT",
).exists()
)
2021-11-14 17:56:23 +00:00
item = items.last()
2021-11-14 19:31:47 +00:00
item.fail_reason = "hello"
item.save()
2021-11-14 17:56:23 +00:00
item.update_job()
2021-11-14 19:31:47 +00:00
import_job.refresh_from_db()
self.assertTrue(import_job.complete)
2021-11-14 17:56:23 +00:00
self.assertTrue(
models.Notification.objects.filter(
user=self.local_user,
related_import=import_job,
notification_type="IMPORT",
).exists()
)
def test_handle_imported_book(self, *_):
"""import added a book, this adds related connections"""
shelf = self.local_user.shelf_set.filter(
identifier=models.Shelf.READ_FINISHED
).first()
self.assertIsNone(shelf.books.first())
import_job = self.importer.create_job(
self.local_user, self.csv, False, "public"
)
import_item = import_job.items.first()
import_item.book = self.book
import_item.save()
2021-11-12 16:55:47 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
2021-11-13 19:22:07 +00:00
handle_imported_book(import_item)
shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book)
def test_handle_imported_book_already_shelved(self, *_):
"""import added a book, this adds related connections"""
2021-11-12 16:55:47 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
shelf = self.local_user.shelf_set.filter(
identifier=models.Shelf.TO_READ
).first()
models.ShelfBook.objects.create(
shelf=shelf,
user=self.local_user,
book=self.book,
shelved_date=make_date(2020, 2, 2),
)
import_job = self.importer.create_job(
2021-11-13 19:22:07 +00:00
self.local_user, self.csv, False, "public"
)
import_item = import_job.items.first()
import_item.book = self.book
import_item.save()
2021-11-12 16:55:47 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
2021-11-13 19:22:07 +00:00
handle_imported_book(import_item)
shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book)
self.assertEqual(
shelf.shelfbook_set.first().shelved_date, make_date(2020, 2, 2)
)
self.assertIsNone(
self.local_user.shelf_set.get(
identifier=models.Shelf.READ_FINISHED
).books.first()
)
def test_handle_import_twice(self, *_):
"""re-importing books"""
shelf = self.local_user.shelf_set.filter(
identifier=models.Shelf.READ_FINISHED
).first()
import_job = self.importer.create_job(
self.local_user, self.csv, False, "public"
)
import_item = import_job.items.first()
import_item.book = self.book
import_item.save()
2021-11-12 16:55:47 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
2021-11-13 19:22:07 +00:00
handle_imported_book(import_item)
handle_imported_book(import_item)
shelf.refresh_from_db()
self.assertEqual(shelf.books.first(), self.book)
self.assertEqual(models.ReadThrough.objects.count(), 1)
@patch("bookwyrm.activitystreams.add_status_task.delay")
def test_handle_imported_book_review(self, *_):
"""review import"""
2021-11-13 19:22:07 +00:00
import_job = self.importer.create_job(
self.local_user, self.csv, True, "unlisted"
)
import_item = import_job.items.filter(index=3).first()
import_item.book = self.book
import_item.save()
with (
patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"),
patch("bookwyrm.models.Status.broadcast") as broadcast_mock,
):
handle_imported_book(import_item)
kwargs = broadcast_mock.call_args.kwargs
self.assertEqual(kwargs["software"], "bookwyrm")
review = models.Review.objects.get(book=self.book, user=self.local_user)
self.assertEqual(review.content, "mixed feelings")
self.assertEqual(review.rating, 2.0)
self.assertEqual(review.privacy, "unlisted")
import_item.refresh_from_db()
self.assertEqual(import_item.linked_review, review)
@patch("bookwyrm.activitystreams.add_status_task.delay")
def test_handle_imported_book_rating(self, *_):
"""rating import"""
import_job = self.importer.create_job(
2021-11-13 19:22:07 +00:00
self.local_user, self.csv, True, "unlisted"
)
import_item = import_job.items.filter(index=1).first()
import_item.book = self.book
import_item.save()
2021-11-12 16:55:47 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
2021-11-13 19:22:07 +00:00
handle_imported_book(import_item)
review = models.ReviewRating.objects.get(book=self.book, user=self.local_user)
self.assertIsInstance(review, models.ReviewRating)
self.assertEqual(review.rating, 3.0)
self.assertEqual(review.privacy, "unlisted")
import_item.refresh_from_db()
self.assertEqual(import_item.linked_review.id, review.id)
2021-11-14 16:23:26 +00:00
@patch("bookwyrm.activitystreams.add_status_task.delay")
def test_handle_imported_book_rating_duplicate_with_link(self, *_):
"""rating import twice"""
import_job = self.importer.create_job(
self.local_user, self.csv, True, "unlisted"
)
import_item = import_job.items.filter(index=1).first()
import_item.book = self.book
import_item.save()
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
handle_imported_book(import_item)
handle_imported_book(import_item)
review = models.ReviewRating.objects.get(book=self.book, user=self.local_user)
self.assertIsInstance(review, models.ReviewRating)
self.assertEqual(review.rating, 3.0)
self.assertEqual(review.privacy, "unlisted")
import_item.refresh_from_db()
self.assertEqual(import_item.linked_review.id, review.id)
@patch("bookwyrm.activitystreams.add_status_task.delay")
def test_handle_imported_book_rating_duplicate_without_link(self, *_):
"""rating import twice"""
import_job = self.importer.create_job(
self.local_user, self.csv, True, "unlisted"
)
import_item = import_job.items.filter(index=1).first()
import_item.book = self.book
import_item.save()
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
handle_imported_book(import_item)
import_item.refresh_from_db()
import_item.linked_review = None
import_item.save()
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
handle_imported_book(import_item)
review = models.ReviewRating.objects.get(book=self.book, user=self.local_user)
self.assertIsInstance(review, models.ReviewRating)
self.assertEqual(review.rating, 3.0)
self.assertEqual(review.privacy, "unlisted")
import_item.refresh_from_db()
self.assertEqual(import_item.linked_review.id, review.id)
def test_handle_imported_book_reviews_disabled(self, *_):
"""review import"""
import_job = self.importer.create_job(
self.local_user, self.csv, False, "unlisted"
)
import_item = import_job.items.filter(index=3).first()
import_item.book = self.book
import_item.save()
2021-11-12 16:55:47 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
2021-11-13 19:22:07 +00:00
handle_imported_book(import_item)
self.assertFalse(
models.Review.objects.filter(book=self.book, user=self.local_user).exists()
)
2022-12-19 22:25:43 +00:00
def test_import_limit(self, *_):
"""checks if import limit works"""
site_settings = models.SiteSettings.objects.get()
site_settings.import_size_limit = 2
site_settings.import_limit_reset = 2
site_settings.save()
import_job = self.importer.create_job(
self.local_user, self.csv, False, "public"
)
import_items = models.ImportItem.objects.filter(job=import_job).all()
self.assertEqual(len(import_items), 2)