Merge branch 'main' into list-not-loading

This commit is contained in:
Mouse Reeve
2021-12-09 11:10:26 -08:00
133 changed files with 12356 additions and 3041 deletions

View File

@ -40,6 +40,8 @@ class AbstractConnector(TestCase):
class TestConnector(abstract_connector.AbstractConnector):
"""nothing added here"""
generated_remote_link_field = "openlibrary_link"
def format_search_result(self, search_result):
return search_result
@ -87,9 +89,7 @@ class AbstractConnector(TestCase):
def test_get_or_create_book_existing(self):
"""find an existing book by remote/origin id"""
self.assertEqual(models.Book.objects.count(), 1)
self.assertEqual(
self.book.remote_id, "https://%s/book/%d" % (DOMAIN, self.book.id)
)
self.assertEqual(self.book.remote_id, f"https://{DOMAIN}/book/{self.book.id}")
self.assertEqual(self.book.origin_id, "https://example.com/book/1234")
# dedupe by origin id
@ -99,7 +99,7 @@ class AbstractConnector(TestCase):
# dedupe by remote id
result = self.connector.get_or_create_book(
"https://%s/book/%d" % (DOMAIN, self.book.id)
f"https://{DOMAIN}/book/{self.book.id}"
)
self.assertEqual(models.Book.objects.count(), 1)
self.assertEqual(result, self.book)
@ -119,7 +119,8 @@ class AbstractConnector(TestCase):
@responses.activate
def test_get_or_create_author(self):
"""load an author"""
self.connector.author_mappings = [ # pylint: disable=attribute-defined-outside-init # pylint: disable=attribute-defined-outside-init
# pylint: disable=attribute-defined-outside-init
self.connector.author_mappings = [
Mapping("id"),
Mapping("name"),
]
@ -139,3 +140,26 @@ class AbstractConnector(TestCase):
author = models.Author.objects.create(name="Test Author")
result = self.connector.get_or_create_author(author.remote_id)
self.assertEqual(author, result)
@responses.activate
def test_update_author_from_remote(self):
"""trigger the function that looks up the remote data"""
author = models.Author.objects.create(name="Test", openlibrary_key="OL123A")
# pylint: disable=attribute-defined-outside-init
self.connector.author_mappings = [
Mapping("id"),
Mapping("name"),
Mapping("isni"),
]
responses.add(
responses.GET,
"https://openlibrary.org/authors/OL123A",
json={"id": "https://www.example.com/author", "name": "Beep", "isni": "hi"},
)
self.connector.update_author_from_remote(author)
author.refresh_from_db()
self.assertEqual(author.name, "Test")
self.assertEqual(author.isni, "hi")

View File

@ -1,11 +1,14 @@
""" testing book data connectors """
import json
import pathlib
from unittest.mock import patch
from django.test import TestCase
import responses
from bookwyrm import models
from bookwyrm.connectors.inventaire import Connector, get_language_code
from bookwyrm.connectors.connector_manager import ConnectorException
class Inventaire(TestCase):
@ -48,6 +51,44 @@ class Inventaire(TestCase):
self.assertEqual(result["wdt:P31"], ["wd:Q3331189"])
self.assertEqual(result["uri"], "isbn:9780375757853")
@responses.activate
def test_get_book_data_invalid(self):
"""error if there isn't any entity data"""
responses.add(
responses.GET,
"https://test.url/ok",
json={
"entities": {},
"redirects": {},
},
)
with self.assertRaises(ConnectorException):
self.connector.get_book_data("https://test.url/ok")
@responses.activate
def test_search(self):
"""min confidence filtering"""
responses.add(
responses.GET,
"https://inventaire.io/search?q=hi",
json={
"results": [
{
"_score": 200,
"label": "hello",
},
{
"_score": 100,
"label": "hi",
},
],
},
)
results = self.connector.search("hi", min_confidence=0.5)
self.assertEqual(len(results), 1)
self.assertEqual(results[0].title, "hello")
def test_format_search_result(self):
"""json to search result objs"""
search_file = pathlib.Path(__file__).parent.joinpath(
@ -157,6 +198,88 @@ class Inventaire(TestCase):
"https://covers.inventaire.io/img/entities/12345",
)
def test_isbn_search_empty(self):
"""another search type"""
search_results = {}
results = self.connector.parse_isbn_search_data(search_results)
self.assertEqual(results, [])
def test_isbn_search_no_title(self):
"""another search type"""
search_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_isbn_search.json"
)
search_results = json.loads(search_file.read_bytes())
search_results["entities"]["isbn:9782290349229"]["claims"]["wdt:P1476"] = None
result = self.connector.format_isbn_search_result(
search_results.get("entities")
)
self.assertIsNone(result)
def test_is_work_data(self):
"""is it a work"""
work_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_work.json"
)
work_data = json.loads(work_file.read_bytes())
with patch("bookwyrm.connectors.inventaire.get_data") as get_data_mock:
get_data_mock.return_value = work_data
formatted = self.connector.get_book_data("hi")
self.assertTrue(self.connector.is_work_data(formatted))
edition_file = pathlib.Path(__file__).parent.joinpath(
"../data/inventaire_edition.json"
)
edition_data = json.loads(edition_file.read_bytes())
with patch("bookwyrm.connectors.inventaire.get_data") as get_data_mock:
get_data_mock.return_value = edition_data
formatted = self.connector.get_book_data("hi")
self.assertFalse(self.connector.is_work_data(formatted))
@responses.activate
def test_get_edition_from_work_data(self):
"""load edition"""
responses.add(
responses.GET,
"https://inventaire.io/?action=by-uris&uris=hello",
json={"entities": {}},
)
data = {"uri": "blah"}
with patch(
"bookwyrm.connectors.inventaire.Connector.load_edition_data"
) as loader_mock, patch(
"bookwyrm.connectors.inventaire.Connector.get_book_data"
) as getter_mock:
loader_mock.return_value = {"uris": ["hello"]}
self.connector.get_edition_from_work_data(data)
self.assertTrue(getter_mock.called)
with patch(
"bookwyrm.connectors.inventaire.Connector.load_edition_data"
) as loader_mock:
loader_mock.return_value = {"uris": []}
with self.assertRaises(ConnectorException):
self.connector.get_edition_from_work_data(data)
@responses.activate
def test_get_work_from_edition_data(self):
"""load work"""
responses.add(
responses.GET,
"https://inventaire.io/?action=by-uris&uris=hello",
)
data = {"wdt:P629": ["hello"]}
with patch("bookwyrm.connectors.inventaire.Connector.get_book_data") as mock:
self.connector.get_work_from_edition_data(data)
self.assertEqual(mock.call_count, 1)
args = mock.call_args[0]
self.assertEqual(args[0], "https://inventaire.io?action=by-uris&uris=hello")
data = {"wdt:P629": [None]}
with self.assertRaises(ConnectorException):
self.connector.get_work_from_edition_data(data)
def test_get_language_code(self):
"""get english or whatever is in reach"""
options = {
@ -183,3 +306,11 @@ class Inventaire(TestCase):
extract = self.connector.get_description({"enwiki": "test_path"})
self.assertEqual(extract, "hi hi")
def test_remote_id_from_model(self):
"""figure out a url from an id"""
obj = models.Author.objects.create(name="hello", inventaire_id="123")
self.assertEqual(
self.connector.get_remote_id_from_model(obj),
"https://inventaire.io?action=by-uris&uris=123",
)

View File

@ -98,6 +98,9 @@ class Openlibrary(TestCase):
"type": "/type/datetime",
"value": "2008-08-31 10:09:33.413686",
},
"remote_ids": {
"isni": "000111",
},
"key": "/authors/OL453734A",
"type": {"key": "/type/author"},
"id": 1259965,
@ -110,6 +113,7 @@ class Openlibrary(TestCase):
self.assertIsInstance(result, models.Author)
self.assertEqual(result.name, "George Elliott")
self.assertEqual(result.openlibrary_key, "OL453734A")
self.assertEqual(result.isni, "000111")
def test_get_cover_url(self):
"""formats a url that should contain the cover image"""

View File

@ -19,7 +19,7 @@ from django.utils import timezone
from bookwyrm import activitypub
from bookwyrm.activitypub.base_activity import ActivityObject
from bookwyrm.models import fields, User, Status
from bookwyrm.models import fields, User, Status, Edition
from bookwyrm.models.base_model import BookWyrmModel
from bookwyrm.models.activitypub_mixin import ActivitypubMixin
from bookwyrm.settings import DOMAIN
@ -215,7 +215,7 @@ class ModelFields(TestCase):
"rat", "rat@rat.rat", "ratword", local=True, localname="rat"
)
public = "https://www.w3.org/ns/activitystreams#Public"
followers = "%s/followers" % user.remote_id
followers = f"{user.remote_id}/followers"
instance = fields.PrivacyField()
instance.name = "privacy_field"
@ -409,11 +409,10 @@ class ModelFields(TestCase):
"""loadin' a list of items from Links"""
# TODO
@responses.activate
@patch("bookwyrm.models.activitypub_mixin.ObjectMixin.broadcast")
@patch("bookwyrm.suggested_users.remove_user_task.delay")
def test_image_field(self, *_):
"""storing images"""
def test_image_field_to_activity(self, *_):
"""serialize an image field to activitypub"""
user = User.objects.create_user(
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
)
@ -437,16 +436,155 @@ class ModelFields(TestCase):
self.assertEqual(output.name, "")
self.assertEqual(output.type, "Document")
@responses.activate
def test_image_field_from_activity(self, *_):
"""load an image from activitypub"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
instance = fields.ImageField()
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=user.avatar.file.read(),
body=image.tobytes(),
status=200,
)
loaded_image = instance.field_from_activity("http://www.example.com/image.jpg")
self.assertIsInstance(loaded_image, list)
self.assertIsInstance(loaded_image[1], ContentFile)
@responses.activate
def test_image_field_set_field_from_activity(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=image.tobytes(),
status=200,
)
book = Edition.objects.create(title="hello")
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity)
self.assertIsNotNone(book.cover.name)
self.assertEqual(book.cover.size, 43200)
@responses.activate
def test_image_field_set_field_from_activity_no_overwrite_no_cover(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=image.tobytes(),
status=200,
)
book = Edition.objects.create(title="hello")
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity, overwrite=False)
self.assertIsNotNone(book.cover.name)
self.assertEqual(book.cover.size, 43200)
@responses.activate
def test_image_field_set_field_from_activity_no_overwrite_with_cover(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
another_image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/logo.png"
)
another_image = Image.open(another_image_file)
another_output = BytesIO()
another_image.save(another_output, format=another_image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=another_image.tobytes(),
status=200,
)
book = Edition.objects.create(title="hello")
book.cover.save("test.jpg", ContentFile(output.getvalue()))
self.assertEqual(book.cover.size, 2136)
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity, overwrite=False)
# same cover as before
self.assertEqual(book.cover.size, 2136)
@responses.activate
def test_image_field_set_field_from_activity_with_overwrite_with_cover(self, *_):
"""update a model instance from an activitypub object"""
image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/default_avi.jpg"
)
image = Image.open(image_file)
output = BytesIO()
image.save(output, format=image.format)
book = Edition.objects.create(title="hello")
book.cover.save("test.jpg", ContentFile(output.getvalue()))
self.assertEqual(book.cover.size, 2136)
another_image_file = pathlib.Path(__file__).parent.joinpath(
"../../static/images/logo.png"
)
another_image = Image.open(another_image_file)
another_output = BytesIO()
another_image.save(another_output, format=another_image.format)
instance = fields.ImageField(activitypub_field="cover", name="cover")
responses.add(
responses.GET,
"http://www.example.com/image.jpg",
body=another_image.tobytes(),
status=200,
)
MockActivity = namedtuple("MockActivity", ("cover"))
mock_activity = MockActivity("http://www.example.com/image.jpg")
instance.set_field_from_activity(book, mock_activity, overwrite=True)
# new cover
self.assertIsNotNone(book.cover.name)
self.assertEqual(book.cover.size, 376800)
def test_datetime_field(self, *_):
"""this one is pretty simple, it just has to use isoformat"""
instance = fields.DateTimeField()

View File

@ -1,6 +1,7 @@
""" testing models """
from unittest.mock import patch
from django.test import TestCase
from uuid import UUID
from bookwyrm import models, settings
@ -80,3 +81,12 @@ class List(TestCase):
self.assertEqual(item.book_list.privacy, "public")
self.assertEqual(item.privacy, "direct")
self.assertEqual(item.recipients, [])
def test_embed_key(self, _):
"""embed_key should never be empty"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
book_list = models.List.objects.create(
name="Test List", user=self.local_user
)
self.assertIsInstance(book_list.embed_key, UUID)

View File

@ -209,6 +209,28 @@ class BookViews(TestCase):
self.assertEqual(self.book.description, "new description hi")
self.assertEqual(self.book.last_edited_by, self.local_user)
def test_update_book_from_remote(self):
"""call out to sync with remote connector"""
models.Connector.objects.create(
identifier="openlibrary.org",
name="OpenLibrary",
connector_file="openlibrary",
base_url="https://openlibrary.org",
books_url="https://openlibrary.org",
covers_url="https://covers.openlibrary.org",
search_url="https://openlibrary.org/search?q=",
isbn_search_url="https://openlibrary.org/isbn",
)
self.local_user.groups.add(self.group)
request = self.factory.post("")
request.user = self.local_user
with patch(
"bookwyrm.connectors.openlibrary.Connector.update_book_from_remote"
) as mock:
views.update_book_from_remote(request, self.book.id, "openlibrary.org")
self.assertEqual(mock.call_count, 1)
def _setup_cover_url():
"""creates cover url mock"""

View File

@ -148,3 +148,26 @@ class AuthorViews(TestCase):
self.assertEqual(author.name, "Test Author")
validate_html(resp.render())
self.assertEqual(resp.status_code, 200)
def test_update_author_from_remote(self):
"""call out to sync with remote connector"""
author = models.Author.objects.create(name="Test Author")
models.Connector.objects.create(
identifier="openlibrary.org",
name="OpenLibrary",
connector_file="openlibrary",
base_url="https://openlibrary.org",
books_url="https://openlibrary.org",
covers_url="https://covers.openlibrary.org",
search_url="https://openlibrary.org/search?q=",
isbn_search_url="https://openlibrary.org/isbn",
)
self.local_user.groups.add(self.group)
request = self.factory.post("")
request.user = self.local_user
with patch(
"bookwyrm.connectors.openlibrary.Connector.update_author_from_remote"
) as mock:
views.update_author_from_remote(request, author.id, "openlibrary.org")
self.assertEqual(mock.call_count, 1)

View File

@ -4,10 +4,12 @@ from unittest.mock import patch
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
from bookwyrm import models, views
from bookwyrm.tests.validate_html import validate_html
@patch("bookwyrm.activitystreams.add_user_statuses_task.delay")
@ -16,6 +18,7 @@ class FollowViews(TestCase):
def setUp(self):
"""we need basic test data and mocks"""
models.SiteSettings.objects.create()
self.factory = RequestFactory()
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
@ -174,3 +177,43 @@ class FollowViews(TestCase):
self.assertEqual(models.UserFollowRequest.objects.filter(id=rel.id).count(), 0)
# follow relationship should not exist
self.assertEqual(models.UserFollows.objects.filter(id=rel.id).count(), 0)
def test_ostatus_follow_request(self, _):
"""check ostatus subscribe template loads"""
request = self.factory.get(
"", {"acct": "https%3A%2F%2Fexample.com%2Fusers%2Frat"}
)
request.user = self.local_user
result = views.ostatus_follow_request(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_remote_follow_page(self, _):
"""check remote follow page loads"""
request = self.factory.get("", {"acct": "mouse@local.com"})
request.user = self.remote_user
result = views.remote_follow_page(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_ostatus_follow_success(self, _):
"""check remote follow success page loads"""
request = self.factory.get("")
request.user = self.remote_user
request.following = "mouse@local.com"
result = views.ostatus_follow_success(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)
def test_remote_follow(self, _):
"""check follow from remote page loads"""
request = self.factory.post("", {"user": self.remote_user.id})
request.user = self.remote_user
request.remote_user = "mouse@local.com"
result = views.remote_follow(request)
self.assertIsInstance(result, TemplateResponse)
validate_html(result.render())
self.assertEqual(result.status_code, 200)

View File

@ -3,6 +3,7 @@ import json
from unittest.mock import patch
from django.contrib.auth.models import AnonymousUser
from django.http.response import Http404
from django.template.response import TemplateResponse
from django.test import TestCase
from django.test.client import RequestFactory
@ -385,3 +386,46 @@ class ListViews(TestCase):
result = view(request, self.local_user.username)
self.assertEqual(result.status_code, 302)
def test_embed_call_without_key(self):
"""there are so many views, this just makes sure it DOESNT load"""
view = views.unsafe_embed_list
request = self.factory.get("")
request.user = self.anonymous_user
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
models.ListItem.objects.create(
book_list=self.list,
user=self.local_user,
book=self.book,
approved=True,
order=1,
)
with patch("bookwyrm.views.list.is_api_request") as is_api:
is_api.return_value = False
with self.assertRaises(Http404):
result = view(request, self.list.id, "")
def test_embed_call_with_key(self):
"""there are so many views, this just makes sure it LOADS"""
view = views.unsafe_embed_list
request = self.factory.get("")
request.user = self.anonymous_user
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
models.ListItem.objects.create(
book_list=self.list,
user=self.local_user,
book=self.book,
approved=True,
order=1,
)
embed_key = str(self.list.embed_key.hex)
with patch("bookwyrm.views.list.is_api_request") as is_api:
is_api.return_value = False
result = view(request, self.list.id, embed_key)
self.assertIsInstance(result, TemplateResponse)
result.render()
self.assertEqual(result.status_code, 200)

View File

@ -50,10 +50,17 @@ class UpdateViews(TestCase):
request = self.factory.get("")
request.user = self.local_user
with patch("bookwyrm.activitystreams.ActivityStream.get_unread_count") as mock:
mock.return_value = 3
result = views.get_unread_status_count(request, "home")
with patch(
"bookwyrm.activitystreams.ActivityStream.get_unread_count"
) as mock_count:
with patch(
"bookwyrm.activitystreams.ActivityStream.get_unread_count_by_status_type"
) as mock_count_by_status:
mock_count.return_value = 3
mock_count_by_status.return_value = {"review": 5}
result = views.get_unread_status_count(request, "home")
self.assertIsInstance(result, JsonResponse)
data = json.loads(result.getvalue())
self.assertEqual(data["count"], 3)
self.assertEqual(data["count_by_type"]["review"], 5)