Merge branch 'main' into outgoing-tests
This commit is contained in:
commit
98aa946519
63 changed files with 1482 additions and 700 deletions
|
@ -103,7 +103,7 @@ class BaseActivity(TestCase):
|
|||
|
||||
def test_to_model_simple_fields(self):
|
||||
''' test setting simple fields '''
|
||||
self.assertEqual(self.user.name, '')
|
||||
self.assertIsNone(self.user.name)
|
||||
|
||||
activity = activitypub.Person(
|
||||
id=self.user.remote_id,
|
||||
|
|
|
@ -30,10 +30,10 @@ class AbstractConnector(TestCase):
|
|||
'series': ['one', 'two'],
|
||||
}
|
||||
self.connector.key_mappings = [
|
||||
Mapping('isbn_10', model=models.Edition),
|
||||
Mapping('isbn_10'),
|
||||
Mapping('isbn_13'),
|
||||
Mapping('lccn', model=models.Work),
|
||||
Mapping('asin', remote_field='ASIN'),
|
||||
Mapping('lccn'),
|
||||
Mapping('asin'),
|
||||
]
|
||||
|
||||
|
||||
|
@ -41,7 +41,6 @@ class AbstractConnector(TestCase):
|
|||
mapping = Mapping('isbn')
|
||||
self.assertEqual(mapping.local_field, 'isbn')
|
||||
self.assertEqual(mapping.remote_field, 'isbn')
|
||||
self.assertEqual(mapping.model, None)
|
||||
self.assertEqual(mapping.formatter('bb'), 'bb')
|
||||
|
||||
|
||||
|
@ -49,7 +48,6 @@ class AbstractConnector(TestCase):
|
|||
mapping = Mapping('isbn', remote_field='isbn13')
|
||||
self.assertEqual(mapping.local_field, 'isbn')
|
||||
self.assertEqual(mapping.remote_field, 'isbn13')
|
||||
self.assertEqual(mapping.model, None)
|
||||
self.assertEqual(mapping.formatter('bb'), 'bb')
|
||||
|
||||
|
||||
|
@ -59,40 +57,4 @@ class AbstractConnector(TestCase):
|
|||
self.assertEqual(mapping.local_field, 'isbn')
|
||||
self.assertEqual(mapping.remote_field, 'isbn')
|
||||
self.assertEqual(mapping.formatter, formatter)
|
||||
self.assertEqual(mapping.model, None)
|
||||
self.assertEqual(mapping.formatter('bb'), 'aabb')
|
||||
|
||||
|
||||
def test_match_from_mappings(self):
|
||||
edition = models.Edition.objects.create(
|
||||
title='Blah',
|
||||
isbn_13='blahhh',
|
||||
)
|
||||
match = self.connector.match_from_mappings(self.data, models.Edition)
|
||||
self.assertEqual(match, edition)
|
||||
|
||||
|
||||
def test_match_from_mappings_with_model(self):
|
||||
edition = models.Edition.objects.create(
|
||||
title='Blah',
|
||||
isbn_10='1234567890',
|
||||
)
|
||||
match = self.connector.match_from_mappings(self.data, models.Edition)
|
||||
self.assertEqual(match, edition)
|
||||
|
||||
|
||||
def test_match_from_mappings_with_remote(self):
|
||||
edition = models.Edition.objects.create(
|
||||
title='Blah',
|
||||
asin='A00BLAH',
|
||||
)
|
||||
match = self.connector.match_from_mappings(self.data, models.Edition)
|
||||
self.assertEqual(match, edition)
|
||||
|
||||
|
||||
def test_match_from_mappings_no_match(self):
|
||||
edition = models.Edition.objects.create(
|
||||
title='Blah',
|
||||
)
|
||||
match = self.connector.match_from_mappings(self.data, models.Edition)
|
||||
self.assertEqual(match, None)
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
''' testing book data connectors '''
|
||||
from dateutil import parser
|
||||
from django.test import TestCase
|
||||
import json
|
||||
import pathlib
|
||||
from dateutil import parser
|
||||
from django.test import TestCase
|
||||
import pytz
|
||||
|
||||
from bookwyrm import models
|
||||
from bookwyrm.connectors.openlibrary import Connector
|
||||
from bookwyrm.connectors.openlibrary import get_languages, get_description
|
||||
from bookwyrm.connectors.openlibrary import pick_default_edition, get_openlibrary_key
|
||||
from bookwyrm.connectors.abstract_connector import SearchResult, get_date
|
||||
from bookwyrm.connectors.openlibrary import pick_default_edition, \
|
||||
get_openlibrary_key
|
||||
from bookwyrm.connectors.abstract_connector import SearchResult
|
||||
|
||||
|
||||
class Openlibrary(TestCase):
|
||||
|
@ -43,7 +44,7 @@ class Openlibrary(TestCase):
|
|||
|
||||
def test_pick_default_edition(self):
|
||||
edition = pick_default_edition(self.edition_list_data['entries'])
|
||||
self.assertEqual(edition['key'], '/books/OL9952943M')
|
||||
self.assertEqual(edition['key'], '/books/OL9788823M')
|
||||
|
||||
|
||||
def test_format_search_result(self):
|
||||
|
@ -67,12 +68,6 @@ class Openlibrary(TestCase):
|
|||
self.assertEqual(description, expected)
|
||||
|
||||
|
||||
def test_get_date(self):
|
||||
date = get_date(self.work_data['first_publish_date'])
|
||||
expected = pytz.utc.localize(parser.parse('1995'))
|
||||
self.assertEqual(date, expected)
|
||||
|
||||
|
||||
def test_get_languages(self):
|
||||
languages = get_languages(self.edition_data['languages'])
|
||||
self.assertEqual(languages, ['English'])
|
||||
|
@ -81,4 +76,3 @@ class Openlibrary(TestCase):
|
|||
def test_get_ol_key(self):
|
||||
key = get_openlibrary_key('/books/OL27320736M')
|
||||
self.assertEqual(key, 'OL27320736M')
|
||||
|
||||
|
|
|
@ -25,12 +25,13 @@ class SelfConnector(TestCase):
|
|||
self.work = models.Work.objects.create(
|
||||
title='Example Work',
|
||||
)
|
||||
author = models.Author.objects.create(name='Anonymous')
|
||||
self.edition = models.Edition.objects.create(
|
||||
title='Edition of Example Work',
|
||||
author_text='Anonymous',
|
||||
published_date=datetime.datetime(1980, 5, 10, tzinfo=timezone.utc),
|
||||
parent_work=self.work,
|
||||
)
|
||||
self.edition.authors.add(author)
|
||||
models.Edition.objects.create(
|
||||
title='Another Edition',
|
||||
parent_work=self.work,
|
||||
|
@ -41,11 +42,12 @@ class SelfConnector(TestCase):
|
|||
subtitle='The Anonymous Edition',
|
||||
parent_work=self.work,
|
||||
)
|
||||
models.Edition.objects.create(
|
||||
|
||||
edition = models.Edition.objects.create(
|
||||
title='An Edition',
|
||||
author_text='Fish',
|
||||
parent_work=self.work
|
||||
)
|
||||
edition.authors.add(models.Author.objects.create(name='Fish'))
|
||||
|
||||
|
||||
def test_format_search_result(self):
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
''' testing models '''
|
||||
from dateutil.parser import parse
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import models, settings
|
||||
from bookwyrm.models.book import isbn_10_to_13, isbn_13_to_10
|
||||
|
@ -56,3 +58,27 @@ class Book(TestCase):
|
|||
isbn_13 = '978-1788-16167-1'
|
||||
isbn_10 = isbn_13_to_10(isbn_13)
|
||||
self.assertEqual(isbn_10, '178816167X')
|
||||
|
||||
|
||||
def test_get_edition_info(self):
|
||||
''' text slug about an edition '''
|
||||
book = models.Edition.objects.create(title='Test Edition')
|
||||
self.assertEqual(book.edition_info, '')
|
||||
|
||||
book.physical_format = 'worm'
|
||||
book.save()
|
||||
self.assertEqual(book.edition_info, 'worm')
|
||||
|
||||
book.languages = ['English']
|
||||
book.save()
|
||||
self.assertEqual(book.edition_info, 'worm')
|
||||
|
||||
book.languages = ['Glorbish', 'English']
|
||||
book.save()
|
||||
self.assertEqual(book.edition_info, 'worm, Glorbish language')
|
||||
|
||||
book.published_date = timezone.make_aware(parse('2020'))
|
||||
book.save()
|
||||
self.assertEqual(book.edition_info, 'worm, Glorbish language, 2020')
|
||||
self.assertEqual(
|
||||
book.alt_text, 'Test Edition cover (worm, Glorbish language, 2020)')
|
||||
|
|
|
@ -21,31 +21,23 @@ from bookwyrm.activitypub.base_activity import ActivityObject
|
|||
from bookwyrm.models import fields, User, Status
|
||||
from bookwyrm.models.base_model import ActivitypubMixin, BookWyrmModel
|
||||
|
||||
#pylint: disable=too-many-public-methods
|
||||
class ActivitypubFields(TestCase):
|
||||
''' overwrites standard model feilds to work with activitypub '''
|
||||
def test_validate_remote_id(self):
|
||||
''' should look like a url '''
|
||||
self.assertIsNone(fields.validate_remote_id(
|
||||
'http://www.example.com'
|
||||
))
|
||||
self.assertIsNone(fields.validate_remote_id(
|
||||
'https://www.example.com'
|
||||
))
|
||||
self.assertIsNone(fields.validate_remote_id(
|
||||
'http://example.com/dlfjg-23/x'
|
||||
))
|
||||
self.assertIsNone(fields.validate_remote_id('http://www.example.com'))
|
||||
self.assertIsNone(fields.validate_remote_id('https://www.example.com'))
|
||||
self.assertIsNone(fields.validate_remote_id('http://exle.com/dlg-23/x'))
|
||||
self.assertRaises(
|
||||
ValidationError, fields.validate_remote_id,
|
||||
'http:/example.com/dlfjg-23/x'
|
||||
)
|
||||
'http:/example.com/dlfjg-23/x')
|
||||
self.assertRaises(
|
||||
ValidationError, fields.validate_remote_id,
|
||||
'www.example.com/dlfjg-23/x'
|
||||
)
|
||||
'www.example.com/dlfjg-23/x')
|
||||
self.assertRaises(
|
||||
ValidationError, fields.validate_remote_id,
|
||||
'http://www.example.com/dlfjg 23/x'
|
||||
)
|
||||
'http://www.example.com/dlfjg 23/x')
|
||||
|
||||
def test_activitypub_field_mixin(self):
|
||||
''' generic mixin with super basic to and from functionality '''
|
||||
|
@ -71,6 +63,38 @@ class ActivitypubFields(TestCase):
|
|||
instance.name = 'snake_case_name'
|
||||
self.assertEqual(instance.get_activitypub_field(), 'snakeCaseName')
|
||||
|
||||
def test_set_field_from_activity(self):
|
||||
''' setter from entire json blob '''
|
||||
@dataclass
|
||||
class TestModel:
|
||||
''' real simple mock '''
|
||||
field_name: str
|
||||
|
||||
mock_model = TestModel(field_name='bip')
|
||||
TestActivity = namedtuple('test', ('fieldName', 'unrelated'))
|
||||
data = TestActivity(fieldName='hi', unrelated='bfkjh')
|
||||
|
||||
instance = fields.ActivitypubFieldMixin()
|
||||
instance.name = 'field_name'
|
||||
|
||||
instance.set_field_from_activity(mock_model, data)
|
||||
self.assertEqual(mock_model.field_name, 'hi')
|
||||
|
||||
def test_set_activity_from_field(self):
|
||||
''' set json field given entire model '''
|
||||
@dataclass
|
||||
class TestModel:
|
||||
''' real simple mock '''
|
||||
field_name: str
|
||||
unrelated: str
|
||||
mock_model = TestModel(field_name='bip', unrelated='field')
|
||||
instance = fields.ActivitypubFieldMixin()
|
||||
instance.name = 'field_name'
|
||||
|
||||
data = {}
|
||||
instance.set_activity_from_field(data, mock_model)
|
||||
self.assertEqual(data['fieldName'], 'bip')
|
||||
|
||||
def test_remote_id_field(self):
|
||||
''' just sets some defaults on charfield '''
|
||||
instance = fields.RemoteIdField()
|
||||
|
@ -369,17 +393,19 @@ class ActivitypubFields(TestCase):
|
|||
ContentFile(output.getvalue())
|
||||
)
|
||||
|
||||
output = fields.image_serializer(user.avatar)
|
||||
output = fields.image_serializer(user.avatar, alt='alt text')
|
||||
self.assertIsNotNone(
|
||||
re.match(
|
||||
r'.*\.jpg',
|
||||
output.url,
|
||||
)
|
||||
)
|
||||
self.assertEqual(output.name, 'alt text')
|
||||
self.assertEqual(output.type, 'Image')
|
||||
|
||||
instance = fields.ImageField()
|
||||
|
||||
output = fields.image_serializer(user.avatar, alt=None)
|
||||
self.assertEqual(instance.field_to_activity(user.avatar), output)
|
||||
|
||||
responses.add(
|
||||
|
@ -408,3 +434,12 @@ class ActivitypubFields(TestCase):
|
|||
''' idk why it makes them strings but probably for a good reason '''
|
||||
instance = fields.ArrayField(fields.IntegerField)
|
||||
self.assertEqual(instance.field_to_activity([0, 1]), ['0', '1'])
|
||||
|
||||
|
||||
def test_html_field(self):
|
||||
''' sanitizes html, the sanitizer has its own tests '''
|
||||
instance = fields.HtmlField()
|
||||
self.assertEqual(
|
||||
instance.field_from_activity('<marquee><p>hi</p></marquee>'),
|
||||
'<p>hi</p>'
|
||||
)
|
||||
|
|
|
@ -1,42 +1,274 @@
|
|||
''' testing models '''
|
||||
from io import BytesIO
|
||||
import pathlib
|
||||
|
||||
from PIL import Image
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import IntegrityError
|
||||
from django.test import TestCase
|
||||
from django.utils import timezone
|
||||
|
||||
from bookwyrm import models, settings
|
||||
|
||||
|
||||
class Status(TestCase):
|
||||
''' lotta types of statuses '''
|
||||
def setUp(self):
|
||||
user = models.User.objects.create_user(
|
||||
''' useful things for creating a status '''
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword', local=True)
|
||||
book = models.Edition.objects.create(title='Example Edition')
|
||||
self.book = models.Edition.objects.create(title='Test Edition')
|
||||
|
||||
models.Status.objects.create(user=user, content='Blah blah')
|
||||
models.Comment.objects.create(user=user, content='content', book=book)
|
||||
models.Quotation.objects.create(
|
||||
user=user, content='content', book=book, quote='blah')
|
||||
models.Review.objects.create(
|
||||
user=user, content='content', book=book, rating=3)
|
||||
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.book.cover.save(
|
||||
'test.jpg',
|
||||
ContentFile(output.getvalue())
|
||||
)
|
||||
|
||||
def test_status(self):
|
||||
status = models.Status.objects.first()
|
||||
def test_status_generated_fields(self):
|
||||
''' setting remote id '''
|
||||
status = models.Status.objects.create(content='bleh', user=self.user)
|
||||
expected_id = 'https://%s/user/mouse/status/%d' % \
|
||||
(settings.DOMAIN, status.id)
|
||||
self.assertEqual(status.remote_id, expected_id)
|
||||
self.assertEqual(status.privacy, 'public')
|
||||
|
||||
def test_comment(self):
|
||||
comment = models.Comment.objects.first()
|
||||
expected_id = 'https://%s/user/mouse/comment/%d' % \
|
||||
(settings.DOMAIN, comment.id)
|
||||
self.assertEqual(comment.remote_id, expected_id)
|
||||
def test_replies(self):
|
||||
''' 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)
|
||||
|
||||
def test_quotation(self):
|
||||
quotation = models.Quotation.objects.first()
|
||||
expected_id = 'https://%s/user/mouse/quotation/%d' % \
|
||||
(settings.DOMAIN, quotation.id)
|
||||
self.assertEqual(quotation.remote_id, expected_id)
|
||||
replies = models.Status.replies(parent)
|
||||
self.assertEqual(replies.count(), 2)
|
||||
self.assertEqual(replies.first(), child)
|
||||
# should select subclasses
|
||||
self.assertIsInstance(replies.last(), models.Review)
|
||||
|
||||
def test_review(self):
|
||||
review = models.Review.objects.first()
|
||||
expected_id = 'https://%s/user/mouse/review/%d' % \
|
||||
(settings.DOMAIN, review.id)
|
||||
self.assertEqual(review.remote_id, expected_id)
|
||||
def test_status_type(self):
|
||||
''' 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')
|
||||
self.assertEqual(models.Boost().status_type, 'Boost')
|
||||
|
||||
def test_boostable(self):
|
||||
''' 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)
|
||||
|
||||
def test_to_replies(self):
|
||||
''' 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)
|
||||
|
||||
def test_status_to_activity(self):
|
||||
''' 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)
|
||||
|
||||
def test_status_to_activity_tombstone(self):
|
||||
''' 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'))
|
||||
|
||||
def test_status_to_pure_activity(self):
|
||||
''' 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'], [])
|
||||
|
||||
def test_generated_note_to_activity(self):
|
||||
''' 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)
|
||||
|
||||
def test_generated_note_to_pure_activity(self):
|
||||
''' 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')
|
||||
|
||||
def test_comment_to_activity(self):
|
||||
''' 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)
|
||||
|
||||
def test_comment_to_pure_activity(self):
|
||||
''' 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'],
|
||||
'test content<p>(comment on <a href="%s">"Test Edition"</a>)</p>' %
|
||||
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')
|
||||
|
||||
def test_quotation_to_activity(self):
|
||||
''' 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)
|
||||
|
||||
def test_quotation_to_pure_activity(self):
|
||||
''' 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'],
|
||||
'a sickening sense <p>-- <a href="%s">"Test Edition"</a></p>' \
|
||||
'test content' % 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')
|
||||
|
||||
def test_review_to_activity(self):
|
||||
''' 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)
|
||||
|
||||
def test_review_to_pure_activity(self):
|
||||
''' 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')
|
||||
|
||||
def test_favorite(self):
|
||||
''' fav a status '''
|
||||
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)
|
||||
|
||||
def test_boost(self):
|
||||
''' 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))
|
||||
|
||||
def test_notification(self):
|
||||
''' 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')
|
||||
|
|
|
@ -410,7 +410,10 @@ class Incoming(TestCase):
|
|||
'actor': self.remote_user.remote_id,
|
||||
'object': self.status.to_activity(),
|
||||
}
|
||||
incoming.handle_boost(activity)
|
||||
with patch('bookwyrm.models.status.Status.ignore_activity') \
|
||||
as discarder:
|
||||
discarder.return_value = False
|
||||
incoming.handle_boost(activity)
|
||||
boost = models.Boost.objects.get()
|
||||
self.assertEqual(boost.boosted_status, self.status)
|
||||
notification = models.Notification.objects.get()
|
||||
|
@ -433,13 +436,35 @@ class Incoming(TestCase):
|
|||
boosted_status=self.status, user=self.remote_user)
|
||||
incoming.handle_unboost(activity)
|
||||
|
||||
|
||||
def test_handle_add_book(self):
|
||||
''' shelving a book '''
|
||||
book = models.Edition.objects.create(
|
||||
title='Test', remote_id='https://bookwyrm.social/book/37292')
|
||||
shelf = models.Shelf.objects.create(
|
||||
user=self.remote_user, name='Test Shelf')
|
||||
shelf.remote_id = 'https://bookwyrm.social/user/mouse/shelf/to-read'
|
||||
shelf.save()
|
||||
|
||||
activity = {
|
||||
"id": "https://bookwyrm.social/shelfbook/6189#add",
|
||||
"type": "Add",
|
||||
"actor": "hhttps://example.com/users/rat",
|
||||
"object": "https://bookwyrm.social/book/37292",
|
||||
"target": "https://bookwyrm.social/user/mouse/shelf/to-read",
|
||||
"@context": "https://www.w3.org/ns/activitystreams"
|
||||
}
|
||||
incoming.handle_add(activity)
|
||||
self.assertEqual(shelf.books.first(), book)
|
||||
|
||||
|
||||
def test_handle_update_user(self):
|
||||
''' update an existing user '''
|
||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
||||
'data/ap_user.json')
|
||||
userdata = json.loads(datafile.read_bytes())
|
||||
del userdata['icon']
|
||||
self.assertEqual(self.local_user.name, '')
|
||||
self.assertIsNone(self.local_user.name)
|
||||
incoming.handle_update_user({'object': userdata})
|
||||
user = models.User.objects.get(id=self.local_user.id)
|
||||
self.assertEqual(user.name, 'MOUSE?? MOUSE!!')
|
||||
|
@ -451,11 +476,14 @@ class Incoming(TestCase):
|
|||
'data/fr_edition.json')
|
||||
bookdata = json.loads(datafile.read_bytes())
|
||||
|
||||
models.Work.objects.create(
|
||||
title='Test Work', remote_id='https://bookwyrm.social/book/5988')
|
||||
book = models.Edition.objects.create(
|
||||
title='Test Book', remote_id='https://bookwyrm.social/book/5989')
|
||||
|
||||
del bookdata['authors']
|
||||
self.assertEqual(book.title, 'Test Book')
|
||||
|
||||
with patch(
|
||||
'bookwyrm.activitypub.base_activity.set_related_field.delay'):
|
||||
incoming.handle_update_edition({'object': bookdata})
|
||||
|
|
|
@ -1,34 +1,36 @@
|
|||
''' make sure only valid html gets to the app '''
|
||||
from django.test import TestCase
|
||||
|
||||
from bookwyrm.sanitize_html import InputHtmlParser
|
||||
|
||||
|
||||
class Sanitizer(TestCase):
|
||||
''' sanitizer tests '''
|
||||
def test_no_html(self):
|
||||
''' just text '''
|
||||
input_text = 'no html '
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual(input_text, output)
|
||||
|
||||
|
||||
def test_valid_html(self):
|
||||
''' leave the html untouched '''
|
||||
input_text = '<b>yes </b> <i>html</i>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual(input_text, output)
|
||||
|
||||
|
||||
def test_valid_html_attrs(self):
|
||||
''' and don't remove attributes '''
|
||||
input_text = '<a href="fish.com">yes </a> <i>html</i>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual(input_text, output)
|
||||
|
||||
|
||||
def test_invalid_html(self):
|
||||
''' remove all html when the html is malformed '''
|
||||
input_text = '<b>yes <i>html</i>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
|
@ -41,8 +43,8 @@ class Sanitizer(TestCase):
|
|||
output = parser.get_output()
|
||||
self.assertEqual('yes html ', output)
|
||||
|
||||
|
||||
def test_disallowed_html(self):
|
||||
''' remove disallowed html but keep allowed html '''
|
||||
input_text = '<div> yes <i>html</i></div>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
|
|
|
@ -158,34 +158,6 @@ class TemplateTags(TestCase):
|
|||
self.assertEqual(boosted, status)
|
||||
|
||||
|
||||
def test_get_edition_info(self):
|
||||
''' text slug about an edition '''
|
||||
self.assertEqual(
|
||||
bookwyrm_tags.get_edition_info(self.book), '')
|
||||
|
||||
self.book.physical_format = 'worm'
|
||||
self.book.save()
|
||||
self.assertEqual(
|
||||
bookwyrm_tags.get_edition_info(self.book), 'worm')
|
||||
|
||||
self.book.languages = ['English']
|
||||
self.book.save()
|
||||
self.assertEqual(
|
||||
bookwyrm_tags.get_edition_info(self.book), 'worm')
|
||||
|
||||
self.book.languages = ['Glorbish', 'English']
|
||||
self.book.save()
|
||||
self.assertEqual(
|
||||
bookwyrm_tags.get_edition_info(self.book),
|
||||
'worm, Glorbish language')
|
||||
|
||||
self.book.published_date = timezone.make_aware(parse('2020'))
|
||||
self.book.save()
|
||||
self.assertEqual(
|
||||
bookwyrm_tags.get_edition_info(self.book),
|
||||
'worm, Glorbish language, 2020')
|
||||
|
||||
|
||||
def test_get_book_description(self):
|
||||
''' grab it from the edition or the parent '''
|
||||
work = models.Work.objects.create(title='Test Work')
|
||||
|
|
264
bookwyrm/tests/test_view_actions.py
Normal file
264
bookwyrm/tests/test_view_actions.py
Normal file
|
@ -0,0 +1,264 @@
|
|||
''' test for app action functionality '''
|
||||
from unittest.mock import patch
|
||||
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.http.response import Http404
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from bookwyrm import view_actions as actions, models
|
||||
from bookwyrm.settings import DOMAIN
|
||||
|
||||
|
||||
#pylint: disable=too-many-public-methods
|
||||
class ViewActions(TestCase):
|
||||
''' a lot here: all handlers for receiving activitypub requests '''
|
||||
def setUp(self):
|
||||
''' we need basic things, like users '''
|
||||
self.local_user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.com', 'mouseword', local=True)
|
||||
self.local_user.remote_id = 'https://example.com/user/mouse'
|
||||
self.local_user.save()
|
||||
with patch('bookwyrm.models.user.set_remote_server.delay'):
|
||||
self.remote_user = models.User.objects.create_user(
|
||||
'rat', 'rat@rat.com', 'ratword',
|
||||
local=False,
|
||||
remote_id='https://example.com/users/rat',
|
||||
inbox='https://example.com/users/rat/inbox',
|
||||
outbox='https://example.com/users/rat/outbox',
|
||||
)
|
||||
self.status = models.Status.objects.create(
|
||||
user=self.local_user,
|
||||
content='Test status',
|
||||
remote_id='https://example.com/status/1',
|
||||
)
|
||||
self.settings = models.SiteSettings.objects.create(id=1)
|
||||
self.factory = RequestFactory()
|
||||
|
||||
|
||||
def test_register(self):
|
||||
''' create a user '''
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria-user.user_nutria',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.cccc'
|
||||
})
|
||||
with patch('bookwyrm.view_actions.login'):
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 3)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
nutria = models.User.objects.last()
|
||||
self.assertEqual(nutria.username, 'nutria-user.user_nutria@%s' % DOMAIN)
|
||||
self.assertEqual(nutria.localname, 'nutria-user.user_nutria')
|
||||
self.assertEqual(nutria.local, True)
|
||||
|
||||
def test_register_trailing_space(self):
|
||||
''' django handles this so weirdly '''
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria ',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc'
|
||||
})
|
||||
with patch('bookwyrm.view_actions.login'):
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 3)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
nutria = models.User.objects.last()
|
||||
self.assertEqual(nutria.username, 'nutria@%s' % DOMAIN)
|
||||
self.assertEqual(nutria.localname, 'nutria')
|
||||
self.assertEqual(nutria.local, True)
|
||||
|
||||
def test_register_invalid_email(self):
|
||||
''' gotta have an email '''
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa'
|
||||
})
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
self.assertEqual(response.template_name, 'login.html')
|
||||
|
||||
def test_register_invalid_username(self):
|
||||
''' gotta have an email '''
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nut@ria',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc'
|
||||
})
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
self.assertEqual(response.template_name, 'login.html')
|
||||
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutr ia',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc'
|
||||
})
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
self.assertEqual(response.template_name, 'login.html')
|
||||
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nut@ria',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc'
|
||||
})
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 2)
|
||||
self.assertEqual(response.template_name, 'login.html')
|
||||
|
||||
|
||||
def test_register_closed_instance(self):
|
||||
''' you can't just register '''
|
||||
self.settings.allow_registration = False
|
||||
self.settings.save()
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria ',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc'
|
||||
})
|
||||
with self.assertRaises(PermissionDenied):
|
||||
actions.register(request)
|
||||
|
||||
def test_register_invite(self):
|
||||
''' you can't just register '''
|
||||
self.settings.allow_registration = False
|
||||
self.settings.save()
|
||||
models.SiteInvite.objects.create(
|
||||
code='testcode', user=self.local_user, use_limit=1)
|
||||
self.assertEqual(models.SiteInvite.objects.get().times_used, 0)
|
||||
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc',
|
||||
'invite_code': 'testcode'
|
||||
})
|
||||
with patch('bookwyrm.view_actions.login'):
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 3)
|
||||
self.assertEqual(response.status_code, 302)
|
||||
self.assertEqual(models.SiteInvite.objects.get().times_used, 1)
|
||||
|
||||
# invalid invite
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria2',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc',
|
||||
'invite_code': 'testcode'
|
||||
})
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 3)
|
||||
|
||||
# bad invite code
|
||||
request = self.factory.post(
|
||||
'register/',
|
||||
{
|
||||
'username': 'nutria3',
|
||||
'password': 'mouseword',
|
||||
'email': 'aa@bb.ccc',
|
||||
'invite_code': 'dkfkdjgdfkjgkdfj'
|
||||
})
|
||||
with self.assertRaises(Http404):
|
||||
response = actions.register(request)
|
||||
self.assertEqual(models.User.objects.count(), 3)
|
||||
|
||||
|
||||
def test_password_reset_request(self):
|
||||
''' send 'em an email '''
|
||||
request = self.factory.post('', {'email': 'aa@bb.ccc'})
|
||||
resp = actions.password_reset_request(request)
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
|
||||
request = self.factory.post(
|
||||
'', {'email': 'mouse@mouse.com'})
|
||||
with patch('bookwyrm.emailing.send_email.delay'):
|
||||
resp = actions.password_reset_request(request)
|
||||
self.assertEqual(resp.template_name, 'password_reset_request.html')
|
||||
|
||||
self.assertEqual(
|
||||
models.PasswordReset.objects.get().user, self.local_user)
|
||||
|
||||
def test_password_reset(self):
|
||||
''' reset from code '''
|
||||
code = models.PasswordReset.objects.create(user=self.local_user)
|
||||
request = self.factory.post('', {
|
||||
'reset-code': code.code,
|
||||
'password': 'hi',
|
||||
'confirm-password': 'hi'
|
||||
})
|
||||
with patch('bookwyrm.view_actions.login'):
|
||||
resp = actions.password_reset(request)
|
||||
self.assertEqual(resp.status_code, 302)
|
||||
self.assertFalse(models.PasswordReset.objects.exists())
|
||||
|
||||
def test_password_reset_wrong_code(self):
|
||||
''' reset from code '''
|
||||
models.PasswordReset.objects.create(user=self.local_user)
|
||||
request = self.factory.post('', {
|
||||
'reset-code': 'jhgdkfjgdf',
|
||||
'password': 'hi',
|
||||
'confirm-password': 'hi'
|
||||
})
|
||||
resp = actions.password_reset(request)
|
||||
self.assertEqual(resp.template_name, 'password_reset.html')
|
||||
self.assertTrue(models.PasswordReset.objects.exists())
|
||||
|
||||
def test_password_reset_mismatch(self):
|
||||
''' reset from code '''
|
||||
code = models.PasswordReset.objects.create(user=self.local_user)
|
||||
request = self.factory.post('', {
|
||||
'reset-code': code.code,
|
||||
'password': 'hi',
|
||||
'confirm-password': 'hihi'
|
||||
})
|
||||
resp = actions.password_reset(request)
|
||||
self.assertEqual(resp.template_name, 'password_reset.html')
|
||||
self.assertTrue(models.PasswordReset.objects.exists())
|
||||
|
||||
def test_switch_edition(self):
|
||||
''' updates user's relationships to a book '''
|
||||
work = models.Work.objects.create(title='test work')
|
||||
edition1 = models.Edition.objects.create(
|
||||
title='first ed', parent_work=work)
|
||||
edition2 = models.Edition.objects.create(
|
||||
title='second ed', parent_work=work)
|
||||
shelf = models.Shelf.objects.create(
|
||||
name='Test Shelf', user=self.local_user)
|
||||
shelf.books.add(edition1)
|
||||
models.ReadThrough.objects.create(
|
||||
user=self.local_user, book=edition1)
|
||||
|
||||
self.assertEqual(models.ShelfBook.objects.get().book, edition1)
|
||||
self.assertEqual(models.ReadThrough.objects.get().book, edition1)
|
||||
request = self.factory.post('', {
|
||||
'edition': edition2.id
|
||||
})
|
||||
request.user = self.local_user
|
||||
with patch('bookwyrm.broadcast.broadcast_task.delay'):
|
||||
actions.switch_edition(request)
|
||||
|
||||
self.assertEqual(models.ShelfBook.objects.get().book, edition2)
|
||||
self.assertEqual(models.ReadThrough.objects.get().book, edition2)
|
Loading…
Add table
Add a link
Reference in a new issue