moviewyrm/bookwyrm/tests/activitypub/test_base_activity.py

247 lines
8.9 KiB
Python
Raw Normal View History

2021-03-08 16:49:10 +00:00
""" tests the base functionality for activitypub dataclasses """
2020-12-07 20:19:15 +00:00
from io import BytesIO
2020-12-07 18:46:41 +00:00
import json
import pathlib
from unittest.mock import patch
from dataclasses import dataclass
from django.test import TestCase
2020-12-07 20:19:15 +00:00
from PIL import Image
2020-12-07 18:46:41 +00:00
import responses
2020-12-07 20:19:15 +00:00
from bookwyrm import activitypub
2021-03-08 16:49:10 +00:00
from bookwyrm.activitypub.base_activity import (
ActivityObject,
resolve_remote_id,
set_related_field,
)
from bookwyrm.activitypub import ActivitySerializerError
from bookwyrm import models
2021-03-08 16:49:10 +00:00
2021-09-06 20:53:49 +00:00
@patch("bookwyrm.activitystreams.add_status_task.delay")
@patch("bookwyrm.suggested_users.rerank_user_task.delay")
@patch("bookwyrm.suggested_users.remove_user_task.delay")
2021-08-03 19:02:47 +00:00
@patch("bookwyrm.suggested_users.rerank_suggestions_task.delay")
2021-09-06 21:50:33 +00:00
@patch("bookwyrm.activitystreams.populate_stream_task.delay")
class BaseActivity(TestCase):
2021-04-26 16:15:42 +00:00
"""the super class for model-linked activitypub dataclasses"""
2021-03-08 16:49:10 +00:00
2020-12-07 20:19:15 +00:00
def setUp(self):
2021-04-26 16:15:42 +00:00
"""we're probably going to re-use this so why copy/paste"""
with patch("bookwyrm.suggested_users.rerank_suggestions_task.delay"), patch(
"bookwyrm.activitystreams.populate_stream_task.delay"
):
self.user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
)
2021-08-02 23:05:40 +00:00
self.user.remote_id = "http://example.com/a/b"
self.user.save(broadcast=False, update_fields=["remote_id"])
2020-12-07 20:19:15 +00:00
2021-08-02 23:05:40 +00:00
datafile = pathlib.Path(__file__).parent.joinpath("../data/ap_user.json")
self.userdata = json.loads(datafile.read_bytes())
# don't try to load the user icon
del self.userdata["icon"]
2020-12-07 20:19:15 +00:00
2021-08-02 23:05:40 +00:00
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)
self.image_data = output.getvalue()
def test_init(self, *_):
2021-04-26 16:15:42 +00:00
"""simple successfuly init"""
2021-03-08 16:49:10 +00:00
instance = ActivityObject(id="a", type="b")
self.assertTrue(hasattr(instance, "id"))
self.assertTrue(hasattr(instance, "type"))
def test_init_missing(self, *_):
2021-04-26 16:15:42 +00:00
"""init with missing required params"""
with self.assertRaises(ActivitySerializerError):
ActivityObject()
def test_init_extra_fields(self, *_):
2021-04-26 16:15:42 +00:00
"""init ignoring additional fields"""
2021-03-08 16:49:10 +00:00
instance = ActivityObject(id="a", type="b", fish="c")
self.assertTrue(hasattr(instance, "id"))
self.assertTrue(hasattr(instance, "type"))
def test_init_default_field(self, *_):
2021-04-26 16:15:42 +00:00
"""replace an existing required field with a default field"""
2021-03-08 16:49:10 +00:00
@dataclass(init=False)
class TestClass(ActivityObject):
2021-04-26 16:15:42 +00:00
"""test class with default field"""
2021-03-08 16:49:10 +00:00
type: str = "TestObject"
2021-03-08 16:49:10 +00:00
instance = TestClass(id="a")
self.assertEqual(instance.id, "a")
self.assertEqual(instance.type, "TestObject")
def test_serialize(self, *_):
2021-04-26 16:15:42 +00:00
"""simple function for converting dataclass to dict"""
2021-03-08 16:49:10 +00:00
instance = ActivityObject(id="a", type="b")
serialized = instance.serialize()
self.assertIsInstance(serialized, dict)
2021-03-08 16:49:10 +00:00
self.assertEqual(serialized["id"], "a")
self.assertEqual(serialized["type"], "b")
2020-12-07 18:46:41 +00:00
@responses.activate
def test_resolve_remote_id(self, *_):
2021-04-26 16:15:42 +00:00
"""look up or load remote data"""
2020-12-07 18:46:41 +00:00
# existing item
2021-03-08 16:49:10 +00:00
result = resolve_remote_id("http://example.com/a/b", model=models.User)
2020-12-07 20:19:15 +00:00
self.assertEqual(result, self.user)
2020-12-07 18:46:41 +00:00
# remote item
responses.add(
responses.GET,
2021-03-08 16:49:10 +00:00
"https://example.com/user/mouse",
2020-12-07 20:19:15 +00:00
json=self.userdata,
2021-03-08 16:49:10 +00:00
status=200,
)
2020-12-07 18:46:41 +00:00
2021-08-02 23:05:40 +00:00
with patch("bookwyrm.models.user.set_remote_server.delay"):
result = resolve_remote_id(
"https://example.com/user/mouse", model=models.User
)
2020-12-07 18:46:41 +00:00
self.assertIsInstance(result, models.User)
2021-03-08 16:49:10 +00:00
self.assertEqual(result.remote_id, "https://example.com/user/mouse")
self.assertEqual(result.name, "MOUSE?? MOUSE!!")
2020-12-07 20:19:15 +00:00
def test_to_model_invalid_model(self, *_):
2021-04-26 16:15:42 +00:00
"""catch mismatch between activity type and model type"""
2021-03-08 16:49:10 +00:00
instance = ActivityObject(id="a", type="b")
2020-12-07 20:19:15 +00:00
with self.assertRaises(ActivitySerializerError):
instance.to_model(model=models.User)
2020-12-07 20:19:15 +00:00
@responses.activate
def test_to_model_image(self, *_):
2021-04-26 16:15:42 +00:00
"""update an image field"""
activity = activitypub.Person(
id=self.user.remote_id,
2021-03-08 16:49:10 +00:00
name="New Name",
preferredUsername="mouse",
inbox="http://www.com/",
outbox="http://www.com/",
followers="",
summary="",
publicKey={"id": "hi", "owner": self.user.remote_id, "publicKeyPem": "hi"},
endpoints={},
icon={"type": "Document", "url": "http://www.example.com/image.jpg"},
)
2020-12-07 20:19:15 +00:00
responses.add(
responses.GET,
2021-03-08 16:49:10 +00:00
"http://www.example.com/image.jpg",
body=self.image_data,
2021-03-08 16:49:10 +00:00
status=200,
)
2020-12-07 20:19:15 +00:00
self.assertIsNone(self.user.avatar.name)
with self.assertRaises(ValueError):
2021-03-08 16:49:10 +00:00
self.user.avatar.file # pylint: disable=pointless-statement
2020-12-07 20:19:15 +00:00
2021-02-08 17:38:28 +00:00
# this would trigger a broadcast because it's a local user
2021-08-02 23:05:40 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
activity.to_model(model=models.User, instance=self.user)
2020-12-07 20:19:15 +00:00
self.assertIsNotNone(self.user.avatar.file)
2021-03-08 16:49:10 +00:00
self.assertEqual(self.user.name, "New Name")
self.assertEqual(self.user.key_pair.public_key, "hi")
def test_to_model_many_to_many(self, *_):
2021-04-26 16:15:42 +00:00
"""annoying that these all need special handling"""
2021-03-08 16:49:10 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
2021-02-08 17:38:28 +00:00
status = models.Status.objects.create(
2021-03-08 16:49:10 +00:00
content="test status",
2021-02-08 17:38:28 +00:00
user=self.user,
)
2021-08-02 23:05:40 +00:00
book = models.Edition.objects.create(
title="Test Edition", remote_id="http://book.com/book"
)
update_data = activitypub.Note(
id=status.remote_id,
content=status.content,
attributedTo=self.user.remote_id,
2021-03-08 16:49:10 +00:00
published="hi",
to=[],
cc=[],
tag=[
2021-03-08 16:49:10 +00:00
{"type": "Mention", "name": "gerald", "href": "http://example.com/a/b"},
{
2021-03-08 16:49:10 +00:00
"type": "Edition",
"name": "gerald j. books",
"href": "http://book.com/book",
},
2021-03-08 16:49:10 +00:00
],
)
update_data.to_model(model=models.Status, instance=status)
self.assertEqual(status.mention_users.first(), self.user)
2020-12-12 22:15:10 +00:00
self.assertEqual(status.mention_books.first(), book)
@responses.activate
def test_to_model_one_to_many(self, *_):
2021-03-08 16:49:10 +00:00
"""these are reversed relationships, where the secondary object
keys the primary object but not vice versa"""
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
2021-02-08 17:38:28 +00:00
status = models.Status.objects.create(
2021-03-08 16:49:10 +00:00
content="test status",
2021-02-08 17:38:28 +00:00
user=self.user,
)
update_data = activitypub.Note(
id=status.remote_id,
content=status.content,
attributedTo=self.user.remote_id,
2021-03-08 16:49:10 +00:00
published="hi",
to=[],
cc=[],
2021-03-08 16:49:10 +00:00
attachment=[
{
"url": "http://www.example.com/image.jpg",
"name": "alt text",
"type": "Document",
2021-03-08 16:49:10 +00:00
}
],
)
responses.add(
responses.GET,
2021-03-08 16:49:10 +00:00
"http://www.example.com/image.jpg",
body=self.image_data,
2021-03-08 16:49:10 +00:00
status=200,
)
2020-12-09 21:11:42 +00:00
# sets the celery task call to the function call
2021-03-08 16:49:10 +00:00
with patch("bookwyrm.activitypub.base_activity.set_related_field.delay"):
with patch("bookwyrm.models.status.Status.ignore_activity") as discarder:
2021-03-07 21:13:16 +00:00
discarder.return_value = False
update_data.to_model(model=models.Status, instance=status)
2020-12-09 21:11:42 +00:00
self.assertIsNone(status.attachments.first())
@responses.activate
def test_set_related_field(self, *_):
2021-04-26 16:15:42 +00:00
"""celery task to add back-references to created objects"""
2021-03-08 16:49:10 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.delay"):
2021-02-08 17:38:28 +00:00
status = models.Status.objects.create(
2021-03-08 16:49:10 +00:00
content="test status",
2021-02-08 17:38:28 +00:00
user=self.user,
)
2020-12-09 21:11:42 +00:00
data = {
2021-03-08 16:49:10 +00:00
"url": "http://www.example.com/image.jpg",
"name": "alt text",
"type": "Document",
2020-12-09 21:11:42 +00:00
}
responses.add(
responses.GET,
2021-03-08 16:49:10 +00:00
"http://www.example.com/image.jpg",
2020-12-09 21:11:42 +00:00
body=self.image_data,
2021-03-08 16:49:10 +00:00
status=200,
)
set_related_field("Image", "Status", "status", status.remote_id, data)
2020-12-09 21:11:42 +00:00
self.assertIsInstance(status.attachments.first(), models.Image)
2020-12-09 21:11:42 +00:00
self.assertIsNotNone(status.attachments.first().image)