diff --git a/bookwyrm/activitypub/base_activity.py b/bookwyrm/activitypub/base_activity.py index 93af0d0c7..a49514dc7 100644 --- a/bookwyrm/activitypub/base_activity.py +++ b/bookwyrm/activitypub/base_activity.py @@ -44,9 +44,7 @@ def naive_parse(activity_objects, activity_json): ''' this navigates circular import issues ''' try: activity_type = activity_json['type'] - print(activity_type) serializer = activity_objects[activity_type] - print(serializer) except KeyError as e: raise ActivitySerializerError(e) @@ -64,7 +62,6 @@ class ActivityObject: has a default value ''' for field in fields(self): try: - print(field.name, field.type) value = kwargs[field.name] try: is_subclass = issubclass(field.type, ActivityObject) diff --git a/bookwyrm/activitypub/verbs.py b/bookwyrm/activitypub/verbs.py index c4b302535..fee5f3624 100644 --- a/bookwyrm/activitypub/verbs.py +++ b/bookwyrm/activitypub/verbs.py @@ -23,7 +23,6 @@ class Create(Verb): def action(self): ''' create the model instance from the dataclass ''' - # check for dupes self.object.to_model() diff --git a/bookwyrm/incoming.py b/bookwyrm/incoming.py index 83d325af1..7ddd893fe 100644 --- a/bookwyrm/incoming.py +++ b/bookwyrm/incoming.py @@ -53,14 +53,6 @@ def shared_inbox(request): 'Accept': handle_follow_accept, 'Reject': handle_follow_reject, 'Block': handle_block, - 'Create': { - 'BookList': handle_create_list, - 'Note': handle_create_status, - 'Article': handle_create_status, - 'Review': handle_create_status, - 'Comment': handle_create_status, - 'Quotation': handle_create_status, - }, 'Delete': handle_delete_status, 'Like': handle_favorite, 'Announce': handle_boost, @@ -204,13 +196,6 @@ def handle_unblock(activity): block.delete() -@app.task -def handle_create_list(activity): - ''' a new list ''' - activity = activity['object'] - activitypub.BookList(**activity).to_model(models.List) - - @app.task def handle_update_list(activity): ''' update a list ''' @@ -222,32 +207,6 @@ def handle_update_list(activity): **activity['object']).to_model(models.List, instance=book_list) -@app.task -def handle_create_status(activity): - ''' someone did something, good on them ''' - # deduplicate incoming activities - activity = activity['object'] - status_id = activity.get('id') - if models.Status.objects.filter(remote_id=status_id).count(): - return - - try: - serializer = activitypub.activity_objects[activity['type']] - except KeyError: - return - - activity = serializer(**activity) - try: - model = models.activity_models[activity.type] - except KeyError: - # not a type of status we are prepared to deserialize - return - - status = activity.to_model(model) - if not status: - # it was discarded because it's not a bookwyrm type - return - @app.task def handle_delete_status(activity): diff --git a/bookwyrm/tests/test_incoming.py b/bookwyrm/tests/test_incoming.py index 9be0bb979..288ae3020 100644 --- a/bookwyrm/tests/test_incoming.py +++ b/bookwyrm/tests/test_incoming.py @@ -40,19 +40,6 @@ class Incoming(TestCase): self.factory = RequestFactory() - def test_inbox_success(self): - ''' a known type, for which we start a task ''' - request = self.factory.post( - self.local_user.shared_inbox, - '{"type": "Accept", "object": "exists"}', - content_type='application/json') - with patch('bookwyrm.incoming.has_valid_signature') as mock_has_valid: - mock_has_valid.return_value = True - - with patch('bookwyrm.incoming.handle_follow_accept.delay'): - self.assertEqual( - incoming.shared_inbox(request).status_code, 200) - def test_handle_follow(self): ''' remote user wants to follow local user ''' @@ -198,36 +185,6 @@ class Incoming(TestCase): self.assertEqual(follows.count(), 0) - def test_handle_create_list(self): - ''' a new list ''' - activity = { - 'object': { - "id": "https://example.com/list/22", - "type": "BookList", - "totalItems": 1, - "first": "https://example.com/list/22?page=1", - "last": "https://example.com/list/22?page=1", - "name": "Test List", - "owner": "https://example.com/user/mouse", - "to": [ - "https://www.w3.org/ns/activitystreams#Public" - ], - "cc": [ - "https://example.com/user/mouse/followers" - ], - "summary": "summary text", - "curation": "curated", - "@context": "https://www.w3.org/ns/activitystreams" - } - } - incoming.handle_create_list(activity) - book_list = models.List.objects.get() - self.assertEqual(book_list.name, 'Test List') - self.assertEqual(book_list.curation, 'curated') - self.assertEqual(book_list.description, 'summary text') - self.assertEqual(book_list.remote_id, 'https://example.com/list/22') - - def test_handle_update_list(self): ''' a new list ''' with patch('bookwyrm.models.activitypub_mixin.broadcast_task.delay'): @@ -262,80 +219,6 @@ class Incoming(TestCase): self.assertEqual(book_list.remote_id, 'https://example.com/list/22') - def test_handle_create_status(self): - ''' the "it justs works" mode ''' - self.assertEqual(models.Status.objects.count(), 1) - - datafile = pathlib.Path(__file__).parent.joinpath( - 'data/ap_quotation.json') - status_data = json.loads(datafile.read_bytes()) - models.Edition.objects.create( - title='Test Book', remote_id='https://example.com/book/1') - activity = {'object': status_data, 'type': 'Create'} - - incoming.handle_create_status(activity) - - status = models.Quotation.objects.get() - self.assertEqual( - status.remote_id, 'https://example.com/user/mouse/quotation/13') - self.assertEqual(status.quote, 'quote body') - self.assertEqual(status.content, 'commentary') - self.assertEqual(status.user, self.local_user) - self.assertEqual(models.Status.objects.count(), 2) - - # while we're here, lets ensure we avoid dupes - incoming.handle_create_status(activity) - self.assertEqual(models.Status.objects.count(), 2) - - def test_handle_create_status_unknown_type(self): - ''' folks send you all kinds of things ''' - activity = {'object': {'id': 'hi'}, 'type': 'Fish'} - result = incoming.handle_create_status(activity) - self.assertIsNone(result) - - def test_handle_create_status_remote_note_with_mention(self): - ''' should only create it under the right circumstances ''' - self.assertEqual(models.Status.objects.count(), 1) - self.assertFalse( - models.Notification.objects.filter(user=self.local_user).exists()) - - datafile = pathlib.Path(__file__).parent.joinpath( - 'data/ap_note.json') - status_data = json.loads(datafile.read_bytes()) - activity = {'object': status_data, 'type': 'Create'} - - incoming.handle_create_status(activity) - status = models.Status.objects.last() - self.assertEqual(status.content, 'test content in note') - self.assertEqual(status.mention_users.first(), self.local_user) - self.assertTrue( - models.Notification.objects.filter(user=self.local_user).exists()) - self.assertEqual( - models.Notification.objects.get().notification_type, 'MENTION') - - def test_handle_create_status_remote_note_with_reply(self): - ''' should only create it under the right circumstances ''' - self.assertEqual(models.Status.objects.count(), 1) - self.assertFalse( - models.Notification.objects.filter(user=self.local_user)) - - datafile = pathlib.Path(__file__).parent.joinpath( - 'data/ap_note.json') - status_data = json.loads(datafile.read_bytes()) - del status_data['tag'] - status_data['inReplyTo'] = self.status.remote_id - activity = {'object': status_data, 'type': 'Create'} - - incoming.handle_create_status(activity) - status = models.Status.objects.last() - self.assertEqual(status.content, 'test content in note') - self.assertEqual(status.reply_parent, self.status) - self.assertTrue( - models.Notification.objects.filter(user=self.local_user)) - self.assertEqual( - models.Notification.objects.get().notification_type, 'REPLY') - - def test_handle_delete_status(self): ''' remove a status ''' self.status.user = self.remote_user diff --git a/bookwyrm/tests/views/test_inbox.py b/bookwyrm/tests/views/test_inbox.py index 3bb7760e9..784f2127b 100644 --- a/bookwyrm/tests/views/test_inbox.py +++ b/bookwyrm/tests/views/test_inbox.py @@ -1,11 +1,12 @@ ''' tests incoming activities''' import json +import pathlib from unittest.mock import patch from django.http import HttpResponseNotAllowed, HttpResponseNotFound from django.test import TestCase, Client -from bookwyrm import models +from bookwyrm import models, views class Inbox(TestCase): ''' readthrough tests ''' @@ -31,6 +32,19 @@ class Inbox(TestCase): content='Test status', remote_id='https://example.com/status/1', ) + + self.create_json = { + 'id': 'hi', + 'type': 'Create', + 'actor': 'hi', + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://example.com/user/mouse/followers" + ], + 'object': {} + } models.SiteSettings.objects.create() @@ -86,34 +100,24 @@ class Inbox(TestCase): def test_inbox_success(self): ''' a known type, for which we start a task ''' - activity = { - 'id': 'hi', - 'type': 'Create', - 'actor': 'hi', + activity = self.create_json + activity['object'] = { + "id": "https://example.com/list/22", + "type": "BookList", + "totalItems": 1, + "first": "https://example.com/list/22?page=1", + "last": "https://example.com/list/22?page=1", + "name": "Test List", + "owner": "https://example.com/user/mouse", "to": [ "https://www.w3.org/ns/activitystreams#Public" ], "cc": [ "https://example.com/user/mouse/followers" ], - 'object': { - "id": "https://example.com/list/22", - "type": "BookList", - "totalItems": 1, - "first": "https://example.com/list/22?page=1", - "last": "https://example.com/list/22?page=1", - "name": "Test List", - "owner": "https://example.com/user/mouse", - "to": [ - "https://www.w3.org/ns/activitystreams#Public" - ], - "cc": [ - "https://example.com/user/mouse/followers" - ], - "summary": "summary text", - "curation": "curated", - "@context": "https://www.w3.org/ns/activitystreams" - } + "summary": "summary text", + "curation": "curated", + "@context": "https://www.w3.org/ns/activitystreams" } with patch('bookwyrm.views.inbox.has_valid_signature') as mock_valid: mock_valid.return_value = True @@ -125,3 +129,104 @@ class Inbox(TestCase): content_type="application/json" ) self.assertEqual(result.status_code, 200) + + + def test_handle_create_status(self): + ''' the "it justs works" mode ''' + self.assertEqual(models.Status.objects.count(), 1) + + datafile = pathlib.Path(__file__).parent.joinpath( + '../data/ap_quotation.json') + status_data = json.loads(datafile.read_bytes()) + models.Edition.objects.create( + title='Test Book', remote_id='https://example.com/book/1') + activity = self.create_json + activity['object'] = status_data + + views.inbox.activity_task(activity) + + status = models.Quotation.objects.get() + self.assertEqual( + status.remote_id, 'https://example.com/user/mouse/quotation/13') + self.assertEqual(status.quote, 'quote body') + self.assertEqual(status.content, 'commentary') + self.assertEqual(status.user, self.local_user) + self.assertEqual(models.Status.objects.count(), 2) + + # while we're here, lets ensure we avoid dupes + views.inbox.activity_task(activity) + self.assertEqual(models.Status.objects.count(), 2) + + + def test_handle_create_status_remote_note_with_mention(self): + ''' should only create it under the right circumstances ''' + self.assertEqual(models.Status.objects.count(), 1) + self.assertFalse( + models.Notification.objects.filter(user=self.local_user).exists()) + + datafile = pathlib.Path(__file__).parent.joinpath( + '../data/ap_note.json') + status_data = json.loads(datafile.read_bytes()) + activity = self.create_json + activity['object'] = status_data + + views.inbox.activity_task(activity) + status = models.Status.objects.last() + self.assertEqual(status.content, 'test content in note') + self.assertEqual(status.mention_users.first(), self.local_user) + self.assertTrue( + models.Notification.objects.filter(user=self.local_user).exists()) + self.assertEqual( + models.Notification.objects.get().notification_type, 'MENTION') + + def test_handle_create_status_remote_note_with_reply(self): + ''' should only create it under the right circumstances ''' + self.assertEqual(models.Status.objects.count(), 1) + self.assertFalse( + models.Notification.objects.filter(user=self.local_user)) + + datafile = pathlib.Path(__file__).parent.joinpath( + '../data/ap_note.json') + status_data = json.loads(datafile.read_bytes()) + del status_data['tag'] + status_data['inReplyTo'] = self.status.remote_id + activity = self.create_json + activity['object'] = status_data + + views.inbox.activity_task(activity) + status = models.Status.objects.last() + self.assertEqual(status.content, 'test content in note') + self.assertEqual(status.reply_parent, self.status) + self.assertTrue( + models.Notification.objects.filter(user=self.local_user)) + self.assertEqual( + models.Notification.objects.get().notification_type, 'REPLY') + + + def test_handle_create_list(self): + ''' a new list ''' + activity = self.create_json + activity['object'] = { + "id": "https://example.com/list/22", + "type": "BookList", + "totalItems": 1, + "first": "https://example.com/list/22?page=1", + "last": "https://example.com/list/22?page=1", + "name": "Test List", + "owner": "https://example.com/user/mouse", + "to": [ + "https://www.w3.org/ns/activitystreams#Public" + ], + "cc": [ + "https://example.com/user/mouse/followers" + ], + "summary": "summary text", + "curation": "curated", + "@context": "https://www.w3.org/ns/activitystreams" + } + views.inbox.activity_task(activity) + book_list = models.List.objects.get() + self.assertEqual(book_list.name, 'Test List') + self.assertEqual(book_list.curation, 'curated') + self.assertEqual(book_list.description, 'summary text') + self.assertEqual(book_list.remote_id, 'https://example.com/list/22') diff --git a/bookwyrm/views/inbox.py b/bookwyrm/views/inbox.py index fdac84466..6e7b036b3 100644 --- a/bookwyrm/views/inbox.py +++ b/bookwyrm/views/inbox.py @@ -45,7 +45,7 @@ class Inbox(View): not activity_json['type'] in activitypub.activity_objects: return HttpResponseNotFound() - activity_task.delay() + activity_task.delay(activity_json) return HttpResponse()