chapeau/kepi/bowler_pub/tests/test_send_to_outbox.py

389 wiersze
10 KiB
Python

from django.test import TestCase
from unittest import skip
from kepi.bowler_pub.tests import *
from kepi.bowler_pub.create import create
from kepi.bowler_pub.models.audience import Audience, AUDIENCE_FIELD_NAMES
from kepi.bowler_pub.models.mention import Mention
from kepi.bowler_pub.models.item import AcItem
from kepi.bowler_pub.models.acobject import AcObject
from kepi.bowler_pub.models.activity import AcActivity
from django.test import Client
from urllib.parse import urlparse
import httpretty
import logging
import json
REMOTE_DAVE_ID = "https://dave.example.net/users/dave"
REMOTE_DAVE_DOMAIN = urlparse(REMOTE_DAVE_ID).netloc
REMOTE_DAVE_FOLLOWERS = REMOTE_DAVE_ID + 'followers'
REMOTE_DAVE_KEY = REMOTE_DAVE_ID + '#main-key'
ALICE_ID = 'https://testserver/users/alice'
OUTBOX = ALICE_ID+'/outbox'
OUTBOX_PATH = '/users/alice/outbox'
BOB_ID = 'https://testserver/users/bob'
MIME_TYPE = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'
# as given in https://www.w3.org/TR/activitypub/
OBJECT_FORM = {
"@context": ["https://www.w3.org/ns/activitystreams",
{"@language": "en"}],
"type": "Note",
'attributedTo': ALICE_ID,
"name": "Chris liked 'Minimal ActivityPub update client'",
"object": "https://example.net/2016/05/minimal-activitypub",
"to": [PUBLIC],
}
CREATE_FORM = {
'@context': 'https://www.w3.org/ns/activitystreams',
'id': ALICE_ID + '#foo',
'actor': ALICE_ID,
'type': 'Create',
'object': OBJECT_FORM,
}
logger = logging.getLogger(name='kepi')
class TestOutbox(TestCase):
def setUp(self):
settings.KEPI['LOCAL_OBJECT_HOSTNAME'] = 'testserver'
settings.ALLOWED_HOSTS = [
'altair.example.com',
'testserver',
]
def _send(self,
content,
keys = None,
sender = None,
signed = True,
):
if keys is None:
keys = json.load(open('kepi/bowler_pub/tests/keys/keys-0001.json', 'r'))
if sender is None:
sender = create_local_person(
name = 'alice',
publicKey = keys['public'],
privateKey = keys['private'],
)
f_body = dict([('f_'+f,v) for f,v in content.items()])
body, headers = test_message_body_and_headers(
secret = keys['private'],
path = OUTBOX_PATH,
key_id = sender['name']+'#main-key',
signed = signed,
**f_body,
)
headers=dict([('HTTP_'+f,v) for f,v in headers.items()])
c = Client()
response = c.post(OUTBOX,
body,
**headers,
content_type='application/activity+json',
)
return response
def _get(self,
client,
url):
response = client.get(url,
HTTP_ACCEPT = MIME_TYPE,
)
self.assertEqual(
response.status_code,
200)
return json.loads(
str(response.content, encoding='UTF-8'))
def _get_collection(self, url):
c = Client()
result = []
linkname = 'first'
while True:
page = self._get(c, url)
logger.debug('Received %s:', url)
logger.debug(' -- %s', page)
if 'orderedAcItems' in page:
result.extend(page['orderedAcItems'])
if linkname not in page:
logger.info('Inbox contains: %s',
result)
return result
url = page[linkname]
linkname = 'next'
def test_no_signature(self):
self._send(
content = CREATE_FORM,
signed = False,
)
statuses = AcItem.objects.filter(
f_attributedTo=ALICE_ID,
)
self.assertEqual(
len(statuses),
0)
def test_create(self):
self._send(
content = CREATE_FORM,
)
statuses = AcItem.objects.filter(
f_attributedTo=ALICE_ID,
)
something = self._get_collection(OUTBOX)
self.assertEqual(
len(statuses),
1)
def test_post_by_remote_interloper(self):
keys = json.load(open('kepi/bowler_pub/tests/keys/keys-0002.json', 'r'))
sender = create_remote_person(
url = REMOTE_DAVE_ID,
name = 'dave',
publicKey = keys['public'],
)
create = CREATE_FORM
create['actor'] = REMOTE_DAVE_ID
create['id'] = REMOTE_DAVE_ID+'#foo'
self._send(
content = create,
sender = sender,
)
statuses = AcItem.objects.filter(
f_attributedTo=REMOTE_DAVE_ID,
)
self.assertEqual(
len(statuses),
0)
def test_post_by_local_interloper(self):
keys1 = json.load(open('kepi/bowler_pub/tests/keys/keys-0001.json', 'r'))
keys2 = json.load(open('kepi/bowler_pub/tests/keys/keys-0002.json', 'r'))
create_local_person(
name = 'alice',
privateKey = keys1['private'],
publicKey = keys1['public'],
)
sender = create_local_person(
name = 'bob',
privateKey = keys2['private'],
publicKey = keys2['public'],
)
create = CREATE_FORM
create['actor'] = sender.url
create['id'] = sender.url+'#foo'
self._send(
content = create,
sender = sender,
)
statuses = AcItem.objects.filter(
f_attributedTo=sender.id,
)
self.assertEqual(
len(statuses),
0)
@httpretty.activate
def test_unwrapped_object(self):
items_before = list(AcObject.objects.all())
self._send(
content = OBJECT_FORM,
)
items_after = list(AcObject.objects.all())
# This should have created two objects:
# the Note we sent, and an implict Create.
self.assertEqual(
len(items_after)-len(items_before),
2)
def test_create_doesnt_work_on_activities(self):
create = CREATE_FORM
create['object']['type'] = 'Like'
self._send(
content = create,
)
activities = AcActivity.objects.all()
self.assertEqual(
len(activities),
0)
def test_like(self):
note = create_local_note(
attributedTo = BOB_ID,
)
self._send(
content = {
'@context': 'https://www.w3.org/ns/activitystreams',
'actor': ALICE_ID,
'type': 'Like',
'object': note.url,
}
)
self.assertEqual(
len(AcActivity.objects.filter(f_actor=ALICE_ID)),
1)
# TODO When AcActors have liked() and AcObjects have likes(),
# test those here too.
def test_update(self):
note = create_local_note(
attributedTo = ALICE_ID,
content = 'Twas brillig, and the slithy toves',
)
self.assertEqual(
note['content'],
'Twas brillig, and the slithy toves',
)
self._send(
content = {
'@context': 'https://www.w3.org/ns/activitystreams',
'actor': ALICE_ID,
'type': 'Update',
'object': {
'id': note.url,
'content': 'did gyre and gimble in the wabe.',
},
}
)
note = AcItem.objects.get(f_attributedTo = ALICE_ID)
self.assertEqual(
note['content'],
'did gyre and gimble in the wabe.',
)
def test_update_someone_elses(self):
note = create_local_note(
attributedTo = BOB_ID,
content = 'Twas brillig, and the slithy toves',
)
self.assertEqual(
note['content'],
'Twas brillig, and the slithy toves',
)
self._send(
content = {
'@context': 'https://www.w3.org/ns/activitystreams',
'actor': ALICE_ID,
'type': 'Update',
'object': {
'id': note.url,
'content': 'did gyre and gimble in the wabe.',
},
}
)
note = AcItem.objects.get(f_attributedTo = BOB_ID)
# no change, because Alice doesn't own this note
self.assertEqual(
note['content'],
'Twas brillig, and the slithy toves',
)
def test_delete(self):
c = Client()
keys = json.load(open('kepi/bowler_pub/tests/keys/keys-0001.json', 'r'))
alice = create_local_person(
name = 'alice',
publicKey = keys['public'],
privateKey = keys['private'],
)
for tombstones, result_code in [
(True, 410),
(False, 404),
]:
settings.KEPI['TOMBSTONES'] = tombstones
note = create_local_note(
attributedTo = ALICE_ID,
content = 'Twas brillig, and the slithy toves',
)
response = c.get(note.url)
self.assertEqual(
response.status_code,
200)
self._send(
keys = keys,
sender = alice,
content = {
'@context': 'https://www.w3.org/ns/activitystreams',
'actor': ALICE_ID,
'type': 'Delete',
'object': note.url,
},
)
response = c.get(note.url)
self.assertEqual(
response.status_code,
result_code)