2020-05-10 00:10:02 +00:00
|
|
|
''' testing models '''
|
2021-02-09 20:48:46 +00:00
|
|
|
from unittest.mock import patch
|
2020-12-18 19:00:30 +00:00
|
|
|
from io import BytesIO
|
|
|
|
import pathlib
|
|
|
|
|
|
|
|
from PIL import Image
|
|
|
|
from django.core.files.base import ContentFile
|
2020-12-18 19:34:21 +00:00
|
|
|
from django.db import IntegrityError
|
2020-05-10 00:10:02 +00:00
|
|
|
from django.test import TestCase
|
2020-12-18 19:00:30 +00:00
|
|
|
from django.utils import timezone
|
2020-05-10 00:10:02 +00:00
|
|
|
|
2020-09-21 15:10:37 +00:00
|
|
|
from bookwyrm import models, settings
|
2020-05-10 00:10:02 +00:00
|
|
|
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
@patch('bookwyrm.models.Status.broadcast')
|
2020-05-10 00:10:02 +00:00
|
|
|
class Status(TestCase):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' lotta types of statuses '''
|
2020-05-10 00:10:02 +00:00
|
|
|
def setUp(self):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' useful things for creating a status '''
|
|
|
|
self.user = models.User.objects.create_user(
|
2020-12-27 23:18:48 +00:00
|
|
|
'mouse', 'mouse@mouse.mouse', 'mouseword',
|
|
|
|
local=True, localname='mouse')
|
2020-12-18 19:00:30 +00:00
|
|
|
self.book = models.Edition.objects.create(title='Test Edition')
|
2020-05-10 00:10:02 +00:00
|
|
|
|
2020-12-18 19:00:30 +00:00
|
|
|
image_file = pathlib.Path(__file__).parent.joinpath(
|
|
|
|
'../../static/images/default_avi.jpg')
|
|
|
|
image = Image.open(image_file)
|
|
|
|
output = BytesIO()
|
2021-02-09 21:06:59 +00:00
|
|
|
with patch('bookwyrm.models.Status.broadcast'):
|
|
|
|
image.save(output, format=image.format)
|
|
|
|
self.book.cover.save(
|
|
|
|
'test.jpg',
|
|
|
|
ContentFile(output.getvalue())
|
|
|
|
)
|
2020-05-10 00:10:02 +00:00
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_status_generated_fields(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' setting remote id '''
|
|
|
|
status = models.Status.objects.create(content='bleh', user=self.user)
|
2020-05-10 00:10:02 +00:00
|
|
|
expected_id = 'https://%s/user/mouse/status/%d' % \
|
|
|
|
(settings.DOMAIN, status.id)
|
2020-05-13 01:56:28 +00:00
|
|
|
self.assertEqual(status.remote_id, expected_id)
|
2020-12-18 19:00:30 +00:00
|
|
|
self.assertEqual(status.privacy, 'public')
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_replies(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' get a list of replies '''
|
|
|
|
parent = models.Status.objects.create(content='hi', user=self.user)
|
|
|
|
child = models.Status.objects.create(
|
|
|
|
content='hello', reply_parent=parent, user=self.user)
|
|
|
|
models.Review.objects.create(
|
|
|
|
content='hey', reply_parent=parent, user=self.user, book=self.book)
|
|
|
|
models.Status.objects.create(
|
|
|
|
content='hi hello', reply_parent=child, user=self.user)
|
|
|
|
|
|
|
|
replies = models.Status.replies(parent)
|
|
|
|
self.assertEqual(replies.count(), 2)
|
|
|
|
self.assertEqual(replies.first(), child)
|
|
|
|
# should select subclasses
|
|
|
|
self.assertIsInstance(replies.last(), models.Review)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_status_type(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' class name '''
|
|
|
|
self.assertEqual(models.Status().status_type, 'Note')
|
|
|
|
self.assertEqual(models.Review().status_type, 'Review')
|
|
|
|
self.assertEqual(models.Quotation().status_type, 'Quotation')
|
|
|
|
self.assertEqual(models.Comment().status_type, 'Comment')
|
2021-02-17 18:38:09 +00:00
|
|
|
self.assertEqual(models.Boost().status_type, 'Announce')
|
2020-12-18 19:00:30 +00:00
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_boostable(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' can a status be boosted, based on privacy '''
|
|
|
|
self.assertTrue(models.Status(privacy='public').boostable)
|
|
|
|
self.assertTrue(models.Status(privacy='unlisted').boostable)
|
|
|
|
self.assertFalse(models.Status(privacy='followers').boostable)
|
|
|
|
self.assertFalse(models.Status(privacy='direct').boostable)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_to_replies(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' activitypub replies collection '''
|
|
|
|
parent = models.Status.objects.create(content='hi', user=self.user)
|
|
|
|
child = models.Status.objects.create(
|
|
|
|
content='hello', reply_parent=parent, user=self.user)
|
|
|
|
models.Review.objects.create(
|
|
|
|
content='hey', reply_parent=parent, user=self.user, book=self.book)
|
|
|
|
models.Status.objects.create(
|
|
|
|
content='hi hello', reply_parent=child, user=self.user)
|
|
|
|
|
|
|
|
replies = parent.to_replies()
|
|
|
|
self.assertEqual(replies['id'], '%s/replies' % parent.remote_id)
|
|
|
|
self.assertEqual(replies['totalItems'], 2)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_status_to_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Status.objects.create(
|
|
|
|
content='test content', user=self.user)
|
|
|
|
activity = status.to_activity()
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Note')
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['sensitive'], False)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_status_to_activity_tombstone(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Status.objects.create(
|
|
|
|
content='test content', user=self.user,
|
|
|
|
deleted=True, deleted_date=timezone.now())
|
|
|
|
activity = status.to_activity()
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Tombstone')
|
|
|
|
self.assertFalse(hasattr(activity, 'content'))
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_status_to_pure_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Status.objects.create(
|
|
|
|
content='test content', user=self.user)
|
|
|
|
activity = status.to_activity(pure=True)
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Note')
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['sensitive'], False)
|
|
|
|
self.assertEqual(activity['attachment'], [])
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_generated_note_to_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.GeneratedNote.objects.create(
|
|
|
|
content='test content', user=self.user)
|
|
|
|
status.mention_books.set([self.book])
|
|
|
|
status.mention_users.set([self.user])
|
|
|
|
activity = status.to_activity()
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'GeneratedNote')
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['sensitive'], False)
|
|
|
|
self.assertEqual(len(activity['tag']), 2)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_generated_note_to_pure_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.GeneratedNote.objects.create(
|
|
|
|
content='test content', user=self.user)
|
|
|
|
status.mention_books.set([self.book])
|
|
|
|
status.mention_users.set([self.user])
|
|
|
|
activity = status.to_activity(pure=True)
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(
|
|
|
|
activity['content'],
|
|
|
|
'mouse test content <a href="%s">"Test Edition"</a>' % \
|
|
|
|
self.book.remote_id)
|
|
|
|
self.assertEqual(len(activity['tag']), 2)
|
|
|
|
self.assertEqual(activity['type'], 'Note')
|
|
|
|
self.assertEqual(activity['sensitive'], False)
|
|
|
|
self.assertIsInstance(activity['attachment'], list)
|
|
|
|
self.assertEqual(activity['attachment'][0].type, 'Image')
|
|
|
|
self.assertEqual(activity['attachment'][0].url, 'https://%s%s' % \
|
|
|
|
(settings.DOMAIN, self.book.cover.url))
|
|
|
|
self.assertEqual(
|
|
|
|
activity['attachment'][0].name, 'Test Edition cover')
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_comment_to_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Comment.objects.create(
|
|
|
|
content='test content', user=self.user, book=self.book)
|
|
|
|
activity = status.to_activity()
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Comment')
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['inReplyToBook'], self.book.remote_id)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_comment_to_pure_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Comment.objects.create(
|
|
|
|
content='test content', user=self.user, book=self.book)
|
|
|
|
activity = status.to_activity(pure=True)
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Note')
|
|
|
|
self.assertEqual(
|
|
|
|
activity['content'],
|
2020-12-20 20:50:35 +00:00
|
|
|
'test content<p>(comment on <a href="%s">"Test Edition"</a>)</p>' %
|
2020-12-18 19:00:30 +00:00
|
|
|
self.book.remote_id)
|
|
|
|
self.assertEqual(activity['attachment'][0].type, 'Image')
|
|
|
|
self.assertEqual(activity['attachment'][0].url, 'https://%s%s' % \
|
|
|
|
(settings.DOMAIN, self.book.cover.url))
|
|
|
|
self.assertEqual(
|
|
|
|
activity['attachment'][0].name, 'Test Edition cover')
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_quotation_to_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Quotation.objects.create(
|
|
|
|
quote='a sickening sense', content='test content',
|
|
|
|
user=self.user, book=self.book)
|
|
|
|
activity = status.to_activity()
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Quotation')
|
|
|
|
self.assertEqual(activity['quote'], 'a sickening sense')
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['inReplyToBook'], self.book.remote_id)
|
2020-05-10 00:10:02 +00:00
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_quotation_to_pure_activity(self, _):
|
2020-12-18 19:00:30 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Quotation.objects.create(
|
|
|
|
quote='a sickening sense', content='test content',
|
|
|
|
user=self.user, book=self.book)
|
|
|
|
activity = status.to_activity(pure=True)
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Note')
|
|
|
|
self.assertEqual(
|
|
|
|
activity['content'],
|
2020-12-20 20:50:35 +00:00
|
|
|
'a sickening sense <p>-- <a href="%s">"Test Edition"</a></p>' \
|
|
|
|
'test content' % self.book.remote_id)
|
2020-12-18 19:00:30 +00:00
|
|
|
self.assertEqual(activity['attachment'][0].type, 'Image')
|
|
|
|
self.assertEqual(activity['attachment'][0].url, 'https://%s%s' % \
|
|
|
|
(settings.DOMAIN, self.book.cover.url))
|
|
|
|
self.assertEqual(
|
|
|
|
activity['attachment'][0].name, 'Test Edition cover')
|
2020-12-18 19:34:21 +00:00
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_review_to_activity(self, _):
|
2020-12-18 19:34:21 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Review.objects.create(
|
|
|
|
name='Review name', content='test content', rating=3,
|
|
|
|
user=self.user, book=self.book)
|
|
|
|
activity = status.to_activity()
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Review')
|
|
|
|
self.assertEqual(activity['rating'], 3)
|
|
|
|
self.assertEqual(activity['name'], 'Review name')
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['inReplyToBook'], self.book.remote_id)
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_review_to_pure_activity(self, _):
|
2020-12-18 19:34:21 +00:00
|
|
|
''' subclass of the base model version with a "pure" serializer '''
|
|
|
|
status = models.Review.objects.create(
|
|
|
|
name='Review name', content='test content', rating=3,
|
|
|
|
user=self.user, book=self.book)
|
|
|
|
activity = status.to_activity(pure=True)
|
|
|
|
self.assertEqual(activity['id'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Article')
|
|
|
|
self.assertEqual(
|
|
|
|
activity['name'], 'Review of "%s" (3 stars): Review name' \
|
|
|
|
% self.book.title)
|
|
|
|
self.assertEqual(activity['content'], 'test content')
|
|
|
|
self.assertEqual(activity['attachment'][0].type, 'Image')
|
|
|
|
self.assertEqual(activity['attachment'][0].url, 'https://%s%s' % \
|
|
|
|
(settings.DOMAIN, self.book.cover.url))
|
|
|
|
self.assertEqual(
|
|
|
|
activity['attachment'][0].name, 'Test Edition cover')
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_favorite(self, _):
|
2020-12-18 19:34:21 +00:00
|
|
|
''' fav a status '''
|
2021-02-08 17:38:28 +00:00
|
|
|
real_broadcast = models.Favorite.broadcast
|
2021-02-07 05:26:39 +00:00
|
|
|
def fav_broadcast_mock(_, activity, user):
|
|
|
|
''' ok '''
|
|
|
|
self.assertEqual(user.remote_id, self.user.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Like')
|
|
|
|
models.Favorite.broadcast = fav_broadcast_mock
|
|
|
|
|
2020-12-18 19:34:21 +00:00
|
|
|
status = models.Status.objects.create(
|
|
|
|
content='test content', user=self.user)
|
|
|
|
fav = models.Favorite.objects.create(status=status, user=self.user)
|
|
|
|
|
|
|
|
# can't fav a status twice
|
|
|
|
with self.assertRaises(IntegrityError):
|
|
|
|
models.Favorite.objects.create(status=status, user=self.user)
|
|
|
|
|
|
|
|
activity = fav.to_activity()
|
|
|
|
self.assertEqual(activity['type'], 'Like')
|
|
|
|
self.assertEqual(activity['actor'], self.user.remote_id)
|
|
|
|
self.assertEqual(activity['object'], status.remote_id)
|
2021-02-08 17:38:28 +00:00
|
|
|
models.Favorite.broadcast = real_broadcast
|
2020-12-18 19:34:21 +00:00
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_boost(self, _):
|
2020-12-18 19:34:21 +00:00
|
|
|
''' boosting, this one's a bit fussy '''
|
|
|
|
status = models.Status.objects.create(
|
|
|
|
content='test content', user=self.user)
|
|
|
|
boost = models.Boost.objects.create(
|
|
|
|
boosted_status=status, user=self.user)
|
|
|
|
activity = boost.to_activity()
|
|
|
|
self.assertEqual(activity['actor'], self.user.remote_id)
|
|
|
|
self.assertEqual(activity['object'], status.remote_id)
|
|
|
|
self.assertEqual(activity['type'], 'Announce')
|
|
|
|
self.assertEqual(activity, boost.to_activity(pure=True))
|
|
|
|
|
2021-02-09 20:48:46 +00:00
|
|
|
def test_notification(self, _):
|
2020-12-18 19:34:21 +00:00
|
|
|
''' a simple model '''
|
|
|
|
notification = models.Notification.objects.create(
|
|
|
|
user=self.user, notification_type='FAVORITE')
|
|
|
|
self.assertFalse(notification.read)
|
|
|
|
|
|
|
|
with self.assertRaises(IntegrityError):
|
|
|
|
models.Notification.objects.create(
|
|
|
|
user=self.user, notification_type='GLORB')
|
2021-02-20 19:24:41 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_create_broadcast(self, broadcast_mock):
|
|
|
|
''' should send out two verions of a status on create '''
|
|
|
|
models.Comment.objects.create(
|
|
|
|
content='hi', user=self.user, book=self.book)
|
|
|
|
self.assertEqual(broadcast_mock.call_count, 2)
|
|
|
|
pure_call = broadcast_mock.call_args_list[0]
|
|
|
|
bw_call = broadcast_mock.call_args_list[1]
|
|
|
|
|
|
|
|
self.assertEqual(pure_call[1]['software'], 'other')
|
|
|
|
args = pure_call[0][0]
|
|
|
|
self.assertEqual(args['type'], 'Create')
|
|
|
|
self.assertEqual(args['object']['type'], 'Note')
|
|
|
|
self.assertTrue('content' in args['object'])
|
|
|
|
|
|
|
|
|
|
|
|
self.assertEqual(bw_call[1]['software'], 'bookwyrm')
|
|
|
|
args = bw_call[0][0]
|
|
|
|
self.assertEqual(args['type'], 'Create')
|
|
|
|
self.assertEqual(args['object']['type'], 'Comment')
|