bookwyrm/bookwyrm/tests/activitypub/test_base_activity.py

264 lines
9.6 KiB
Python
Raw Normal View History

2021-03-08 16:49:10 +00:00
""" tests the base functionality for activitypub dataclasses """
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 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,
2023-01-20 05:31:27 +00:00
get_representative,
2021-03-08 16:49:10 +00:00
)
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
@classmethod
def setUpTestData(cls):
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"),
patch("bookwyrm.lists_stream.populate_lists_task.delay"),
):
cls.user = models.User.objects.create_user(
"mouse", "mouse@mouse.mouse", "mouseword", local=True, localname="mouse"
)
cls.user.remote_id = "http://example.com/a/b"
cls.user.save(broadcast=False, update_fields=["remote_id"])
2020-12-07 20:19:15 +00:00
def setUp(self):
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
image_path = pathlib.Path(__file__).parent.joinpath(
2021-08-02 23:05:40 +00:00
"../../static/images/default_avi.jpg"
)
with open(image_path, "rb") as image_file:
self.image_data = image_file.read()
def test_get_representative_not_existing(self, *_):
2023-01-20 07:20:18 +00:00
"""test that an instance representative actor is created if it does not exist"""
2022-01-05 14:42:54 +00:00
representative = get_representative()
self.assertIsInstance(representative, models.User)
def test_init(self, *_):
"""simple successfully 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-11-12 17:17:00 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
2021-08-02 23:05:40 +00:00
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-11-12 17:17:00 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
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",
},
{
"type": "Hashtag",
"name": "#BookClub",
"href": "http://example.com/tags/BookClub",
},
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)
hashtag = models.Hashtag.objects.filter(name="#BookClub").first()
self.assertIsNotNone(hashtag)
self.assertEqual(status.mention_hashtags.first(), hashtag)
@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"""
2021-11-12 17:17:00 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
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
with (
patch("bookwyrm.activitypub.base_activity.set_related_field.delay"),
patch("bookwyrm.models.status.Status.ignore_activity") as discarder,
):
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-11-12 17:17:00 +00:00
with patch("bookwyrm.models.activitypub_mixin.broadcast_task.apply_async"):
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)