kopia lustrzana https://gitlab.com/jaywink/federation
fix tests
rodzic
63b591db46
commit
46a518f04a
|
@ -28,7 +28,13 @@ def get_outbound_entity(entity: BaseEntity, private_key):
|
||||||
return entity
|
return entity
|
||||||
outbound = None
|
outbound = None
|
||||||
cls = entity.__class__
|
cls = entity.__class__
|
||||||
if cls == Accept:
|
if cls in [
|
||||||
|
models.Accept, models.Follow, models.Person, models.Note,
|
||||||
|
models.Delete, models.Tombstone, models.Announce,
|
||||||
|
] and isinstance(entity, BaseEntity):
|
||||||
|
# Already fine
|
||||||
|
outbound = entity
|
||||||
|
elif cls == Accept:
|
||||||
outbound = models.Accept.from_base(entity)
|
outbound = models.Accept.from_base(entity)
|
||||||
elif cls == Follow:
|
elif cls == Follow:
|
||||||
outbound = models.Follow.from_base(entity)
|
outbound = models.Follow.from_base(entity)
|
||||||
|
|
|
@ -206,7 +206,6 @@ class MixedField(fields.Nested):
|
||||||
return super()._serialize(value, attr, obj, **kwargs)
|
return super()._serialize(value, attr, obj, **kwargs)
|
||||||
|
|
||||||
def _deserialize(self, value, attr, data, **kwargs):
|
def _deserialize(self, value, attr, data, **kwargs):
|
||||||
print(attr, value, type(value))
|
|
||||||
# this is just so the ACTIVITYPUB_POST_OBJECT_IMAGES test payload passes
|
# this is just so the ACTIVITYPUB_POST_OBJECT_IMAGES test payload passes
|
||||||
if len(value) == 0: return value
|
if len(value) == 0: return value
|
||||||
|
|
||||||
|
@ -293,7 +292,6 @@ class Object(BaseEntity, metaclass=JsonLDAnnotation):
|
||||||
|
|
||||||
def to_as2(self):
|
def to_as2(self):
|
||||||
obj = self.activity if isinstance(self.activity, Activity) else self
|
obj = self.activity if isinstance(self.activity, Activity) else self
|
||||||
print('to_as2', obj, getattr(obj, 'tag_objects', None))
|
|
||||||
return jsonld.compact(obj.dump(), CONTEXT)
|
return jsonld.compact(obj.dump(), CONTEXT)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -399,10 +397,8 @@ class Home(metaclass=JsonLDAnnotation):
|
||||||
|
|
||||||
class NormalizedList(fields.List):
|
class NormalizedList(fields.List):
|
||||||
def _deserialize(self,value, attr, data, **kwargs):
|
def _deserialize(self,value, attr, data, **kwargs):
|
||||||
print('List', attr, value)
|
|
||||||
value = normalize_value(value)
|
value = normalize_value(value)
|
||||||
ret = super()._deserialize(value,attr,data,**kwargs)
|
ret = super()._deserialize(value,attr,data,**kwargs)
|
||||||
print('List after', ret)
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
@ -555,9 +551,9 @@ class Emoji(Object):
|
||||||
class Person(Object, base.Profile):
|
class Person(Object, base.Profile):
|
||||||
id = fields.Id()
|
id = fields.Id()
|
||||||
inbox = IRI(ldp.inbox)
|
inbox = IRI(ldp.inbox)
|
||||||
outbox = IRI(as2.outbox, dump_derived={'fmt': '{id}/outbox/', 'fields': ['id']})
|
outbox = IRI(as2.outbox)
|
||||||
following = IRI(as2.following, dump_derived={'fmt': '{id}/following/', 'fields': ['id']})
|
following = IRI(as2.following)
|
||||||
followers = IRI(as2.followers, dump_derived={'fmt': '{id}/followers/', 'fields': ['id']})
|
followers = IRI(as2.followers)
|
||||||
username = fields.String(as2.preferredUsername)
|
username = fields.String(as2.preferredUsername)
|
||||||
endpoints = CompactedDict(as2.endpoints)
|
endpoints = CompactedDict(as2.endpoints)
|
||||||
shared_inbox = IRI(as2.sharedInbox) # misskey adds this
|
shared_inbox = IRI(as2.sharedInbox) # misskey adds this
|
||||||
|
@ -570,7 +566,7 @@ class Person(Object, base.Profile):
|
||||||
devices = IRI(toot.devices)
|
devices = IRI(toot.devices)
|
||||||
public_key_dict = CompactedDict(sec.publicKey)
|
public_key_dict = CompactedDict(sec.publicKey)
|
||||||
guid = fields.String(diaspora.guid)
|
guid = fields.String(diaspora.guid)
|
||||||
handle = fields.String(diaspora.handle)
|
handle = fields.String(diaspora.handle, default="")
|
||||||
raw_content = fields.String(as2.summary, default="") # None fails in extract_mentions
|
raw_content = fields.String(as2.summary, default="") # None fails in extract_mentions
|
||||||
has_address = MixedField(vcard.hasAddress, nested='HomeSchema')
|
has_address = MixedField(vcard.hasAddress, nested='HomeSchema')
|
||||||
has_instant_message = fields.List(vcard.hasInstantMessage, cls_or_instance=fields.String)
|
has_instant_message = fields.List(vcard.hasInstantMessage, cls_or_instance=fields.String)
|
||||||
|
@ -580,6 +576,7 @@ class Person(Object, base.Profile):
|
||||||
copied_to = IRI(toot.copiedTo)
|
copied_to = IRI(toot.copiedTo)
|
||||||
capabilities = CompactedDict(litepub.capabilities)
|
capabilities = CompactedDict(litepub.capabilities)
|
||||||
suspended = fields.Boolean(toot.suspended)
|
suspended = fields.Boolean(toot.suspended)
|
||||||
|
public = True
|
||||||
_inboxes = None
|
_inboxes = None
|
||||||
_public_key = None
|
_public_key = None
|
||||||
_image_urls = None
|
_image_urls = None
|
||||||
|
@ -598,7 +595,21 @@ class Person(Object, base.Profile):
|
||||||
self._allowed_children += (PropertyValue, IdentityProof)
|
self._allowed_children += (PropertyValue, IdentityProof)
|
||||||
|
|
||||||
def to_as2(self):
|
def to_as2(self):
|
||||||
self.id = self.id.rstrip('/') # TODO: sort out the trailing / business
|
#self.id = self.id.rstrip('/') # TODO: sort out the trailing / business
|
||||||
|
self.followers = f'{with_slash(self.id)}followers/'
|
||||||
|
self.following = f'{with_slash(self.id)}following/'
|
||||||
|
self.outbox = f'{with_slash(self.id)}outbox/'
|
||||||
|
|
||||||
|
if hasattr(self, 'times'):
|
||||||
|
if self.times.get('updated',0) > self.times.get('created',0):
|
||||||
|
self.updated = self.times.get('updated')
|
||||||
|
if self.times.get('edited'):
|
||||||
|
self.activity = Update(
|
||||||
|
activity_id=f'{self.id}#profile-{uuid.uuid4()}',
|
||||||
|
actor_id=self.id,
|
||||||
|
created_at=self.times.get('updated'),
|
||||||
|
object_=self,
|
||||||
|
)
|
||||||
return super().to_as2()
|
return super().to_as2()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@ -617,10 +628,11 @@ class Person(Object, base.Profile):
|
||||||
|
|
||||||
@inboxes.setter
|
@inboxes.setter
|
||||||
def inboxes(self, value):
|
def inboxes(self, value):
|
||||||
self._inboxes = value
|
if value != {'private':None, 'public':None}:
|
||||||
if isinstance(value, dict):
|
self._inboxes = value
|
||||||
self.inbox = value.get('private', None)
|
if isinstance(value, dict):
|
||||||
self.endpoints = {'sharedInbox': value.get('public', None)}
|
self.inbox = value.get('private', None)
|
||||||
|
self.endpoints = {'sharedInbox': value.get('public', None)}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def public_key(self):
|
def public_key(self):
|
||||||
|
@ -634,8 +646,9 @@ class Person(Object, base.Profile):
|
||||||
@public_key.setter
|
@public_key.setter
|
||||||
def public_key(self, value):
|
def public_key(self, value):
|
||||||
self._public_key = value
|
self._public_key = value
|
||||||
id_ = self.id.rstrip('/')
|
#id_ = self.id.rstrip('/')
|
||||||
self.public_key_dict = {'id': id_+'#main-key', 'owner': id_, 'publicKeyPem': value}
|
#self.public_key_dict = {'id': id_+'#main-key', 'owner': id_, 'publicKeyPem': value}
|
||||||
|
self.public_key_dict = {'id': self.id+'#main-key', 'owner': self.id, 'publicKeyPem': value}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_urls(self):
|
def image_urls(self):
|
||||||
|
@ -652,18 +665,15 @@ class Person(Object, base.Profile):
|
||||||
|
|
||||||
@image_urls.setter
|
@image_urls.setter
|
||||||
def image_urls(self, value):
|
def image_urls(self, value):
|
||||||
self._image_urls = value
|
if value != {'large':'', 'medium':'', 'small':''}:
|
||||||
if value.get('large'):
|
self._image_urls = value
|
||||||
try:
|
if value.get('large'):
|
||||||
profile_icon = base.Image(url=value.get('large'))
|
try:
|
||||||
if profile_icon.media_type:
|
profile_icon = base.Image(url=value.get('large'))
|
||||||
self.icon = [Image.from_base(profile_icon)]
|
if profile_icon.media_type:
|
||||||
except Exception as ex:
|
self.icon = [Image.from_base(profile_icon)]
|
||||||
logger.warning("models.Person - failed to set profile icon: %s", ex)
|
except Exception as ex:
|
||||||
|
logger.warning("models.Person - failed to set profile icon: %s", ex)
|
||||||
def to_base(self):
|
|
||||||
set_public(self)
|
|
||||||
return self
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
rdf_type = as2.Person
|
rdf_type = as2.Person
|
||||||
|
@ -988,11 +998,10 @@ class Follow(Activity, base.Follow):
|
||||||
def to_as2(self):
|
def to_as2(self):
|
||||||
if not self.following:
|
if not self.following:
|
||||||
self.activity = Undo(
|
self.activity = Undo(
|
||||||
activity_id = self.activity_id,
|
activity_id = self.activity_id if self.activity_id else f"{self.actor_id}#follow-{uuid.uuid4()}",
|
||||||
actor_id = self.actor_id,
|
actor_id = self.actor_id,
|
||||||
object_ = self
|
object_ = self
|
||||||
)
|
)
|
||||||
self.activity_id = f"{self.actor_id}#follow-{uuid.uuid4()}"
|
|
||||||
|
|
||||||
return super().to_as2()
|
return super().to_as2()
|
||||||
|
|
||||||
|
@ -1067,10 +1076,8 @@ class Announce(Activity, base.Share):
|
||||||
self.activity = self.activity(
|
self.activity = self.activity(
|
||||||
activity_id = self.activity_id if self.activity_id else f"{self.actor_id}#share-{uuid.uuid4()}",
|
activity_id = self.activity_id if self.activity_id else f"{self.actor_id}#share-{uuid.uuid4()}",
|
||||||
actor_id = self.actor_id,
|
actor_id = self.actor_id,
|
||||||
created_at = self.created_at,
|
|
||||||
object_ = self
|
object_ = self
|
||||||
)
|
)
|
||||||
self.id = f"{self.target_id}"
|
|
||||||
|
|
||||||
return super().to_as2()
|
return super().to_as2()
|
||||||
|
|
||||||
|
@ -1097,7 +1104,7 @@ class Tombstone(Object, base.Retraction):
|
||||||
def to_as2(self):
|
def to_as2(self):
|
||||||
if not isinstance(self.activity, type): return None
|
if not isinstance(self.activity, type): return None
|
||||||
self.activity = self.activity(
|
self.activity = self.activity(
|
||||||
activity_id = self.activity_id,
|
activity_id = self.activity_id if self.activity_id else f"{self.actor_id}#delete-{uuid.uuid4()}",
|
||||||
actor_id = self.actor_id,
|
actor_id = self.actor_id,
|
||||||
created_at = self.created_at,
|
created_at = self.created_at,
|
||||||
object_ = self,
|
object_ = self,
|
||||||
|
@ -1201,8 +1208,8 @@ def extract_receivers(entity):
|
||||||
receivers = []
|
receivers = []
|
||||||
profile = None
|
profile = None
|
||||||
# don't care about receivers for payloads without an actor_id
|
# don't care about receivers for payloads without an actor_id
|
||||||
with rc.enabled(cache_name='fed_cache', backend=backend):
|
if getattr(entity, 'actor_id'):
|
||||||
if getattr(entity, 'actor_id'):
|
with rc.enabled(cache_name='fed_cache', backend=backend):
|
||||||
profile = retrieve_and_parse_profile(entity.actor_id)
|
profile = retrieve_and_parse_profile(entity.actor_id)
|
||||||
if not profile: return receivers
|
if not profile: return receivers
|
||||||
|
|
||||||
|
@ -1262,11 +1269,10 @@ def element_to_objects(element: Union[Dict, Object]) -> List:
|
||||||
|
|
||||||
# json-ld handling with calamus
|
# json-ld handling with calamus
|
||||||
# Skips unimplemented payloads
|
# Skips unimplemented payloads
|
||||||
# TODO: remove unused code
|
|
||||||
entity = model_to_objects(element) if not isinstance(element, Object) else element
|
entity = model_to_objects(element) if not isinstance(element, Object) else element
|
||||||
#if entity: entity = entity.to_base()
|
|
||||||
if entity and hasattr(entity, 'to_base'):
|
if entity and hasattr(entity, 'to_base'):
|
||||||
entity = entity.to_base()
|
entity = entity.to_base()
|
||||||
|
if isinstance(entity, BaseEntity):
|
||||||
try:
|
try:
|
||||||
extract_and_validate(entity)
|
extract_and_validate(entity)
|
||||||
except ValueError as ex:
|
except ValueError as ex:
|
||||||
|
|
|
@ -358,6 +358,7 @@ def handle_send(
|
||||||
# Do actual sending
|
# Do actual sending
|
||||||
for payload in payloads:
|
for payload in payloads:
|
||||||
for url in payload["urls"]:
|
for url in payload["urls"]:
|
||||||
|
# Comment this out for testing
|
||||||
#try:
|
#try:
|
||||||
# pprint(json.loads(payload["payload"]))
|
# pprint(json.loads(payload["payload"]))
|
||||||
#except:
|
#except:
|
||||||
|
|
|
@ -15,7 +15,7 @@ def disable_network_calls(monkeypatch):
|
||||||
"""Disable network calls."""
|
"""Disable network calls."""
|
||||||
monkeypatch.setattr("requests.post", Mock())
|
monkeypatch.setattr("requests.post", Mock())
|
||||||
|
|
||||||
class MockResponse(str):
|
class MockGetResponse(str):
|
||||||
status_code = 200
|
status_code = 200
|
||||||
text = ""
|
text = ""
|
||||||
|
|
||||||
|
@ -29,8 +29,17 @@ def disable_network_calls(monkeypatch):
|
||||||
return saved_get(*args, **kwargs)
|
return saved_get(*args, **kwargs)
|
||||||
return DEFAULT
|
return DEFAULT
|
||||||
|
|
||||||
monkeypatch.setattr("requests.get", Mock(return_value=MockResponse, side_effect=side_effect))
|
monkeypatch.setattr("requests.get", Mock(return_value=MockGetResponse, side_effect=side_effect))
|
||||||
|
|
||||||
|
class MockHeadResponse(dict):
|
||||||
|
status_code = 200
|
||||||
|
headers = {'Content-Type':'image/jpeg'}
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def raise_for_status():
|
||||||
|
pass
|
||||||
|
|
||||||
|
monkeypatch.setattr("requests.head", Mock(return_value=MockHeadResponse))
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def private_key():
|
def private_key():
|
||||||
|
|
|
@ -376,10 +376,12 @@ class TestEntitiesConvertToAS2:
|
||||||
'id': 'http://127.0.0.1:8000/post/123456/#delete',
|
'id': 'http://127.0.0.1:8000/post/123456/#delete',
|
||||||
'actor': 'http://127.0.0.1:8000/profile/123456/',
|
'actor': 'http://127.0.0.1:8000/profile/123456/',
|
||||||
'object': {
|
'object': {
|
||||||
|
'actor': 'http://127.0.0.1:8000/profile/123456/',
|
||||||
'id': 'http://127.0.0.1:8000/post/123456/activity',
|
'id': 'http://127.0.0.1:8000/post/123456/activity',
|
||||||
|
'object': 'http://127.0.0.1:8000/post/123456',
|
||||||
'type': 'Announce',
|
'type': 'Announce',
|
||||||
|
'published': '2019-04-27T00:00:00',
|
||||||
},
|
},
|
||||||
'published': '2019-04-27T00:00:00',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from unittest.mock import patch, Mock
|
from unittest.mock import patch, Mock, DEFAULT
|
||||||
|
|
||||||
|
import json
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
#from federation.entities.activitypub.entities import (
|
#from federation.entities.activitypub.entities import (
|
||||||
|
@ -8,12 +9,13 @@ import pytest
|
||||||
# models.Delete, models.Announce)
|
# models.Delete, models.Announce)
|
||||||
import federation.entities.activitypub.models as models
|
import federation.entities.activitypub.models as models
|
||||||
from federation.entities.activitypub.mappers import message_to_objects, get_outbound_entity
|
from federation.entities.activitypub.mappers import message_to_objects, get_outbound_entity
|
||||||
from federation.entities.base import Accept, Follow, Profile, Post, Comment, Image, Share
|
from federation.entities.base import Accept, Follow, Profile, Post, Comment, Image, Share, Retraction
|
||||||
from federation.tests.fixtures.payloads import (
|
from federation.tests.fixtures.payloads import (
|
||||||
ACTIVITYPUB_FOLLOW, ACTIVITYPUB_PROFILE, ACTIVITYPUB_PROFILE_INVALID, ACTIVITYPUB_UNDO_FOLLOW, ACTIVITYPUB_POST,
|
ACTIVITYPUB_FOLLOW, ACTIVITYPUB_PROFILE, ACTIVITYPUB_PROFILE_INVALID, ACTIVITYPUB_UNDO_FOLLOW, ACTIVITYPUB_POST,
|
||||||
ACTIVITYPUB_COMMENT, ACTIVITYPUB_RETRACTION, ACTIVITYPUB_SHARE, ACTIVITYPUB_RETRACTION_SHARE,
|
ACTIVITYPUB_COMMENT, ACTIVITYPUB_RETRACTION, ACTIVITYPUB_SHARE, ACTIVITYPUB_RETRACTION_SHARE,
|
||||||
ACTIVITYPUB_POST_IMAGES, ACTIVITYPUB_POST_WITH_SOURCE_MARKDOWN, ACTIVITYPUB_POST_WITH_TAGS,
|
ACTIVITYPUB_POST_IMAGES, ACTIVITYPUB_POST_WITH_SOURCE_MARKDOWN, ACTIVITYPUB_POST_WITH_TAGS,
|
||||||
ACTIVITYPUB_POST_WITH_SOURCE_BBCODE, ACTIVITYPUB_POST_WITH_MENTIONS, ACTIVITYPUB_PROFILE_WITH_DIASPORA_GUID)
|
ACTIVITYPUB_POST_WITH_SOURCE_BBCODE, ACTIVITYPUB_POST_WITH_MENTIONS, ACTIVITYPUB_PROFILE_WITH_DIASPORA_GUID,
|
||||||
|
ACTIVITYPUB_REMOTE_PROFILE, ACTIVITYPUB_COLLECTION)
|
||||||
from federation.types import UserType, ReceiverVariant
|
from federation.types import UserType, ReceiverVariant
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,7 +219,20 @@ class TestActivitypubEntityMappersReceive:
|
||||||
assert profile.id == "https://friendica.feneas.org/profile/feneas"
|
assert profile.id == "https://friendica.feneas.org/profile/feneas"
|
||||||
assert profile.guid == "76158462365bd347844d248732383358"
|
assert profile.guid == "76158462365bd347844d248732383358"
|
||||||
|
|
||||||
def test_message_to_objects_receivers_are_saved(self):
|
@patch('federation.utils.activitypub.fetch_document')
|
||||||
|
def test_message_to_objects_receivers_are_saved(self, mock_fetch):
|
||||||
|
def side_effect(*args, **kwargs):
|
||||||
|
payloads = {'https://diaspodon.fr/users/jaywink': json.dumps(ACTIVITYPUB_PROFILE),
|
||||||
|
'https://fosstodon.org/users/astdenis': json.dumps(ACTIVITYPUB_REMOTE_PROFILE),
|
||||||
|
'https://diaspodon.fr/users/jaywink/followers': json.dumps(ACTIVITYPUB_COLLECTION),
|
||||||
|
}
|
||||||
|
if args[0] in payloads.keys():
|
||||||
|
return payloads[args[0]], 200, None
|
||||||
|
else:
|
||||||
|
return DEFAULT
|
||||||
|
|
||||||
|
mock_fetch.side_effect = side_effect
|
||||||
|
|
||||||
# noinspection PyTypeChecker
|
# noinspection PyTypeChecker
|
||||||
entities = message_to_objects(
|
entities = message_to_objects(
|
||||||
ACTIVITYPUB_POST,
|
ACTIVITYPUB_POST,
|
||||||
|
@ -230,7 +245,7 @@ class TestActivitypubEntityMappersReceive:
|
||||||
id='https://diaspodon.fr/users/jaywink', receiver_variant=ReceiverVariant.FOLLOWERS,
|
id='https://diaspodon.fr/users/jaywink', receiver_variant=ReceiverVariant.FOLLOWERS,
|
||||||
),
|
),
|
||||||
UserType(
|
UserType(
|
||||||
id='https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
|
id='https://fosstodon.org/users/astdenis',
|
||||||
receiver_variant=ReceiverVariant.ACTOR,
|
receiver_variant=ReceiverVariant.ACTOR,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -239,7 +254,7 @@ class TestActivitypubEntityMappersReceive:
|
||||||
entities = message_to_objects(ACTIVITYPUB_RETRACTION, "https://friendica.feneas.org/profile/jaywink")
|
entities = message_to_objects(ACTIVITYPUB_RETRACTION, "https://friendica.feneas.org/profile/jaywink")
|
||||||
assert len(entities) == 1
|
assert len(entities) == 1
|
||||||
entity = entities[0]
|
entity = entities[0]
|
||||||
assert isinstance(entity, models.Delete)
|
assert isinstance(entity, Retraction)
|
||||||
assert entity.actor_id == "https://friendica.feneas.org/profile/jaywink"
|
assert entity.actor_id == "https://friendica.feneas.org/profile/jaywink"
|
||||||
assert entity.target_id == "https://friendica.feneas.org/objects/76158462-165d-3386-aa23-ba2090614385"
|
assert entity.target_id == "https://friendica.feneas.org/objects/76158462-165d-3386-aa23-ba2090614385"
|
||||||
assert entity.entity_type == "Object"
|
assert entity.entity_type == "Object"
|
||||||
|
@ -248,7 +263,7 @@ class TestActivitypubEntityMappersReceive:
|
||||||
entities = message_to_objects(ACTIVITYPUB_RETRACTION_SHARE, "https://mastodon.social/users/jaywink")
|
entities = message_to_objects(ACTIVITYPUB_RETRACTION_SHARE, "https://mastodon.social/users/jaywink")
|
||||||
assert len(entities) == 1
|
assert len(entities) == 1
|
||||||
entity = entities[0]
|
entity = entities[0]
|
||||||
assert isinstance(entity, models.Announce)
|
assert isinstance(entity, Retraction)
|
||||||
assert entity.actor_id == "https://mastodon.social/users/jaywink"
|
assert entity.actor_id == "https://mastodon.social/users/jaywink"
|
||||||
assert entity.target_id == "https://mastodon.social/users/jaywink/statuses/102571932479036987/activity"
|
assert entity.target_id == "https://mastodon.social/users/jaywink/statuses/102571932479036987/activity"
|
||||||
assert entity.entity_type == "Object"
|
assert entity.entity_type == "Object"
|
||||||
|
|
|
@ -19,7 +19,7 @@ class TestGetBaseAttributes:
|
||||||
assert set(attrs) == {
|
assert set(attrs) == {
|
||||||
"created_at", "location", "provider_display_name", "public", "raw_content",
|
"created_at", "location", "provider_display_name", "public", "raw_content",
|
||||||
"signature", "base_url", "actor_id", "id", "handle", "guid", "activity", "activity_id",
|
"signature", "base_url", "actor_id", "id", "handle", "guid", "activity", "activity_id",
|
||||||
"url", "mxid",
|
"url", "mxid", "times",
|
||||||
}
|
}
|
||||||
entity = Profile()
|
entity = Profile()
|
||||||
attrs = get_base_attributes(entity).keys()
|
attrs = get_base_attributes(entity).keys()
|
||||||
|
@ -27,7 +27,7 @@ class TestGetBaseAttributes:
|
||||||
"created_at", "name", "email", "gender", "raw_content", "location", "public",
|
"created_at", "name", "email", "gender", "raw_content", "location", "public",
|
||||||
"nsfw", "public_key", "image_urls", "tag_list", "signature", "url", "atom_url",
|
"nsfw", "public_key", "image_urls", "tag_list", "signature", "url", "atom_url",
|
||||||
"base_url", "id", "actor_id", "handle", "handle", "guid", "activity", "activity_id", "username",
|
"base_url", "id", "actor_id", "handle", "handle", "guid", "activity", "activity_id", "username",
|
||||||
"inboxes", "mxid",
|
"inboxes", "mxid", "times",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -180,7 +180,7 @@ https://jasonrobinson.me/media/uploads/2019/07/16/daa24d89-cedf-4fc7-bad8-74a902
|
||||||
def activitypubprofile(mock_fetch):
|
def activitypubprofile(mock_fetch):
|
||||||
with freeze_time("2022-09-06"):
|
with freeze_time("2022-09-06"):
|
||||||
return models.Person(
|
return models.Person(
|
||||||
id="https://example.com/bob/", raw_content="foobar", name="Bob Bobertson", public=True,
|
id="https://example.com/bob", raw_content="foobar", name="Bob Bobertson", public=True,
|
||||||
tag_list=["socialfederation", "federation"], image_urls={
|
tag_list=["socialfederation", "federation"], image_urls={
|
||||||
"large": "urllarge", "medium": "urlmedium", "small": "urlsmall"
|
"large": "urllarge", "medium": "urlmedium", "small": "urlsmall"
|
||||||
}, inboxes={
|
}, inboxes={
|
||||||
|
@ -195,7 +195,7 @@ def activitypubprofile(mock_fetch):
|
||||||
def activitypubprofile_diaspora_guid(mock_fetch):
|
def activitypubprofile_diaspora_guid(mock_fetch):
|
||||||
with freeze_time("2022-09-06"):
|
with freeze_time("2022-09-06"):
|
||||||
return models.Person(
|
return models.Person(
|
||||||
id="https://example.com/bob/", raw_content="foobar", name="Bob Bobertson", public=True,
|
id="https://example.com/bob", raw_content="foobar", name="Bob Bobertson", public=True,
|
||||||
tag_list=["socialfederation", "federation"], image_urls={
|
tag_list=["socialfederation", "federation"], image_urls={
|
||||||
"large": "urllarge", "medium": "urlmedium", "small": "urlsmall"
|
"large": "urllarge", "medium": "urlmedium", "small": "urlsmall"
|
||||||
}, inboxes={
|
}, inboxes={
|
||||||
|
@ -222,7 +222,8 @@ def activitypubretraction():
|
||||||
def activitypubretraction_announce():
|
def activitypubretraction_announce():
|
||||||
with freeze_time("2019-04-27"):
|
with freeze_time("2019-04-27"):
|
||||||
obj = Retraction(
|
obj = Retraction(
|
||||||
target_id="http://127.0.0.1:8000/post/123456/activity",
|
id="http://127.0.0.1:8000/post/123456/activity",
|
||||||
|
target_id="http://127.0.0.1:8000/post/123456",
|
||||||
activity_id="http://127.0.0.1:8000/post/123456/#delete",
|
activity_id="http://127.0.0.1:8000/post/123456/#delete",
|
||||||
actor_id="http://127.0.0.1:8000/profile/123456/",
|
actor_id="http://127.0.0.1:8000/profile/123456/",
|
||||||
entity_type="Share",
|
entity_type="Share",
|
||||||
|
|
|
@ -128,6 +128,85 @@ ACTIVITYPUB_PROFILE = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ACTIVITYPUB_REMOTE_PROFILE = {
|
||||||
|
"@context": ["https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{"Curve25519Key": "toot:Curve25519Key",
|
||||||
|
"Device": "toot:Device",
|
||||||
|
"Ed25519Key": "toot:Ed25519Key",
|
||||||
|
"Ed25519Signature": "toot:Ed25519Signature",
|
||||||
|
"EncryptedMessage": "toot:EncryptedMessage",
|
||||||
|
"PropertyValue": "schema:PropertyValue",
|
||||||
|
"alsoKnownAs": {"@id": "as:alsoKnownAs", "@type": "@id"},
|
||||||
|
"cipherText": "toot:cipherText",
|
||||||
|
"claim": {"@id": "toot:claim", "@type": "@id"},
|
||||||
|
"deviceId": "toot:deviceId",
|
||||||
|
"devices": {"@id": "toot:devices", "@type": "@id"},
|
||||||
|
"discoverable": "toot:discoverable",
|
||||||
|
"featured": {"@id": "toot:featured", "@type": "@id"},
|
||||||
|
"featuredTags": {"@id": "toot:featuredTags", "@type": "@id"},
|
||||||
|
"fingerprintKey": {"@id": "toot:fingerprintKey", "@type": "@id"},
|
||||||
|
"focalPoint": {"@container": "@list", "@id": "toot:focalPoint"},
|
||||||
|
"identityKey": {"@id": "toot:identityKey", "@type": "@id"},
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"messageFranking": "toot:messageFranking",
|
||||||
|
"messageType": "toot:messageType",
|
||||||
|
"movedTo": {"@id": "as:movedTo", "@type": "@id"},
|
||||||
|
"publicKeyBase64": "toot:publicKeyBase64",
|
||||||
|
"schema": "http://schema.org#",
|
||||||
|
"suspended": "toot:suspended",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"value": "schema:value"}],
|
||||||
|
"attachment": [{"name": "OS", "type": "PropertyValue", "value": "Manjaro"},
|
||||||
|
{"name": "Self Hosting",
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"value": "Matrix HS, Nextcloud"}],
|
||||||
|
"devices": "https://fosstodon.org/users/astdenis/collections/devices",
|
||||||
|
"discoverable": True,
|
||||||
|
"endpoints": {"sharedInbox": "https://fosstodon.org/inbox"},
|
||||||
|
"featured": "https://fosstodon.org/users/astdenis/collections/featured",
|
||||||
|
"featuredTags": "https://fosstodon.org/users/astdenis/collections/tags",
|
||||||
|
"followers": "https://fosstodon.org/users/astdenis/followers",
|
||||||
|
"following": "https://fosstodon.org/users/astdenis/following",
|
||||||
|
"icon": {"mediaType": "image/jpeg",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://cdn.fosstodon.org/accounts/avatars/000/252/976/original/09b7067cde009950.jpg"},
|
||||||
|
"id": "https://fosstodon.org/users/astdenis",
|
||||||
|
"image": {"mediaType": "image/jpeg",
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://cdn.fosstodon.org/accounts/headers/000/252/976/original/555a1ac1819e4e7f.jpg"},
|
||||||
|
"inbox": "https://fosstodon.org/users/astdenis/inbox",
|
||||||
|
"manuallyApprovesFollowers": False,
|
||||||
|
"name": "Alain",
|
||||||
|
"outbox": "https://fosstodon.org/users/astdenis/outbox",
|
||||||
|
"preferredUsername": "astdenis",
|
||||||
|
"publicKey": {"id": "https://fosstodon.org/users/astdenis#main-key",
|
||||||
|
"owner": "https://fosstodon.org/users/astdenis",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\n"
|
||||||
|
"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuaoIq/b+aUNqGAJNYF76\n"
|
||||||
|
"WY8tk49Vb1udyb7X+oseBXYtOwCDGfbZMalnFfqur1bAzogkKzuyjCeA3BfVs6R3\n"
|
||||||
|
"Cll897kUveMNHVc24pslhOx5ZzwpNT8e4q97dNaeHWLSLH5H+4JJGbeoD23G5SaY\n"
|
||||||
|
"9ZKt5iP+qRUlO/kSsUPwqsX9i2qSEqzwDiSvyRYhvvx4O588cUaaY9rAliLgtc/P\n"
|
||||||
|
"4EID3v6Edexe2QosUaghwGbb8zZWsYq0O4Umn2QMN4LzmQ0FjP+lq1TFX8FkGDZH\n"
|
||||||
|
"lnP+AMEQMyuac9Yb12t4RwvdsAIk4MXhAKvutMJm/X1GVQIyrsLEmvAO3rgk8dMr\n"
|
||||||
|
"6QIDAQAB\n"
|
||||||
|
"-----END PUBLIC KEY-----\n"},
|
||||||
|
"published": "2020-07-25T00:00:00Z",
|
||||||
|
"summary": "<p>Linux user and sysadmin since 1994, retired from the HPC field "
|
||||||
|
"since 2019.</p><p>Utilisateur et sysadmin Linux depuis 1994, "
|
||||||
|
"retraité du domaine du CHP depuis 2019.</p>",
|
||||||
|
"tag": [],
|
||||||
|
"type": "Person",
|
||||||
|
"url": "https://fosstodon.org/@astdenis"
|
||||||
|
}
|
||||||
|
|
||||||
|
ACTIVITYPUB_COLLECTION = {
|
||||||
|
"@context": "https://www.w3.org/ns/activitystreams",
|
||||||
|
"id": "https://diaspodon.fr/users/jaywink/followers",
|
||||||
|
"totalItems": 231,
|
||||||
|
"type": "OrderedCollection"
|
||||||
|
}
|
||||||
|
|
||||||
ACTIVITYPUB_PROFILE_INVALID = {
|
ACTIVITYPUB_PROFILE_INVALID = {
|
||||||
"@context": [
|
"@context": [
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
@ -313,7 +392,7 @@ ACTIVITYPUB_POST = {
|
||||||
'published': '2019-06-29T21:08:45Z',
|
'published': '2019-06-29T21:08:45Z',
|
||||||
'to': 'https://www.w3.org/ns/activitystreams#Public',
|
'to': 'https://www.w3.org/ns/activitystreams#Public',
|
||||||
'cc': ['https://diaspodon.fr/users/jaywink/followers',
|
'cc': ['https://diaspodon.fr/users/jaywink/followers',
|
||||||
'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/'],
|
'https://fosstodon.org/users/astdenis'],
|
||||||
'object': {'id': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237',
|
'object': {'id': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237',
|
||||||
'type': 'Note',
|
'type': 'Note',
|
||||||
'summary': None,
|
'summary': None,
|
||||||
|
@ -323,7 +402,7 @@ ACTIVITYPUB_POST = {
|
||||||
'attributedTo': 'https://diaspodon.fr/users/jaywink',
|
'attributedTo': 'https://diaspodon.fr/users/jaywink',
|
||||||
'to': 'https://www.w3.org/ns/activitystreams#Public',
|
'to': 'https://www.w3.org/ns/activitystreams#Public',
|
||||||
'cc': ['https://diaspodon.fr/users/jaywink/followers',
|
'cc': ['https://diaspodon.fr/users/jaywink/followers',
|
||||||
'https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/'],
|
'https://fosstodon.org/users/astdenis'],
|
||||||
'sensitive': False,
|
'sensitive': False,
|
||||||
'atomUri': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237',
|
'atomUri': 'https://diaspodon.fr/users/jaywink/statuses/102356911717767237',
|
||||||
'inReplyToAtomUri': None,
|
'inReplyToAtomUri': None,
|
||||||
|
|
|
@ -50,35 +50,39 @@ class TestRetrieveAndParseDocument:
|
||||||
"https://example.com/foobar", extra_headers={'accept': 'application/activity+json'}, auth=auth,
|
"https://example.com/foobar", extra_headers={'accept': 'application/activity+json'}, auth=auth,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@patch("federation.entities.activitypub.models.extract_receivers", return_value=[])
|
||||||
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
||||||
json.dumps(ACTIVITYPUB_FOLLOW), None, None),
|
json.dumps(ACTIVITYPUB_FOLLOW), None, None),
|
||||||
)
|
)
|
||||||
@patch.object(Follow, "post_receive")
|
@patch.object(Follow, "post_receive")
|
||||||
def test_returns_entity_for_valid_document__follow(self, mock_post_receive, mock_fetch):
|
def test_returns_entity_for_valid_document__follow(self, mock_post_receive, mock_fetch, mock_recv):
|
||||||
entity = retrieve_and_parse_document("https://example.com/foobar")
|
entity = retrieve_and_parse_document("https://example.com/foobar")
|
||||||
assert isinstance(entity, Follow)
|
assert isinstance(entity, Follow)
|
||||||
|
|
||||||
|
@patch("federation.entities.activitypub.models.extract_receivers", return_value=[])
|
||||||
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
||||||
json.dumps(ACTIVITYPUB_POST_OBJECT), None, None),
|
json.dumps(ACTIVITYPUB_POST_OBJECT), None, None),
|
||||||
)
|
)
|
||||||
def test_returns_entity_for_valid_document__post__without_activity(self, mock_fetch):
|
def test_returns_entity_for_valid_document__post__without_activity(self, mock_fetch, mock_recv):
|
||||||
entity = retrieve_and_parse_document("https://example.com/foobar")
|
entity = retrieve_and_parse_document("https://example.com/foobar")
|
||||||
assert isinstance(entity, Note)
|
assert isinstance(entity, Note)
|
||||||
|
|
||||||
|
@patch("federation.entities.activitypub.models.extract_receivers", return_value=[])
|
||||||
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
||||||
json.dumps(ACTIVITYPUB_POST_OBJECT_IMAGES), None, None),
|
json.dumps(ACTIVITYPUB_POST_OBJECT_IMAGES), None, None),
|
||||||
)
|
)
|
||||||
def test_returns_entity_for_valid_document__post__without_activity__with_images(self, mock_fetch):
|
def test_returns_entity_for_valid_document__post__without_activity__with_images(self, mock_fetch, mock_recv):
|
||||||
entity = retrieve_and_parse_document("https://example.com/foobar")
|
entity = retrieve_and_parse_document("https://example.com/foobar")
|
||||||
assert isinstance(entity, Note)
|
assert isinstance(entity, Note)
|
||||||
assert len(entity._children) == 1
|
assert len(entity._children) == 1
|
||||||
assert entity._children[0].url == "https://files.mastodon.social/media_attachments/files/017/792/237/original" \
|
assert entity._children[0].url == "https://files.mastodon.social/media_attachments/files/017/792/237/original" \
|
||||||
"/foobar.jpg"
|
"/foobar.jpg"
|
||||||
|
|
||||||
|
@patch("federation.entities.activitypub.models.extract_receivers", return_value=[])
|
||||||
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
@patch("federation.utils.activitypub.fetch_document", autospec=True, return_value=(
|
||||||
json.dumps(ACTIVITYPUB_POST), None, None),
|
json.dumps(ACTIVITYPUB_POST), None, None),
|
||||||
)
|
)
|
||||||
def test_returns_entity_for_valid_document__post__wrapped_in_activity(self, mock_fetch):
|
def test_returns_entity_for_valid_document__post__wrapped_in_activity(self, mock_fetch, mock_recv):
|
||||||
entity = retrieve_and_parse_document("https://example.com/foobar")
|
entity = retrieve_and_parse_document("https://example.com/foobar")
|
||||||
assert isinstance(entity, Note)
|
assert isinstance(entity, Note)
|
||||||
|
|
||||||
|
|
Ładowanie…
Reference in New Issue