rename main code directory
This commit is contained in:
1
bookwyrm/tests/__init__.py
Normal file
1
bookwyrm/tests/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import *
|
1
bookwyrm/tests/activitypub/__init__.py
Normal file
1
bookwyrm/tests/activitypub/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import *
|
27
bookwyrm/tests/activitypub/test_author.py
Normal file
27
bookwyrm/tests/activitypub/test_author.py
Normal file
@ -0,0 +1,27 @@
|
||||
import datetime
|
||||
|
||||
from django.test import TestCase
|
||||
from fedireads import models
|
||||
|
||||
|
||||
class Author(TestCase):
|
||||
def setUp(self):
|
||||
self.book = models.Edition.objects.create(
|
||||
title='Example Edition',
|
||||
remote_id='https://example.com/book/1',
|
||||
)
|
||||
self.author = models.Author.objects.create(
|
||||
name='Author fullname',
|
||||
first_name='Auth',
|
||||
last_name='Or',
|
||||
aliases=['One', 'Two'],
|
||||
bio='bio bio bio',
|
||||
)
|
||||
|
||||
|
||||
def test_serialize_model(self):
|
||||
activity = self.author.to_activity()
|
||||
self.assertEqual(activity['id'], self.author.remote_id)
|
||||
self.assertIsInstance(activity['aliases'], list)
|
||||
self.assertEqual(activity['aliases'], ['One', 'Two'])
|
||||
self.assertEqual(activity['name'], 'Author fullname')
|
32
bookwyrm/tests/activitypub/test_person.py
Normal file
32
bookwyrm/tests/activitypub/test_person.py
Normal file
@ -0,0 +1,32 @@
|
||||
import json
|
||||
import pathlib
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import activitypub, models
|
||||
|
||||
|
||||
class Person(TestCase):
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'rat', 'rat@rat.com', 'ratword',
|
||||
)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/ap_user.json'
|
||||
)
|
||||
self.user_data = json.loads(datafile.read_bytes())
|
||||
|
||||
|
||||
def test_load_user_data(self):
|
||||
activity = activitypub.Person(**self.user_data)
|
||||
self.assertEqual(activity.id, 'https://example.com/user/mouse')
|
||||
self.assertEqual(activity.preferredUsername, 'mouse')
|
||||
self.assertEqual(activity.type, 'Person')
|
||||
|
||||
|
||||
def test_serialize_model(self):
|
||||
activity = self.user.to_activity()
|
||||
self.assertEqual(activity['id'], self.user.remote_id)
|
||||
self.assertEqual(
|
||||
activity['endpoints'],
|
||||
{'sharedInbox': self.user.shared_inbox}
|
||||
)
|
46
bookwyrm/tests/activitypub/test_quotation.py
Normal file
46
bookwyrm/tests/activitypub/test_quotation.py
Normal file
@ -0,0 +1,46 @@
|
||||
import json
|
||||
import pathlib
|
||||
|
||||
from django.test import TestCase
|
||||
from fedireads import activitypub, models
|
||||
|
||||
|
||||
class Quotation(TestCase):
|
||||
''' we have hecka ways to create statuses '''
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword',
|
||||
local=False,
|
||||
inbox='https://example.com/user/mouse/inbox',
|
||||
outbox='https://example.com/user/mouse/outbox',
|
||||
remote_id='https://example.com/user/mouse',
|
||||
)
|
||||
self.book = models.Edition.objects.create(
|
||||
title='Example Edition',
|
||||
remote_id='https://example.com/book/1',
|
||||
)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/ap_quotation.json'
|
||||
)
|
||||
self.status_data = json.loads(datafile.read_bytes())
|
||||
|
||||
|
||||
def test_quotation_activity(self):
|
||||
quotation = activitypub.Quotation(**self.status_data)
|
||||
|
||||
self.assertEqual(quotation.type, 'Quotation')
|
||||
self.assertEqual(
|
||||
quotation.id, 'https://example.com/user/mouse/quotation/13')
|
||||
self.assertEqual(quotation.content, 'commentary')
|
||||
self.assertEqual(quotation.quote, 'quote body')
|
||||
self.assertEqual(quotation.inReplyToBook, 'https://example.com/book/1')
|
||||
self.assertEqual(
|
||||
quotation.published, '2020-05-10T02:38:31.150343+00:00')
|
||||
|
||||
|
||||
def test_activity_to_model(self):
|
||||
activity = activitypub.Quotation(**self.status_data)
|
||||
quotation = activity.to_model(models.Quotation)
|
||||
|
||||
self.assertEqual(quotation.book, self.book)
|
||||
self.assertEqual(quotation.user, self.user)
|
1
bookwyrm/tests/connectors/__init__.py
Normal file
1
bookwyrm/tests/connectors/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import *
|
122
bookwyrm/tests/connectors/test_abstract_connector.py
Normal file
122
bookwyrm/tests/connectors/test_abstract_connector.py
Normal file
@ -0,0 +1,122 @@
|
||||
''' testing book data connectors '''
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads.connectors.abstract_connector import Mapping,\
|
||||
update_from_mappings
|
||||
from fedireads.connectors.fedireads_connector import Connector
|
||||
|
||||
|
||||
class FedireadsConnector(TestCase):
|
||||
def setUp(self):
|
||||
self.book = models.Edition.objects.create(title='Example Edition')
|
||||
|
||||
models.Connector.objects.create(
|
||||
identifier='example.com',
|
||||
connector_file='fedireads_connector',
|
||||
base_url='https://example.com',
|
||||
books_url='https:/example.com',
|
||||
covers_url='https://example.com',
|
||||
search_url='https://example.com/search?q=',
|
||||
)
|
||||
self.connector = Connector('example.com')
|
||||
|
||||
self.data = {
|
||||
'title': 'Unused title',
|
||||
'ASIN': 'A00BLAH',
|
||||
'isbn_10': '1234567890',
|
||||
'isbn_13': 'blahhh',
|
||||
'blah': 'bip',
|
||||
'format': 'hardcover',
|
||||
'series': ['one', 'two'],
|
||||
}
|
||||
self.connector.key_mappings = [
|
||||
Mapping('isbn_10', model=models.Edition),
|
||||
Mapping('isbn_13'),
|
||||
Mapping('lccn', model=models.Work),
|
||||
Mapping('asin', remote_field='ASIN'),
|
||||
]
|
||||
|
||||
|
||||
def test_create_mapping(self):
|
||||
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')
|
||||
|
||||
|
||||
def test_create_mapping_with_remote(self):
|
||||
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')
|
||||
|
||||
|
||||
def test_create_mapping_with_formatter(self):
|
||||
formatter = lambda x: 'aa' + x
|
||||
mapping = Mapping('isbn', formatter=formatter)
|
||||
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_update_from_mappings(self):
|
||||
data = {
|
||||
'title': 'Unused title',
|
||||
'isbn_10': '1234567890',
|
||||
'isbn_13': 'blahhh',
|
||||
'blah': 'bip',
|
||||
'format': 'hardcover',
|
||||
'series': ['one', 'two'],
|
||||
}
|
||||
mappings = [
|
||||
Mapping('isbn_10'),
|
||||
Mapping('blah'),# not present on self.book
|
||||
Mapping('physical_format', remote_field='format'),
|
||||
Mapping('series', formatter=lambda x: x[0]),
|
||||
]
|
||||
book = update_from_mappings(self.book, data, mappings)
|
||||
self.assertEqual(book.title, 'Example Edition')
|
||||
self.assertEqual(book.isbn_10, '1234567890')
|
||||
self.assertEqual(book.isbn_13, None)
|
||||
self.assertEqual(book.physical_format, 'hardcover')
|
||||
self.assertEqual(book.series, 'one')
|
||||
|
||||
|
||||
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)
|
64
bookwyrm/tests/connectors/test_fedireads_connector.py
Normal file
64
bookwyrm/tests/connectors/test_fedireads_connector.py
Normal file
@ -0,0 +1,64 @@
|
||||
''' testing book data connectors '''
|
||||
from dateutil import parser
|
||||
from django.test import TestCase
|
||||
import json
|
||||
import pathlib
|
||||
|
||||
from fedireads import models
|
||||
from fedireads.connectors.fedireads_connector import Connector
|
||||
from fedireads.connectors.abstract_connector import SearchResult, get_date
|
||||
|
||||
|
||||
class FedireadsConnector(TestCase):
|
||||
def setUp(self):
|
||||
models.Connector.objects.create(
|
||||
identifier='example.com',
|
||||
connector_file='fedireads_connector',
|
||||
base_url='https://example.com',
|
||||
books_url='https://example.com',
|
||||
covers_url='https://example.com/images/covers',
|
||||
search_url='https://example.com/search?q=',
|
||||
)
|
||||
self.connector = Connector('example.com')
|
||||
|
||||
work_file = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/fr_work.json')
|
||||
edition_file = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/fr_edition.json')
|
||||
self.work_data = json.loads(work_file.read_bytes())
|
||||
self.edition_data = json.loads(edition_file.read_bytes())
|
||||
|
||||
|
||||
def test_is_work_data(self):
|
||||
self.assertEqual(self.connector.is_work_data(self.work_data), True)
|
||||
self.assertEqual(self.connector.is_work_data(self.edition_data), False)
|
||||
|
||||
|
||||
def test_get_edition_from_work_data(self):
|
||||
edition = self.connector.get_edition_from_work_data(self.work_data)
|
||||
self.assertEqual(edition['url'], 'https://example.com/book/122')
|
||||
|
||||
|
||||
def test_get_work_from_edition_data(self):
|
||||
work = self.connector.get_work_from_edition_date(self.edition_data)
|
||||
self.assertEqual(work['url'], 'https://example.com/book/121')
|
||||
|
||||
|
||||
def test_format_search_result(self):
|
||||
datafile = pathlib.Path(__file__).parent.joinpath('../data/fr_search.json')
|
||||
search_data = json.loads(datafile.read_bytes())
|
||||
results = self.connector.parse_search_data(search_data)
|
||||
self.assertIsInstance(results, list)
|
||||
|
||||
result = self.connector.format_search_result(results[0])
|
||||
self.assertIsInstance(result, SearchResult)
|
||||
self.assertEqual(result.title, 'Jonathan Strange and Mr Norrell')
|
||||
self.assertEqual(result.key, 'https://example.com/book/122')
|
||||
self.assertEqual(result.author, 'Susanna Clarke')
|
||||
self.assertEqual(result.year, 2017)
|
||||
|
||||
|
||||
def test_get_date(self):
|
||||
date = get_date(self.edition_data['published_date'])
|
||||
expected = parser.parse("2017-05-10T00:00:00+00:00")
|
||||
self.assertEqual(date, expected)
|
84
bookwyrm/tests/connectors/test_openlibrary_connector.py
Normal file
84
bookwyrm/tests/connectors/test_openlibrary_connector.py
Normal file
@ -0,0 +1,84 @@
|
||||
''' testing book data connectors '''
|
||||
from dateutil import parser
|
||||
from django.test import TestCase
|
||||
import json
|
||||
import pathlib
|
||||
import pytz
|
||||
|
||||
from fedireads import models
|
||||
from fedireads.connectors.openlibrary import Connector
|
||||
from fedireads.connectors.openlibrary import get_languages, get_description
|
||||
from fedireads.connectors.openlibrary import pick_default_edition, get_openlibrary_key
|
||||
from fedireads.connectors.abstract_connector import SearchResult, get_date
|
||||
|
||||
|
||||
class Openlibrary(TestCase):
|
||||
def setUp(self):
|
||||
models.Connector.objects.create(
|
||||
identifier='openlibrary.org',
|
||||
name='OpenLibrary',
|
||||
connector_file='openlibrary',
|
||||
base_url='https://openlibrary.org',
|
||||
books_url='https://openlibrary.org',
|
||||
covers_url='https://covers.openlibrary.org',
|
||||
search_url='https://openlibrary.org/search?q=',
|
||||
)
|
||||
self.connector = Connector('openlibrary.org')
|
||||
|
||||
work_file = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/ol_work.json')
|
||||
edition_file = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/ol_edition.json')
|
||||
edition_list_file = pathlib.Path(__file__).parent.joinpath(
|
||||
'../data/ol_edition_list.json')
|
||||
self.work_data = json.loads(work_file.read_bytes())
|
||||
self.edition_data = json.loads(edition_file.read_bytes())
|
||||
self.edition_list_data = json.loads(edition_list_file.read_bytes())
|
||||
|
||||
|
||||
def test_is_work_data(self):
|
||||
self.assertEqual(self.connector.is_work_data(self.work_data), True)
|
||||
self.assertEqual(self.connector.is_work_data(self.edition_data), False)
|
||||
|
||||
|
||||
def test_pick_default_edition(self):
|
||||
edition = pick_default_edition(self.edition_list_data['entries'])
|
||||
self.assertEqual(edition['key'], '/books/OL9952943M')
|
||||
|
||||
|
||||
def test_format_search_result(self):
|
||||
''' translate json from openlibrary into SearchResult '''
|
||||
datafile = pathlib.Path(__file__).parent.joinpath('../data/ol_search.json')
|
||||
search_data = json.loads(datafile.read_bytes())
|
||||
results = self.connector.parse_search_data(search_data)
|
||||
self.assertIsInstance(results, list)
|
||||
|
||||
result = self.connector.format_search_result(results[0])
|
||||
self.assertIsInstance(result, SearchResult)
|
||||
self.assertEqual(result.title, 'This Is How You Lose the Time War')
|
||||
self.assertEqual(result.key, 'https://openlibrary.org/works/OL20639540W')
|
||||
self.assertEqual(result.author, 'Amal El-Mohtar, Max Gladstone')
|
||||
self.assertEqual(result.year, 2019)
|
||||
|
||||
|
||||
def test_get_description(self):
|
||||
description = get_description(self.work_data['description'])
|
||||
expected = 'First in the Old Kingdom/Abhorsen series.'
|
||||
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'])
|
||||
|
||||
|
||||
def test_get_ol_key(self):
|
||||
key = get_openlibrary_key('/books/OL27320736M')
|
||||
self.assertEqual(key, 'OL27320736M')
|
||||
|
75
bookwyrm/tests/connectors/test_self_connector.py
Normal file
75
bookwyrm/tests/connectors/test_self_connector.py
Normal file
@ -0,0 +1,75 @@
|
||||
''' testing book data connectors '''
|
||||
import datetime
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads.connectors.self_connector import Connector
|
||||
from fedireads.settings import DOMAIN
|
||||
|
||||
|
||||
class SelfConnector(TestCase):
|
||||
def setUp(self):
|
||||
models.Connector.objects.create(
|
||||
identifier=DOMAIN,
|
||||
name='Local',
|
||||
local=True,
|
||||
connector_file='self_connector',
|
||||
base_url='https://%s' % DOMAIN,
|
||||
books_url='https://%s/book' % DOMAIN,
|
||||
covers_url='https://%s/images/covers' % DOMAIN,
|
||||
search_url='https://%s/search?q=' % DOMAIN,
|
||||
priority=1,
|
||||
)
|
||||
self.connector = Connector(DOMAIN)
|
||||
self.work = models.Work.objects.create(
|
||||
title='Example Work',
|
||||
)
|
||||
self.edition = models.Edition.objects.create(
|
||||
title='Edition of Example Work',
|
||||
author_text='Anonymous',
|
||||
published_date=datetime.datetime(1980, 5, 10),
|
||||
parent_work=self.work,
|
||||
)
|
||||
models.Edition.objects.create(
|
||||
title='Another Edition',
|
||||
parent_work=self.work,
|
||||
series='Anonymous'
|
||||
)
|
||||
models.Edition.objects.create(
|
||||
title='More Editions',
|
||||
subtitle='The Anonymous Edition',
|
||||
parent_work=self.work,
|
||||
)
|
||||
models.Edition.objects.create(
|
||||
title='An Edition',
|
||||
author_text='Fish',
|
||||
parent_work=self.work
|
||||
)
|
||||
|
||||
|
||||
def test_format_search_result(self):
|
||||
result = self.connector.format_search_result(self.edition)
|
||||
self.assertEqual(result.title, 'Edition of Example Work')
|
||||
self.assertEqual(result.key, self.edition.remote_id)
|
||||
self.assertEqual(result.author, 'Anonymous')
|
||||
self.assertEqual(result.year, 1980)
|
||||
|
||||
|
||||
def test_search_rank(self):
|
||||
results = self.connector.search('Anonymous')
|
||||
self.assertEqual(len(results), 3)
|
||||
self.assertEqual(results[0].title, 'Edition of Example Work')
|
||||
self.assertEqual(results[1].title, 'More Editions')
|
||||
self.assertEqual(results[2].title, 'Another Edition')
|
||||
|
||||
|
||||
def test_search_default_filter(self):
|
||||
self.edition.default = True
|
||||
self.edition.save()
|
||||
results = self.connector.search('Anonymous')
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].title, 'Edition of Example Work')
|
||||
|
||||
results = self.connector.search('Fish')
|
||||
self.assertEqual(len(results), 1)
|
||||
self.assertEqual(results[0].title, 'An Edition')
|
29
bookwyrm/tests/data/ap_comment.json
Normal file
29
bookwyrm/tests/data/ap_comment.json
Normal file
@ -0,0 +1,29 @@
|
||||
{
|
||||
"id": "https://example.com/user/mouse/comment/6",
|
||||
"url": "https://example.com/user/mouse/comment/6",
|
||||
"inReplyTo": null,
|
||||
"published": "2020-05-08T23:45:44.768012+00:00",
|
||||
"attributedTo": "https://example.com/user/mouse",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"https://example.com/user/mouse/followers"
|
||||
],
|
||||
"sensitive": null,
|
||||
"content": "commentary",
|
||||
"type": "Comment",
|
||||
"attachment": [],
|
||||
"replies": {
|
||||
"id": "https://example.com/user/mouse/comment/6/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "https://example.com/user/mouse/comment/6/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "https://example.com/user/mouse/comment/6/replies",
|
||||
"items": []
|
||||
}
|
||||
},
|
||||
"inReplyToBook": "https://example.com/book/1"
|
||||
}
|
||||
|
36
bookwyrm/tests/data/ap_quotation.json
Normal file
36
bookwyrm/tests/data/ap_quotation.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"id": "https://example.com/user/mouse/quotation/13",
|
||||
"url": "https://example.com/user/mouse/quotation/13",
|
||||
"inReplyTo": null,
|
||||
"published": "2020-05-10T02:38:31.150343+00:00",
|
||||
"attributedTo": "https://example.com/user/mouse",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"https://example.com/user/mouse/followers"
|
||||
],
|
||||
"sensitive": false,
|
||||
"content": "commentary",
|
||||
"type": "Quotation",
|
||||
"attachment": [
|
||||
{
|
||||
"type": "Document",
|
||||
"mediaType": "image//images/covers/2b4e4712-5a4d-4ac1-9df4-634cc9c7aff3jpg",
|
||||
"url": "https://example.com/images/covers/2b4e4712-5a4d-4ac1-9df4-634cc9c7aff3jpg",
|
||||
"name": "Cover of \"This Is How You Lose the Time War\""
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "https://example.com/user/mouse/quotation/13/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "https://example.com/user/mouse/quotation/13/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "https://example.com/user/mouse/quotation/13/replies",
|
||||
"items": []
|
||||
}
|
||||
},
|
||||
"inReplyToBook": "https://example.com/book/1",
|
||||
"quote": "quote body"
|
||||
}
|
36
bookwyrm/tests/data/ap_user.json
Normal file
36
bookwyrm/tests/data/ap_user.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
"https://w3id.org/security/v1",
|
||||
{
|
||||
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||
"schema": "http://schema.org#",
|
||||
"PropertyValue": "schema:PropertyValue",
|
||||
"value": "schema:value"
|
||||
}
|
||||
],
|
||||
"id": "https://example.com/user/mouse",
|
||||
"type": "Person",
|
||||
"preferredUsername": "mouse",
|
||||
"name": "MOUSE?? MOUSE!!",
|
||||
"inbox": "https://example.com/user/mouse/inbox",
|
||||
"outbox": "https://example.com/user/mouse/outbox",
|
||||
"followers": "https://example.com/user/mouse/followers",
|
||||
"following": "https://example.com/user/mouse/following",
|
||||
"summary": "",
|
||||
"publicKey": {
|
||||
"id": "https://example.com/user/mouse/#main-key",
|
||||
"owner": "https://example.com/user/mouse",
|
||||
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC6QisDrjOQvkRo/MqNmSYPwqtt\nCxg/8rCW+9jKbFUKvqjTeKVotEE85122v/DCvobCCdfQuYIFdVMk+dB1xJ0iPGPg\nyU79QHY22NdV9mFKA2qtXVVxb5cxpA4PlwOHM6PM/k8B+H09OUrop2aPUAYwy+vg\n+MXyz8bAXrIS1kq6fQIDAQAB\n-----END PUBLIC KEY-----"
|
||||
},
|
||||
"endpoints": {
|
||||
"sharedInbox": "https://example.com/inbox"
|
||||
},
|
||||
"fedireadsUser": true,
|
||||
"manuallyApprovesFollowers": false,
|
||||
"icon": {
|
||||
"type": "Image",
|
||||
"mediaType": "image/png",
|
||||
"url": "https://example.com/images/avatars/AL-2-crop-50.png"
|
||||
}
|
||||
}
|
42
bookwyrm/tests/data/fr_edition.json
Normal file
42
bookwyrm/tests/data/fr_edition.json
Normal file
@ -0,0 +1,42 @@
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Document",
|
||||
"book_type": "Edition",
|
||||
"name": "Jonathan Strange and Mr Norrell",
|
||||
"url": "https://example.com/book/122",
|
||||
"authors": [
|
||||
"https://example.com/author/25"
|
||||
],
|
||||
"published_date": "2017-05-10T00:00:00+00:00",
|
||||
"work": {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Document",
|
||||
"book_type": "Work",
|
||||
"name": "Jonathan Strange and Mr Norrell",
|
||||
"url": "https://example.com/book/121",
|
||||
"authors": [
|
||||
"https://example.com/author/25"
|
||||
],
|
||||
"title": "Jonathan Strange and Mr Norrell",
|
||||
"attachment": [
|
||||
{
|
||||
"type": "Document",
|
||||
"mediaType": "image/jpg",
|
||||
"url": "https://example.com/images/covers/8775540-M.jpg",
|
||||
"name": "Cover of \"Jonathan Strange and Mr Norrell\""
|
||||
}
|
||||
]
|
||||
},
|
||||
"title": "Jonathan Strange and Mr Norrell",
|
||||
"subtitle": "Bloomsbury Modern Classics",
|
||||
"isbn_13": "9781408891469",
|
||||
"physical_format": "paperback",
|
||||
"attachment": [
|
||||
{
|
||||
"type": "Document",
|
||||
"mediaType": "image/jpg",
|
||||
"url": "https://example.com/images/covers/9155821-M.jpg",
|
||||
"name": "Cover of \"Jonathan Strange and Mr Norrell\""
|
||||
}
|
||||
]
|
||||
}
|
1
bookwyrm/tests/data/fr_search.json
Normal file
1
bookwyrm/tests/data/fr_search.json
Normal file
@ -0,0 +1 @@
|
||||
[{"title": "Jonathan Strange and Mr Norrell", "key": "https://example.com/book/122", "author": "Susanna Clarke", "year": 2017}]
|
44
bookwyrm/tests/data/fr_work.json
Normal file
44
bookwyrm/tests/data/fr_work.json
Normal file
@ -0,0 +1,44 @@
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Document",
|
||||
"book_type": "Work",
|
||||
"name": "Jonathan Strange and Mr Norrell",
|
||||
"url": "https://example.com/book/121",
|
||||
"authors": [
|
||||
"https://example.com/author/25"
|
||||
],
|
||||
"editions": [
|
||||
{
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
"type": "Document",
|
||||
"book_type": "Edition",
|
||||
"name": "Jonathan Strange and Mr Norrell",
|
||||
"url": "https://example.com/book/122",
|
||||
"authors": [
|
||||
"https://example.com/author/25"
|
||||
],
|
||||
"published_date": "2017-05-10T00:00:00+00:00",
|
||||
"title": "Jonathan Strange and Mr Norrell",
|
||||
"subtitle": "Bloomsbury Modern Classics",
|
||||
"isbn_13": "9781408891469",
|
||||
"physical_format": "paperback",
|
||||
"attachment": [
|
||||
{
|
||||
"type": "Document",
|
||||
"mediaType": "image/jpg",
|
||||
"url": "https://example.com/images/covers/9155821-M.jpg",
|
||||
"name": "Cover of \"Jonathan Strange and Mr Norrell\""
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Jonathan Strange and Mr Norrell",
|
||||
"attachment": [
|
||||
{
|
||||
"type": "Document",
|
||||
"mediaType": "image/jpg",
|
||||
"url": "https://example.com/images/covers/8775540-M.jpg",
|
||||
"name": "Cover of \"Jonathan Strange and Mr Norrell\""
|
||||
}
|
||||
]
|
||||
}
|
83
bookwyrm/tests/data/ol_edition.json
Normal file
83
bookwyrm/tests/data/ol_edition.json
Normal file
@ -0,0 +1,83 @@
|
||||
{
|
||||
"identifiers": {
|
||||
"librarything": [
|
||||
"10014"
|
||||
],
|
||||
"goodreads": [
|
||||
"535197",
|
||||
"1102517",
|
||||
"518848"
|
||||
]
|
||||
},
|
||||
"lc_classifications": [
|
||||
"PZ7.N647 Sab 1995"
|
||||
],
|
||||
"latest_revision": 7,
|
||||
"ocaid": "sabriel00nixg",
|
||||
"ia_box_id": [
|
||||
"IA107202"
|
||||
],
|
||||
"edition_name": "1st American ed.",
|
||||
"title": "Sabriel",
|
||||
"languages": [
|
||||
{
|
||||
"key": "/languages/eng"
|
||||
}
|
||||
],
|
||||
"subjects": [
|
||||
"Fantasy."
|
||||
],
|
||||
"publish_country": "nyu",
|
||||
"by_statement": "Garth Nix.",
|
||||
"type": {
|
||||
"key": "/type/edition"
|
||||
},
|
||||
"revision": 7,
|
||||
"publishers": [
|
||||
"Harper Trophy"
|
||||
],
|
||||
"description": {
|
||||
"type": "/type/text",
|
||||
"value": "Sabriel, daughter of the necromancer Abhorsen, must journey into the mysterious and magical Old Kingdom to rescue her father from the Land of the Dead."
|
||||
},
|
||||
"last_modified": {
|
||||
"type": "/type/datetime",
|
||||
"value": "2017-10-08T21:20:07.665236"
|
||||
},
|
||||
"key": "/books/OL22951843M",
|
||||
"authors": [
|
||||
{
|
||||
"key": "/authors/OL382982A"
|
||||
}
|
||||
],
|
||||
"publish_places": [
|
||||
"New York"
|
||||
],
|
||||
"pagination": "491 p. :",
|
||||
"created": {
|
||||
"type": "/type/datetime",
|
||||
"value": "2009-02-12T16:29:58.929717"
|
||||
},
|
||||
"dewey_decimal_class": [
|
||||
"[Fic]"
|
||||
],
|
||||
"notes": {
|
||||
"type": "/type/text",
|
||||
"value": "Originally published: Australia : HarperCollins, 1995."
|
||||
},
|
||||
"number_of_pages": 491,
|
||||
"lccn": [
|
||||
"96001295"
|
||||
],
|
||||
"isbn_10": [
|
||||
"0060273224",
|
||||
"0060273232",
|
||||
"0064471837"
|
||||
],
|
||||
"publish_date": "1996",
|
||||
"works": [
|
||||
{
|
||||
"key": "/works/OL15832982W"
|
||||
}
|
||||
]
|
||||
}
|
1018
bookwyrm/tests/data/ol_edition_list.json
Normal file
1018
bookwyrm/tests/data/ol_edition_list.json
Normal file
File diff suppressed because it is too large
Load Diff
132
bookwyrm/tests/data/ol_search.json
Normal file
132
bookwyrm/tests/data/ol_search.json
Normal file
@ -0,0 +1,132 @@
|
||||
{
|
||||
"start": 0,
|
||||
"num_found": 2,
|
||||
"numFound": 2,
|
||||
"docs": [
|
||||
{
|
||||
"title_suggest": "This Is How You Lose the Time War",
|
||||
"edition_key": [
|
||||
"OL27901088M"
|
||||
],
|
||||
"isbn": [
|
||||
"9781534431003",
|
||||
"1534431004"
|
||||
],
|
||||
"has_fulltext": false,
|
||||
"text": [
|
||||
"OL27901088M",
|
||||
"9781534431003",
|
||||
"1534431004",
|
||||
"Amal El-Mohtar",
|
||||
"Max Gladstone",
|
||||
"OL7313207A",
|
||||
"OL7129451A",
|
||||
"epistolary",
|
||||
"science fiction",
|
||||
"time-traveling",
|
||||
"LGBT",
|
||||
"This Is How You Lose the Time War",
|
||||
"/works/OL20639540W",
|
||||
"Simon and Schuster",
|
||||
"Atlantis",
|
||||
"London",
|
||||
"The whole of time and space"
|
||||
],
|
||||
"author_name": [
|
||||
"Amal El-Mohtar",
|
||||
"Max Gladstone"
|
||||
],
|
||||
"seed": [
|
||||
"/books/OL27901088M",
|
||||
"/works/OL20639540W",
|
||||
"/subjects/science_fiction",
|
||||
"/subjects/time-traveling",
|
||||
"/subjects/epistolary",
|
||||
"/subjects/lgbt",
|
||||
"/subjects/place:london",
|
||||
"/subjects/place:atlantis",
|
||||
"/subjects/time:the_whole_of_time_and_space",
|
||||
"/authors/OL7313207A",
|
||||
"/authors/OL7129451A"
|
||||
],
|
||||
"author_key": [
|
||||
"OL7313207A",
|
||||
"OL7129451A"
|
||||
],
|
||||
"availability": {
|
||||
"status": "error"
|
||||
},
|
||||
"subject": [
|
||||
"epistolary",
|
||||
"science fiction",
|
||||
"time-traveling",
|
||||
"LGBT"
|
||||
],
|
||||
"title": "This Is How You Lose the Time War",
|
||||
"publish_date": [
|
||||
"July 16, 2019"
|
||||
],
|
||||
"type": "work",
|
||||
"ebook_count_i": 0,
|
||||
"publish_place": [
|
||||
"New York, USA"
|
||||
],
|
||||
"edition_count": 1,
|
||||
"key": "/works/OL20639540W",
|
||||
"publisher": [
|
||||
"Simon and Schuster"
|
||||
],
|
||||
"language": [
|
||||
"eng"
|
||||
],
|
||||
"last_modified_i": 1579909341,
|
||||
"cover_edition_key": "OL27901088M",
|
||||
"publish_year": [
|
||||
2019
|
||||
],
|
||||
"first_publish_year": 2019,
|
||||
"place": [
|
||||
"Atlantis",
|
||||
"London"
|
||||
],
|
||||
"time": [
|
||||
"The whole of time and space"
|
||||
]
|
||||
},
|
||||
{
|
||||
"title_suggest": "This is How You Lose the Time War",
|
||||
"cover_i": 8665647,
|
||||
"has_fulltext": false,
|
||||
"title": "This is How You Lose the Time War",
|
||||
"last_modified_i": 1561998020,
|
||||
"edition_count": 0,
|
||||
"author_name": [
|
||||
"Amal El-Mohtar",
|
||||
"Max Gladstone"
|
||||
],
|
||||
"seed": [
|
||||
"/works/OL19859295W",
|
||||
"/authors/OL7313207A",
|
||||
"/authors/OL7129451A"
|
||||
],
|
||||
"key": "/works/OL19859295W",
|
||||
"text": [
|
||||
"Amal El-Mohtar",
|
||||
"Max Gladstone",
|
||||
"OL7313207A",
|
||||
"OL7129451A",
|
||||
"This is How You Lose the Time War",
|
||||
"/works/OL19859295W"
|
||||
],
|
||||
"author_key": [
|
||||
"OL7313207A",
|
||||
"OL7129451A"
|
||||
],
|
||||
"type": "work",
|
||||
"availability": {
|
||||
"status": "error"
|
||||
},
|
||||
"ebook_count_i": 0
|
||||
}
|
||||
]
|
||||
}
|
63
bookwyrm/tests/data/ol_work.json
Normal file
63
bookwyrm/tests/data/ol_work.json
Normal file
@ -0,0 +1,63 @@
|
||||
{
|
||||
"first_publish_date": "1995",
|
||||
"key": "/works/OL15832982W",
|
||||
"description": {
|
||||
"type": "/type/text",
|
||||
"value": "First in the Old Kingdom/Abhorsen series."
|
||||
},
|
||||
"created": {
|
||||
"type": "/type/datetime",
|
||||
"value": "2011-07-07T16:30:28.384311"
|
||||
},
|
||||
"title": "Sabriel",
|
||||
"covers": [
|
||||
6796986,
|
||||
3843137
|
||||
],
|
||||
"first_sentence": {
|
||||
"type": "/type/text",
|
||||
"value": "THE RABBIT HAD been run over minutes before."
|
||||
},
|
||||
"excerpts": [
|
||||
{
|
||||
"excerpt": "THE RABBIT HAD been run over minutes before."
|
||||
}
|
||||
],
|
||||
"lc_classifications": [
|
||||
"PZ7.N647 Sab 1995"
|
||||
],
|
||||
"latest_revision": 5,
|
||||
"last_modified": {
|
||||
"type": "/type/datetime",
|
||||
"value": "2019-07-22T13:57:34.579651"
|
||||
},
|
||||
"authors": [
|
||||
{
|
||||
"type": {
|
||||
"key": "/type/author_role"
|
||||
},
|
||||
"author": {
|
||||
"key": "/authors/OL382982A"
|
||||
}
|
||||
}
|
||||
],
|
||||
"dewey_number": [
|
||||
"[Fic]"
|
||||
],
|
||||
"subjects": [
|
||||
"Fantasy",
|
||||
"Science Fiction & Fantasy",
|
||||
"Fantasy fiction",
|
||||
"Fiction",
|
||||
"Juvenile Fiction",
|
||||
"Juvenile fiction",
|
||||
"Magical thinking"
|
||||
],
|
||||
"type": {
|
||||
"key": "/type/work"
|
||||
},
|
||||
"subject_times": [
|
||||
"Life and Death."
|
||||
],
|
||||
"revision": 5
|
||||
}
|
1
bookwyrm/tests/models/__init__.py
Normal file
1
bookwyrm/tests/models/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import *
|
25
bookwyrm/tests/models/test_base_model.py
Normal file
25
bookwyrm/tests/models/test_base_model.py
Normal file
@ -0,0 +1,25 @@
|
||||
''' testing models '''
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads.models.base_model import FedireadsModel
|
||||
from fedireads.settings import DOMAIN
|
||||
|
||||
|
||||
class BaseModel(TestCase):
|
||||
def test_remote_id(self):
|
||||
instance = FedireadsModel()
|
||||
instance.id = 1
|
||||
expected = instance.get_remote_id()
|
||||
self.assertEqual(expected, 'https://%s/fedireadsmodel/1' % DOMAIN)
|
||||
|
||||
def test_remote_id_with_user(self):
|
||||
user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.com', 'mouseword')
|
||||
instance = FedireadsModel()
|
||||
instance.user = user
|
||||
instance.id = 1
|
||||
expected = instance.get_remote_id()
|
||||
self.assertEqual(
|
||||
expected,
|
||||
'https://%s/user/mouse/fedireadsmodel/1' % DOMAIN)
|
63
bookwyrm/tests/models/test_book_model.py
Normal file
63
bookwyrm/tests/models/test_book_model.py
Normal file
@ -0,0 +1,63 @@
|
||||
''' testing models '''
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models, settings
|
||||
|
||||
|
||||
class Book(TestCase):
|
||||
''' not too much going on in the books model but here we are '''
|
||||
def setUp(self):
|
||||
self.work = models.Work.objects.create(
|
||||
title='Example Work',
|
||||
remote_id='https://example.com/book/1'
|
||||
)
|
||||
self.first_edition = models.Edition.objects.create(
|
||||
title='Example Edition',
|
||||
parent_work=self.work,
|
||||
)
|
||||
self.second_edition = models.Edition.objects.create(
|
||||
title='Another Example Edition',
|
||||
parent_work=self.work,
|
||||
)
|
||||
|
||||
def test_remote_id(self):
|
||||
local_id = 'https://%s/book/%d' % (settings.DOMAIN, self.work.id)
|
||||
self.assertEqual(self.work.get_remote_id(), local_id)
|
||||
self.assertEqual(self.work.remote_id, 'https://example.com/book/1')
|
||||
|
||||
def test_local_id(self):
|
||||
''' the local_id property for books '''
|
||||
expected_id = 'https://%s/book/%d' % (settings.DOMAIN, self.work.id)
|
||||
self.assertEqual(self.work.local_id, expected_id)
|
||||
|
||||
def test_create_book(self):
|
||||
''' you shouldn't be able to create Books (only editions and works) '''
|
||||
self.assertRaises(
|
||||
ValueError,
|
||||
models.Book.objects.create,
|
||||
title='Invalid Book'
|
||||
)
|
||||
|
||||
def test_default_edition(self):
|
||||
''' a work should always be able to produce a deafult edition '''
|
||||
self.assertIsInstance(self.work.default_edition, models.Edition)
|
||||
self.assertEqual(self.work.default_edition, self.first_edition)
|
||||
|
||||
self.second_edition.default = True
|
||||
self.second_edition.save()
|
||||
|
||||
self.assertEqual(self.work.default_edition, self.second_edition)
|
||||
|
||||
|
||||
class Shelf(TestCase):
|
||||
def setUp(self):
|
||||
user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
models.Shelf.objects.create(
|
||||
name='Test Shelf', identifier='test-shelf', user=user)
|
||||
|
||||
def test_remote_id(self):
|
||||
''' editions and works use the same absolute id syntax '''
|
||||
shelf = models.Shelf.objects.get(identifier='test-shelf')
|
||||
expected_id = 'https://%s/user/mouse/shelf/test-shelf' % settings.DOMAIN
|
||||
self.assertEqual(shelf.get_remote_id(), expected_id)
|
112
bookwyrm/tests/models/test_import_model.py
Normal file
112
bookwyrm/tests/models/test_import_model.py
Normal file
@ -0,0 +1,112 @@
|
||||
''' testing models '''
|
||||
import datetime
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
|
||||
|
||||
class ImportJob(TestCase):
|
||||
''' this is a fancy one!!! '''
|
||||
def setUp(self):
|
||||
''' data is from a goodreads export of The Raven Tower '''
|
||||
read_data = {
|
||||
'Book Id': 39395857,
|
||||
'Title': 'The Raven Tower',
|
||||
'Author': 'Ann Leckie',
|
||||
'Author l-f': 'Leckie, Ann',
|
||||
'Additional Authors': '',
|
||||
'ISBN': '="0356506991"',
|
||||
'ISBN13': '="9780356506999"',
|
||||
'My Rating': 0,
|
||||
'Average Rating': 4.06,
|
||||
'Publisher': 'Orbit',
|
||||
'Binding': 'Hardcover',
|
||||
'Number of Pages': 416,
|
||||
'Year Published': 2019,
|
||||
'Original Publication Year': 2019,
|
||||
'Date Read': '2019/04/09',
|
||||
'Date Added': '2019/04/09',
|
||||
'Bookshelves': '',
|
||||
'Bookshelves with positions': '',
|
||||
'Exclusive Shelf': 'read',
|
||||
'My Review': '',
|
||||
'Spoiler': '',
|
||||
'Private Notes': '',
|
||||
'Read Count': 1,
|
||||
'Recommended For': '',
|
||||
'Recommended By': '',
|
||||
'Owned Copies': 0,
|
||||
'Original Purchase Date': '',
|
||||
'Original Purchase Location': '',
|
||||
'Condition': '',
|
||||
'Condition Description': '',
|
||||
'BCID': ''
|
||||
}
|
||||
currently_reading_data = read_data.copy()
|
||||
currently_reading_data['Exclusive Shelf'] = 'currently-reading'
|
||||
currently_reading_data['Date Read'] = ''
|
||||
|
||||
unknown_read_data = currently_reading_data.copy()
|
||||
unknown_read_data['Exclusive Shelf'] = 'read'
|
||||
unknown_read_data['Date Read'] = ''
|
||||
|
||||
user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
job = models.ImportJob.objects.create(user=user)
|
||||
models.ImportItem.objects.create(
|
||||
job=job, index=1, data=currently_reading_data)
|
||||
models.ImportItem.objects.create(
|
||||
job=job, index=2, data=read_data)
|
||||
models.ImportItem.objects.create(
|
||||
job=job, index=3, data=unknown_read_data)
|
||||
|
||||
|
||||
def test_isbn(self):
|
||||
''' it unquotes the isbn13 field from data '''
|
||||
expected = '9780356506999'
|
||||
item = models.ImportItem.objects.get(index=1)
|
||||
self.assertEqual(item.isbn, expected)
|
||||
|
||||
|
||||
def test_shelf(self):
|
||||
''' converts to the local shelf typology '''
|
||||
expected = 'reading'
|
||||
item = models.ImportItem.objects.get(index=1)
|
||||
self.assertEqual(item.shelf, expected)
|
||||
|
||||
|
||||
def test_date_added(self):
|
||||
''' converts to the local shelf typology '''
|
||||
expected = datetime.datetime(2019, 4, 9, 0, 0)
|
||||
item = models.ImportItem.objects.get(index=1)
|
||||
self.assertEqual(item.date_added, expected)
|
||||
|
||||
|
||||
def test_date_read(self):
|
||||
''' converts to the local shelf typology '''
|
||||
expected = datetime.datetime(2019, 4, 9, 0, 0)
|
||||
item = models.ImportItem.objects.get(index=2)
|
||||
self.assertEqual(item.date_read, expected)
|
||||
|
||||
|
||||
def test_currently_reading_reads(self):
|
||||
expected = [models.ReadThrough(
|
||||
start_date=datetime.datetime(2019, 4, 9, 0, 0))]
|
||||
actual = models.ImportItem.objects.get(index=1)
|
||||
self.assertEqual(actual.reads[0].start_date, expected[0].start_date)
|
||||
self.assertEqual(actual.reads[0].finish_date, expected[0].finish_date)
|
||||
|
||||
def test_read_reads(self):
|
||||
expected = [models.ReadThrough(
|
||||
finish_date=datetime.datetime(2019, 4, 9, 0, 0))]
|
||||
actual = models.ImportItem.objects.get(index=2)
|
||||
self.assertEqual(actual.reads[0].start_date, expected[0].start_date)
|
||||
self.assertEqual(actual.reads[0].finish_date, expected[0].finish_date)
|
||||
|
||||
def test_unread_reads(self):
|
||||
expected = []
|
||||
actual = models.ImportItem.objects.get(index=3)
|
||||
self.assertEqual(actual.reads, expected)
|
||||
|
||||
|
||||
|
52
bookwyrm/tests/models/test_status_model.py
Normal file
52
bookwyrm/tests/models/test_status_model.py
Normal file
@ -0,0 +1,52 @@
|
||||
''' testing models '''
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models, settings
|
||||
|
||||
|
||||
class Status(TestCase):
|
||||
def setUp(self):
|
||||
user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
book = models.Edition.objects.create(title='Example 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)
|
||||
|
||||
def test_status(self):
|
||||
status = models.Status.objects.first()
|
||||
expected_id = 'https://%s/user/mouse/status/%d' % \
|
||||
(settings.DOMAIN, status.id)
|
||||
self.assertEqual(status.remote_id, expected_id)
|
||||
|
||||
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_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)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class Tag(TestCase):
|
||||
def test_tag(self):
|
||||
book = models.Edition.objects.create(title='Example Edition')
|
||||
user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
tag = models.Tag.objects.create(user=user, book=book, name='t/est tag')
|
||||
self.assertEqual(tag.identifier, 't%2Fest+tag')
|
||||
|
34
bookwyrm/tests/models/test_user_model.py
Normal file
34
bookwyrm/tests/models/test_user_model.py
Normal file
@ -0,0 +1,34 @@
|
||||
''' testing models '''
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads.settings import DOMAIN
|
||||
|
||||
|
||||
class User(TestCase):
|
||||
def setUp(self):
|
||||
models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
|
||||
def test_computed_fields(self):
|
||||
''' username instead of id here '''
|
||||
user = models.User.objects.get(localname='mouse')
|
||||
expected_id = 'https://%s/user/mouse' % DOMAIN
|
||||
self.assertEqual(user.remote_id, expected_id)
|
||||
self.assertEqual(user.username, 'mouse@%s' % DOMAIN)
|
||||
self.assertEqual(user.localname, 'mouse')
|
||||
self.assertEqual(user.shared_inbox, 'https://%s/inbox' % DOMAIN)
|
||||
self.assertEqual(user.inbox, '%s/inbox' % expected_id)
|
||||
self.assertEqual(user.outbox, '%s/outbox' % expected_id)
|
||||
self.assertIsNotNone(user.private_key)
|
||||
self.assertIsNotNone(user.public_key)
|
||||
|
||||
|
||||
def test_user_shelves(self):
|
||||
user = models.User.objects.get(localname='mouse')
|
||||
shelves = models.Shelf.objects.filter(user=user).all()
|
||||
self.assertEqual(len(shelves), 3)
|
||||
names = [s.name for s in shelves]
|
||||
self.assertEqual(names, ['To Read', 'Currently Reading', 'Read'])
|
||||
ids = [s.identifier for s in shelves]
|
||||
self.assertEqual(ids, ['to-read', 'reading', 'read'])
|
1
bookwyrm/tests/status/__init__.py
Normal file
1
bookwyrm/tests/status/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from . import *
|
18
bookwyrm/tests/status/test_comment.py
Normal file
18
bookwyrm/tests/status/test_comment.py
Normal file
@ -0,0 +1,18 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads import status as status_builder
|
||||
|
||||
|
||||
class Comment(TestCase):
|
||||
''' we have hecka ways to create statuses '''
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
self.book = models.Edition.objects.create(title='Example Edition')
|
||||
|
||||
|
||||
def test_create_comment(self):
|
||||
comment = status_builder.create_comment(
|
||||
self.user, self.book, 'commentary')
|
||||
self.assertEqual(comment.content, 'commentary')
|
26
bookwyrm/tests/status/test_quotation.py
Normal file
26
bookwyrm/tests/status/test_quotation.py
Normal file
@ -0,0 +1,26 @@
|
||||
from django.test import TestCase
|
||||
import json
|
||||
import pathlib
|
||||
|
||||
from fedireads import activitypub, models
|
||||
from fedireads import status as status_builder
|
||||
|
||||
|
||||
class Quotation(TestCase):
|
||||
''' we have hecka ways to create statuses '''
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword',
|
||||
remote_id='https://example.com/user/mouse'
|
||||
)
|
||||
self.book = models.Edition.objects.create(
|
||||
title='Example Edition',
|
||||
remote_id='https://example.com/book/1',
|
||||
)
|
||||
|
||||
|
||||
def test_create_quotation(self):
|
||||
quotation = status_builder.create_quotation(
|
||||
self.user, self.book, 'commentary', 'a quote')
|
||||
self.assertEqual(quotation.quote, 'a quote')
|
||||
self.assertEqual(quotation.content, 'commentary')
|
39
bookwyrm/tests/status/test_review.py
Normal file
39
bookwyrm/tests/status/test_review.py
Normal file
@ -0,0 +1,39 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads import status as status_builder
|
||||
|
||||
|
||||
class Review(TestCase):
|
||||
''' we have hecka ways to create statuses '''
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
self.book = models.Edition.objects.create(title='Example Edition')
|
||||
|
||||
|
||||
def test_create_review(self):
|
||||
review = status_builder.create_review(
|
||||
self.user, self.book, 'review name', 'content', 5)
|
||||
self.assertEqual(review.name, 'review name')
|
||||
self.assertEqual(review.content, 'content')
|
||||
self.assertEqual(review.rating, 5)
|
||||
|
||||
review = status_builder.create_review(
|
||||
self.user, self.book, '<div>review</div> name', '<b>content', 5)
|
||||
self.assertEqual(review.name, 'review name')
|
||||
self.assertEqual(review.content, 'content')
|
||||
self.assertEqual(review.rating, 5)
|
||||
|
||||
def test_review_rating(self):
|
||||
review = status_builder.create_review(
|
||||
self.user, self.book, 'review name', 'content', -1)
|
||||
self.assertEqual(review.name, 'review name')
|
||||
self.assertEqual(review.content, 'content')
|
||||
self.assertEqual(review.rating, None)
|
||||
|
||||
review = status_builder.create_review(
|
||||
self.user, self.book, 'review name', 'content', 6)
|
||||
self.assertEqual(review.name, 'review name')
|
||||
self.assertEqual(review.content, 'content')
|
||||
self.assertEqual(review.rating, None)
|
28
bookwyrm/tests/status/test_status.py
Normal file
28
bookwyrm/tests/status/test_status.py
Normal file
@ -0,0 +1,28 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models
|
||||
from fedireads import status as status_builder
|
||||
|
||||
|
||||
class Status(TestCase):
|
||||
''' we have hecka ways to create statuses '''
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword',
|
||||
local=False,
|
||||
inbox='https://example.com/user/mouse/inbox',
|
||||
outbox='https://example.com/user/mouse/outbox',
|
||||
remote_id='https://example.com/user/mouse'
|
||||
)
|
||||
|
||||
|
||||
def test_create_status(self):
|
||||
content = 'statuses are usually <i>replies</i>'
|
||||
status = status_builder.create_status(
|
||||
self.user, content)
|
||||
self.assertEqual(status.content, content)
|
||||
|
||||
reply = status_builder.create_status(
|
||||
self.user, content, reply_parent=status)
|
||||
self.assertEqual(reply.content, content)
|
||||
self.assertEqual(reply.reply_parent, status)
|
37
bookwyrm/tests/test_books_manager.py
Normal file
37
bookwyrm/tests/test_books_manager.py
Normal file
@ -0,0 +1,37 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import books_manager, models
|
||||
from fedireads.connectors.fedireads_connector import Connector
|
||||
from fedireads.settings import DOMAIN
|
||||
|
||||
|
||||
class Book(TestCase):
|
||||
def setUp(self):
|
||||
self.work = models.Work.objects.create(
|
||||
title='Example Work'
|
||||
)
|
||||
|
||||
self.edition = models.Edition.objects.create(
|
||||
title='Example Edition',
|
||||
parent_work=self.work
|
||||
)
|
||||
|
||||
def test_get_edition(self):
|
||||
edition = books_manager.get_edition(self.edition.id)
|
||||
self.assertEqual(edition, self.edition)
|
||||
|
||||
|
||||
def test_get_edition_work(self):
|
||||
edition = books_manager.get_edition(self.work.id)
|
||||
self.assertEqual(edition, self.edition)
|
||||
|
||||
|
||||
def test_get_or_create_connector(self):
|
||||
remote_id = 'https://example.com/object/1'
|
||||
connector = books_manager.get_or_create_connector(remote_id)
|
||||
self.assertIsInstance(connector, Connector)
|
||||
self.assertEqual(connector.identifier, 'example.com')
|
||||
self.assertEqual(connector.base_url, 'https://example.com')
|
||||
|
||||
same_connector = books_manager.get_or_create_connector(remote_id)
|
||||
self.assertEqual(connector.identifier, same_connector.identifier)
|
77
bookwyrm/tests/test_broadcast.py
Normal file
77
bookwyrm/tests/test_broadcast.py
Normal file
@ -0,0 +1,77 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models, broadcast
|
||||
from fedireads.settings import DOMAIN
|
||||
|
||||
|
||||
class Book(TestCase):
|
||||
def setUp(self):
|
||||
self.user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.mouse', 'mouseword')
|
||||
|
||||
follower = models.User.objects.create_user(
|
||||
'rat', 'rat@mouse.mouse', 'ratword', local=False,
|
||||
remote_id='http://example.com/u/1',
|
||||
outbox='http://example.com/u/1/o',
|
||||
shared_inbox='http://example.com/inbox',
|
||||
inbox='http://example.com/u/1/inbox')
|
||||
self.user.followers.add(follower)
|
||||
|
||||
no_inbox_follower = models.User.objects.create_user(
|
||||
'hamster', 'hamster@mouse.mouse', 'hamword',
|
||||
shared_inbox=None, local=False,
|
||||
remote_id='http://example.com/u/2',
|
||||
outbox='http://example.com/u/2/o',
|
||||
inbox='http://example.com/u/2/inbox')
|
||||
self.user.followers.add(no_inbox_follower)
|
||||
|
||||
non_fr_follower = models.User.objects.create_user(
|
||||
'gerbil', 'gerb@mouse.mouse', 'gerbword',
|
||||
remote_id='http://example.com/u/3',
|
||||
outbox='http://example2.com/u/3/o',
|
||||
inbox='http://example2.com/u/3/inbox',
|
||||
shared_inbox='http://example2.com/inbox',
|
||||
fedireads_user=False, local=False)
|
||||
self.user.followers.add(non_fr_follower)
|
||||
|
||||
local_follower = models.User.objects.create_user(
|
||||
'joe', 'joe@mouse.mouse', 'jeoword')
|
||||
self.user.followers.add(local_follower)
|
||||
|
||||
models.User.objects.create_user(
|
||||
'nutria', 'nutria@mouse.mouse', 'nuword',
|
||||
remote_id='http://example.com/u/4',
|
||||
outbox='http://example.com/u/4/o',
|
||||
shared_inbox='http://example.com/inbox',
|
||||
inbox='http://example.com/u/4/inbox',
|
||||
local=False)
|
||||
|
||||
|
||||
def test_get_public_recipients(self):
|
||||
expected = [
|
||||
'http://example2.com/inbox',
|
||||
'http://example.com/inbox',
|
||||
'http://example.com/u/2/inbox',
|
||||
]
|
||||
|
||||
recipients = broadcast.get_public_recipients(self.user)
|
||||
self.assertEqual(recipients, expected)
|
||||
|
||||
|
||||
def test_get_public_recipients_software(self):
|
||||
expected = [
|
||||
'http://example.com/inbox',
|
||||
'http://example.com/u/2/inbox',
|
||||
]
|
||||
|
||||
recipients = broadcast.get_public_recipients(self.user, software='fedireads')
|
||||
self.assertEqual(recipients, expected)
|
||||
|
||||
|
||||
def test_get_public_recipients_software_other(self):
|
||||
expected = [
|
||||
'http://example2.com/inbox',
|
||||
]
|
||||
|
||||
recipients = broadcast.get_public_recipients(self.user, software='mastodon')
|
||||
self.assertEqual(recipients, expected)
|
59
bookwyrm/tests/test_incoming_favorite.py
Normal file
59
bookwyrm/tests/test_incoming_favorite.py
Normal file
@ -0,0 +1,59 @@
|
||||
import json
|
||||
import pathlib
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models, incoming
|
||||
|
||||
|
||||
class Favorite(TestCase):
|
||||
''' not too much going on in the books model but here we are '''
|
||||
def setUp(self):
|
||||
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.local_user = models.User.objects.create_user(
|
||||
'mouse', 'mouse@mouse.com', 'mouseword',
|
||||
remote_id='http://local.com/user/mouse')
|
||||
self.status = models.Status.objects.create(
|
||||
user=self.local_user,
|
||||
content='Test status',
|
||||
remote_id='http://local.com/status/1',
|
||||
)
|
||||
|
||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
||||
'data/ap_user.json'
|
||||
)
|
||||
self.user_data = json.loads(datafile.read_bytes())
|
||||
|
||||
|
||||
|
||||
def test_handle_favorite(self):
|
||||
activity = {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': 'http://example.com/activity/1',
|
||||
|
||||
'type': 'Create',
|
||||
'actor': 'https://example.com/users/rat',
|
||||
'published': 'Mon, 25 May 2020 19:31:20 GMT',
|
||||
'to': ['https://example.com/user/rat/followers'],
|
||||
'cc': ['https://www.w3.org/ns/activitystreams#Public'],
|
||||
'object': {
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
'id': 'http://example.com/fav/1',
|
||||
'type': 'Like',
|
||||
'actor': 'https://example.com/users/rat',
|
||||
'object': 'http://local.com/status/1',
|
||||
},
|
||||
'signature': {}
|
||||
}
|
||||
|
||||
result = incoming.handle_favorite(activity)
|
||||
|
||||
fav = models.Favorite.objects.get(remote_id='http://example.com/fav/1')
|
||||
self.assertEqual(fav.status, self.status)
|
||||
self.assertEqual(fav.remote_id, 'http://example.com/fav/1')
|
||||
self.assertEqual(fav.user, self.remote_user)
|
70
bookwyrm/tests/test_remote_user.py
Normal file
70
bookwyrm/tests/test_remote_user.py
Normal file
@ -0,0 +1,70 @@
|
||||
import json
|
||||
import pathlib
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads import models, remote_user
|
||||
|
||||
|
||||
class RemoteUser(TestCase):
|
||||
''' not too much going on in the books model but here we are '''
|
||||
def setUp(self):
|
||||
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',
|
||||
)
|
||||
datafile = pathlib.Path(__file__).parent.joinpath(
|
||||
'data/ap_user.json'
|
||||
)
|
||||
self.user_data = json.loads(datafile.read_bytes())
|
||||
|
||||
|
||||
|
||||
def test_get_remote_user(self):
|
||||
actor = 'https://example.com/users/rat'
|
||||
user = remote_user.get_or_create_remote_user(actor)
|
||||
self.assertEqual(user, self.remote_user)
|
||||
|
||||
|
||||
def test_create_remote_user(self):
|
||||
user = remote_user.create_remote_user(self.user_data)
|
||||
self.assertFalse(user.local)
|
||||
self.assertEqual(user.remote_id, 'https://example.com/user/mouse')
|
||||
self.assertEqual(user.username, 'mouse@example.com')
|
||||
self.assertEqual(user.name, 'MOUSE?? MOUSE!!')
|
||||
self.assertEqual(user.inbox, 'https://example.com/user/mouse/inbox')
|
||||
self.assertEqual(user.outbox, 'https://example.com/user/mouse/outbox')
|
||||
self.assertEqual(user.shared_inbox, 'https://example.com/inbox')
|
||||
self.assertEqual(
|
||||
user.public_key,
|
||||
self.user_data['publicKey']['publicKeyPem']
|
||||
)
|
||||
self.assertEqual(user.local, False)
|
||||
self.assertEqual(user.fedireads_user, True)
|
||||
self.assertEqual(user.manually_approves_followers, False)
|
||||
|
||||
|
||||
def test_create_remote_user_missing_inbox(self):
|
||||
del self.user_data['inbox']
|
||||
self.assertRaises(
|
||||
TypeError,
|
||||
remote_user.create_remote_user,
|
||||
self.user_data
|
||||
)
|
||||
|
||||
|
||||
def test_create_remote_user_missing_outbox(self):
|
||||
del self.user_data['outbox']
|
||||
self.assertRaises(
|
||||
TypeError,
|
||||
remote_user.create_remote_user,
|
||||
self.user_data
|
||||
)
|
||||
|
||||
|
||||
def test_create_remote_user_default_fields(self):
|
||||
del self.user_data['manuallyApprovesFollowers']
|
||||
user = remote_user.create_remote_user(self.user_data)
|
||||
self.assertEqual(user.manually_approves_followers, False)
|
50
bookwyrm/tests/test_sanitize_html.py
Normal file
50
bookwyrm/tests/test_sanitize_html.py
Normal file
@ -0,0 +1,50 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from fedireads.sanitize_html import InputHtmlParser
|
||||
|
||||
|
||||
class Sanitizer(TestCase):
|
||||
def test_no_html(self):
|
||||
input_text = 'no html '
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual(input_text, output)
|
||||
|
||||
|
||||
def test_valid_html(self):
|
||||
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):
|
||||
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):
|
||||
input_text = '<b>yes <i>html</i>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual('yes html', output)
|
||||
|
||||
input_text = 'yes <i></b>html </i>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual('yes html ', output)
|
||||
|
||||
|
||||
def test_disallowed_html(self):
|
||||
input_text = '<div> yes <i>html</i></div>'
|
||||
parser = InputHtmlParser()
|
||||
parser.feed(input_text)
|
||||
output = parser.get_output()
|
||||
self.assertEqual(' yes <i>html</i>', output)
|
189
bookwyrm/tests/test_signing.py
Normal file
189
bookwyrm/tests/test_signing.py
Normal file
@ -0,0 +1,189 @@
|
||||
import time
|
||||
from collections import namedtuple
|
||||
from urllib.parse import urlsplit
|
||||
import pathlib
|
||||
|
||||
import json
|
||||
import responses
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.utils.http import http_date
|
||||
|
||||
from fedireads.models import User
|
||||
from fedireads.activitypub import Follow
|
||||
from fedireads.settings import DOMAIN
|
||||
from fedireads.signatures import create_key_pair, make_signature, make_digest
|
||||
|
||||
def get_follow_data(follower, followee):
|
||||
follow_activity = Follow(
|
||||
id='https://test.com/user/follow/id',
|
||||
actor=follower.remote_id,
|
||||
object=followee.remote_id,
|
||||
).serialize()
|
||||
return json.dumps(follow_activity)
|
||||
|
||||
Sender = namedtuple('Sender', ('remote_id', 'private_key', 'public_key'))
|
||||
|
||||
class Signature(TestCase):
|
||||
def setUp(self):
|
||||
self.mouse = User.objects.create_user('mouse', 'mouse@example.com', '')
|
||||
self.rat = User.objects.create_user('rat', 'rat@example.com', '')
|
||||
self.cat = User.objects.create_user('cat', 'cat@example.com', '')
|
||||
|
||||
private_key, public_key = create_key_pair()
|
||||
|
||||
self.fake_remote = Sender(
|
||||
'http://localhost/user/remote',
|
||||
private_key,
|
||||
public_key,
|
||||
)
|
||||
|
||||
def send(self, signature, now, data, digest):
|
||||
c = Client()
|
||||
return c.post(
|
||||
urlsplit(self.rat.inbox).path,
|
||||
data=data,
|
||||
content_type='application/json',
|
||||
**{
|
||||
'HTTP_DATE': now,
|
||||
'HTTP_SIGNATURE': signature,
|
||||
'HTTP_DIGEST': digest,
|
||||
'HTTP_CONTENT_TYPE': 'application/activity+json; charset=utf-8',
|
||||
'HTTP_HOST': DOMAIN,
|
||||
}
|
||||
)
|
||||
|
||||
def send_test_request(
|
||||
self,
|
||||
sender,
|
||||
signer=None,
|
||||
send_data=None,
|
||||
digest=None,
|
||||
date=None):
|
||||
now = date or http_date()
|
||||
data = get_follow_data(sender, self.rat)
|
||||
digest = digest or make_digest(data)
|
||||
signature = make_signature(
|
||||
signer or sender, self.rat.inbox, now, digest)
|
||||
return self.send(signature, now, send_data or data, digest)
|
||||
|
||||
def test_correct_signature(self):
|
||||
response = self.send_test_request(sender=self.mouse)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_wrong_signature(self):
|
||||
''' Messages must be signed by the right actor.
|
||||
(cat cannot sign messages on behalf of mouse)
|
||||
'''
|
||||
response = self.send_test_request(sender=self.mouse, signer=self.cat)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
@responses.activate
|
||||
def test_remote_signer(self):
|
||||
datafile = pathlib.Path(__file__).parent.joinpath('data/ap_user.json')
|
||||
data = json.loads(datafile.read_bytes())
|
||||
data['id'] = self.fake_remote.remote_id
|
||||
data['publicKey']['publicKeyPem'] = self.fake_remote.public_key
|
||||
del data['icon'] # Avoid having to return an avatar.
|
||||
responses.add(
|
||||
responses.GET,
|
||||
self.fake_remote.remote_id,
|
||||
json=data,
|
||||
status=200)
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://localhost/.well-known/nodeinfo',
|
||||
status=404)
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://example.com/user/mouse/outbox?page=true',
|
||||
json={'orderedItems': []},
|
||||
status=200
|
||||
)
|
||||
|
||||
response = self.send_test_request(sender=self.fake_remote)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@responses.activate
|
||||
def test_key_needs_refresh(self):
|
||||
datafile = pathlib.Path(__file__).parent.joinpath('data/ap_user.json')
|
||||
data = json.loads(datafile.read_bytes())
|
||||
data['id'] = self.fake_remote.remote_id
|
||||
data['publicKey']['publicKeyPem'] = self.fake_remote.public_key
|
||||
del data['icon'] # Avoid having to return an avatar.
|
||||
responses.add(
|
||||
responses.GET,
|
||||
self.fake_remote.remote_id,
|
||||
json=data,
|
||||
status=200)
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://localhost/.well-known/nodeinfo',
|
||||
status=404)
|
||||
responses.add(
|
||||
responses.GET,
|
||||
'https://example.com/user/mouse/outbox?page=true',
|
||||
json={'orderedItems': []},
|
||||
status=200
|
||||
)
|
||||
|
||||
# Second and subsequent fetches get a different key:
|
||||
new_private_key, new_public_key = create_key_pair()
|
||||
new_sender = Sender(
|
||||
self.fake_remote.remote_id, new_private_key, new_public_key)
|
||||
data['publicKey']['publicKeyPem'] = new_public_key
|
||||
responses.add(
|
||||
responses.GET,
|
||||
self.fake_remote.remote_id,
|
||||
json=data,
|
||||
status=200)
|
||||
|
||||
|
||||
# Key correct:
|
||||
response = self.send_test_request(sender=self.fake_remote)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Old key is cached, so still works:
|
||||
response = self.send_test_request(sender=self.fake_remote)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Try with new key:
|
||||
response = self.send_test_request(sender=new_sender)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# Now the old key will fail:
|
||||
response = self.send_test_request(sender=self.fake_remote)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
|
||||
@responses.activate
|
||||
def test_nonexistent_signer(self):
|
||||
responses.add(
|
||||
responses.GET,
|
||||
self.fake_remote.remote_id,
|
||||
json={'error': 'not found'},
|
||||
status=404)
|
||||
|
||||
response = self.send_test_request(sender=self.fake_remote)
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_changed_data(self):
|
||||
'''Message data must match the digest header.'''
|
||||
response = self.send_test_request(
|
||||
self.mouse,
|
||||
send_data=get_follow_data(self.mouse, self.cat))
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_invalid_digest(self):
|
||||
response = self.send_test_request(
|
||||
self.mouse,
|
||||
digest='SHA-256=AAAAAAAAAAAAAAAAAA')
|
||||
self.assertEqual(response.status_code, 401)
|
||||
|
||||
def test_old_message(self):
|
||||
'''Old messages should be rejected to prevent replay attacks.'''
|
||||
response = self.send_test_request(
|
||||
self.mouse,
|
||||
date=http_date(time.time() - 301)
|
||||
)
|
||||
self.assertEqual(response.status_code, 401)
|
Reference in New Issue
Block a user