"""test bookwyrm user export functions""" import datetime import json import pathlib from unittest.mock import patch from django.utils import timezone from django.test import TestCase from bookwyrm import models from bookwyrm.utils.tar import BookwyrmTarFile class BookwyrmExportJob(TestCase): """testing user export functions""" def setUp(self): """lots of stuff to set up for a user export""" with ( patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch("bookwyrm.activitystreams.populate_stream_task.delay"), patch("bookwyrm.lists_stream.populate_lists_task.delay"), patch("bookwyrm.suggested_users.rerank_user_task.delay"), patch("bookwyrm.lists_stream.remove_list_task.delay"), patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"), patch("bookwyrm.activitystreams.add_book_statuses_task"), ): self.local_user = models.User.objects.create_user( "mouse", "mouse@mouse.mouse", "password", local=True, localname="mouse", name="Mouse", summary="I'm a real bookmouse", manually_approves_followers=False, hide_follows=False, show_goal=False, show_suggested_users=False, discoverable=True, preferred_timezone="America/Los Angeles", default_post_privacy="followers", ) avatar_path = pathlib.Path(__file__).parent.joinpath( "../../static/images/default_avi.jpg" ) with open(avatar_path, "rb") as avatar_file: self.local_user.avatar.save("mouse-avatar.jpg", avatar_file) self.rat_user = models.User.objects.create_user( "rat", "rat@rat.rat", "ratword", local=True, localname="rat" ) self.badger_user = models.User.objects.create_user( "badger", "badger@badger.badger", "badgerword", local=True, localname="badger", ) models.AnnualGoal.objects.create( user=self.local_user, year=timezone.now().year, goal=128937123, privacy="followers", ) self.list = models.List.objects.create( name="My excellent list", user=self.local_user, remote_id="https://local.lists/1111", ) self.saved_list = models.List.objects.create( name="My cool list", user=self.rat_user, remote_id="https://local.lists/9999", ) self.local_user.saved_lists.add(self.saved_list) self.local_user.blocks.add(self.badger_user) self.rat_user.followers.add(self.local_user) # book, edition, author self.author = models.Author.objects.create(name="Sam Zhu") self.work = models.Work.objects.create( title="Example Work", remote_id="https://example.com/book/1" ) self.edition = models.Edition.objects.create( title="Example Edition", parent_work=self.work ) # edition cover cover_path = pathlib.Path(__file__).parent.joinpath( "../../static/images/default_avi.jpg" ) with open(cover_path, "rb") as cover_file: self.edition.cover.save("tèst.jpg", cover_file) self.edition.authors.add(self.author) # readthrough self.readthrough_start = timezone.now() finish = self.readthrough_start + datetime.timedelta(days=1) models.ReadThrough.objects.create( user=self.local_user, book=self.edition, start_date=self.readthrough_start, finish_date=finish, ) # shelve read_shelf = models.Shelf.objects.get( user=self.local_user, identifier="read" ) models.ShelfBook.objects.create( book=self.edition, shelf=read_shelf, user=self.local_user ) # add to list models.ListItem.objects.create( book_list=self.list, user=self.local_user, book=self.edition, approved=True, order=1, ) # review models.Review.objects.create( content="awesome", name="my review", rating=5, user=self.local_user, book=self.edition, ) # comment models.Comment.objects.create( content="ok so far", user=self.local_user, book=self.edition, progress=15, ) # quote models.Quotation.objects.create( content="check this out", quote="A rose by any other name", user=self.local_user, book=self.edition, ) self.job = models.BookwyrmExportJob.objects.create(user=self.local_user) # run the first stage of the export with patch("bookwyrm.models.bookwyrm_export_job.create_archive_task.delay"): models.bookwyrm_export_job.create_export_json_task(job_id=self.job.id) self.job.refresh_from_db() def test_add_book_to_user_export_job(self): """does AddBookToUserExportJob ...add the book to the export?""" self.assertIsNotNone(self.job.export_json["books"]) self.assertEqual(len(self.job.export_json["books"]), 1) book = self.job.export_json["books"][0] self.assertEqual(book["work"]["id"], self.work.remote_id) self.assertEqual(len(book["authors"]), 1) self.assertEqual(len(book["shelves"]), 1) self.assertEqual(len(book["lists"]), 1) self.assertEqual(len(book["comments"]), 1) self.assertEqual(len(book["reviews"]), 1) self.assertEqual(len(book["quotations"]), 1) self.assertEqual(len(book["readthroughs"]), 1) self.assertEqual(book["edition"]["id"], self.edition.remote_id) self.assertEqual( book["edition"]["cover"]["url"], f"images/{self.edition.cover.name}" ) def test_start_export_task(self): """test saved list task saves initial json and data""" self.assertIsNotNone(self.job.export_data) self.assertIsNotNone(self.job.export_json) self.assertEqual(self.job.export_json["name"], self.local_user.name) def test_export_saved_lists_task(self): """test export_saved_lists_task adds the saved lists""" self.assertIsNotNone(self.job.export_json["saved_lists"]) self.assertEqual( self.job.export_json["saved_lists"][0], self.saved_list.remote_id ) def test_export_follows_task(self): """test export_follows_task adds the follows""" self.assertIsNotNone(self.job.export_json["follows"]) self.assertEqual(self.job.export_json["follows"][0], self.rat_user.remote_id) def test_export_blocks_task(self): """test export_blocks_task adds the blocks""" self.assertIsNotNone(self.job.export_json["blocks"]) self.assertEqual(self.job.export_json["blocks"][0], self.badger_user.remote_id) def test_export_reading_goals_task(self): """test export_reading_goals_task adds the goals""" self.assertIsNotNone(self.job.export_json["goals"]) self.assertEqual(self.job.export_json["goals"][0]["goal"], 128937123) def test_json_export(self): """test json_export job adds settings""" self.assertIsNotNone(self.job.export_json["settings"]) self.assertFalse(self.job.export_json["settings"]["show_goal"]) self.assertEqual( self.job.export_json["settings"]["preferred_timezone"], "America/Los Angeles", ) self.assertEqual( self.job.export_json["settings"]["default_post_privacy"], "followers" ) self.assertFalse(self.job.export_json["settings"]["show_suggested_users"]) def test_get_books_for_user(self): """does get_books_for_user get all the books""" data = models.bookwyrm_export_job.get_books_for_user(self.local_user) self.assertEqual(len(data), 1) self.assertEqual(data[0].title, "Example Edition") def test_archive(self): """actually create the TAR file""" models.bookwyrm_export_job.create_archive_task(job_id=self.job.id) self.job.refresh_from_db() with ( self.job.export_data.open("rb") as tar_file, BookwyrmTarFile.open(mode="r", fileobj=tar_file) as tar, ): archive_json_file = tar.extractfile("archive.json") data = json.load(archive_json_file) # JSON from the archive should be what we want it to be self.assertEqual(data, self.job.export_json) # User avatar should be present in archive with self.local_user.avatar.open() as expected_avatar: archive_avatar = tar.extractfile(data["icon"]["url"]) self.assertEqual(expected_avatar.read(), archive_avatar.read()) # Edition cover should be present in archive with self.edition.cover.open() as expected_cover: archive_cover = tar.extractfile( data["books"][0]["edition"]["cover"]["url"] ) self.assertEqual(expected_cover.read(), archive_cover.read())