kopia lustrzana https://gitlab.com/jaywink/federation
341 wiersze
17 KiB
Python
341 wiersze
17 KiB
Python
from datetime import datetime
|
|
from unittest.mock import patch, Mock
|
|
|
|
import pytest
|
|
|
|
from federation.entities.activitypub.entities import (
|
|
ActivitypubFollow, ActivitypubAccept, ActivitypubProfile, ActivitypubPost, ActivitypubComment,
|
|
ActivitypubRetraction, ActivitypubShare)
|
|
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.tests.fixtures.payloads import (
|
|
ACTIVITYPUB_FOLLOW, ACTIVITYPUB_PROFILE, ACTIVITYPUB_PROFILE_INVALID, ACTIVITYPUB_UNDO_FOLLOW, ACTIVITYPUB_POST,
|
|
ACTIVITYPUB_COMMENT, ACTIVITYPUB_RETRACTION, ACTIVITYPUB_SHARE, ACTIVITYPUB_RETRACTION_SHARE,
|
|
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)
|
|
from federation.types import UserType, ReceiverVariant
|
|
|
|
|
|
class TestActivitypubEntityMappersReceive:
|
|
@patch.object(ActivitypubFollow, "post_receive", autospec=True)
|
|
def test_message_to_objects__calls_post_receive_hook(self, mock_post_receive):
|
|
message_to_objects(ACTIVITYPUB_FOLLOW, "https://example.com/actor")
|
|
assert mock_post_receive.called
|
|
|
|
def test_message_to_objects__announce(self):
|
|
entities = message_to_objects(ACTIVITYPUB_SHARE, "https://mastodon.social/users/jaywink")
|
|
assert len(entities) == 1
|
|
entity = entities[0]
|
|
assert isinstance(entity, ActivitypubShare)
|
|
assert entity.actor_id == "https://mastodon.social/users/jaywink"
|
|
assert entity.target_id == "https://mastodon.social/users/Gargron/statuses/102559779793316012"
|
|
assert entity.id == "https://mastodon.social/users/jaywink/statuses/102560701449465612/activity"
|
|
assert entity.public is True
|
|
assert entity.entity_type == "Post"
|
|
assert entity.raw_content == ""
|
|
|
|
def test_message_to_objects__follow(self):
|
|
entities = message_to_objects(ACTIVITYPUB_FOLLOW, "https://example.com/actor")
|
|
assert len(entities) == 1
|
|
entity = entities[0]
|
|
assert isinstance(entity, ActivitypubFollow)
|
|
assert entity.actor_id == "https://example.com/actor"
|
|
assert entity.target_id == "https://example.org/actor"
|
|
assert entity.following is True
|
|
|
|
def test_message_to_objects__unfollow(self):
|
|
entities = message_to_objects(ACTIVITYPUB_UNDO_FOLLOW, "https://example.com/actor")
|
|
assert len(entities) == 1
|
|
entity = entities[0]
|
|
assert isinstance(entity, ActivitypubFollow)
|
|
assert entity.actor_id == "https://example.com/actor"
|
|
assert entity.target_id == "https://example.org/actor"
|
|
assert entity.following is False
|
|
|
|
@pytest.mark.skip
|
|
def test_message_to_objects_mentions_are_extracted(self):
|
|
entities = message_to_objects(
|
|
DIASPORA_POST_SIMPLE_WITH_MENTION, "alice@alice.diaspora.example.org"
|
|
)
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert post._mentions == {'jaywink@jasonrobinson.me'}
|
|
|
|
def test_message_to_objects_simple_post(self):
|
|
entities = message_to_objects(ACTIVITYPUB_POST, "https://diaspodon.fr/users/jaywink")
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert isinstance(post, ActivitypubPost)
|
|
assert isinstance(post, Post)
|
|
assert post.raw_content == '<p><span class="h-card"><a class="u-url mention" ' \
|
|
'href="https://dev.jasonrobinson.me/u/jaywink/">' \
|
|
'@<span>jaywink</span></a></span> boom</p>'
|
|
assert post.rendered_content == '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/">' \
|
|
'@<span>jaywink</span></a></span> boom</p>'
|
|
assert post.id == "https://diaspodon.fr/users/jaywink/statuses/102356911717767237"
|
|
assert post.actor_id == "https://diaspodon.fr/users/jaywink"
|
|
assert post.public is True
|
|
assert post._media_type == "text/html"
|
|
assert getattr(post, "target_id", None) is None
|
|
|
|
def test_message_to_objects_simple_post__with_tags(self):
|
|
entities = message_to_objects(ACTIVITYPUB_POST_WITH_TAGS, "https://diaspodon.fr/users/jaywink")
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert isinstance(post, ActivitypubPost)
|
|
assert isinstance(post, Post)
|
|
assert post.raw_content == '<p>boom #test</p>'
|
|
|
|
def test_message_to_objects_simple_post__with_mentions(self):
|
|
entities = message_to_objects(ACTIVITYPUB_POST_WITH_MENTIONS, "https://mastodon.social/users/jaywink")
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert isinstance(post, ActivitypubPost)
|
|
assert isinstance(post, Post)
|
|
assert len(post._mentions) == 1
|
|
assert list(post._mentions)[0] == "https://dev3.jasonrobinson.me/u/jaywink/"
|
|
|
|
def test_message_to_objects_simple_post__with_source__bbcode(self):
|
|
entities = message_to_objects(ACTIVITYPUB_POST_WITH_SOURCE_BBCODE, "https://diaspodon.fr/users/jaywink")
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert isinstance(post, ActivitypubPost)
|
|
assert isinstance(post, Post)
|
|
assert post.rendered_content == '<p><span class="h-card"><a class="u-url mention" href="https://dev.jasonrobinson.me/u/jaywink/">' \
|
|
'@<span>jaywink</span></a></span> boom</p>'
|
|
assert post.raw_content == '<p><span class="h-card"><a class="u-url mention" ' \
|
|
'href="https://dev.jasonrobinson.me/u/jaywink/">' \
|
|
'@<span>jaywink</span></a></span> boom</p>'
|
|
|
|
def test_message_to_objects_simple_post__with_source__markdown(self):
|
|
entities = message_to_objects(ACTIVITYPUB_POST_WITH_SOURCE_MARKDOWN, "https://diaspodon.fr/users/jaywink")
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert isinstance(post, ActivitypubPost)
|
|
assert isinstance(post, Post)
|
|
assert post.rendered_content == '<p><span class="h-card"><a href="https://dev.jasonrobinson.me/u/jaywink/" ' \
|
|
'class="u-url mention">@<span>jaywink</span></a></span> boom</p>'
|
|
assert post.raw_content == "@jaywink boom"
|
|
assert post.id == "https://diaspodon.fr/users/jaywink/statuses/102356911717767237"
|
|
assert post.actor_id == "https://diaspodon.fr/users/jaywink"
|
|
assert post.public is True
|
|
assert post._media_type == "text/markdown"
|
|
assert getattr(post, "target_id", None) is None
|
|
|
|
def test_message_to_objects_post_with_photos(self):
|
|
entities = message_to_objects(ACTIVITYPUB_POST_IMAGES, "https://mastodon.social/users/jaywink")
|
|
assert len(entities) == 1
|
|
post = entities[0]
|
|
assert isinstance(post, ActivitypubPost)
|
|
# TODO: test video and audio attachment
|
|
assert len(post._children) == 2
|
|
photo = post._children[0]
|
|
assert isinstance(photo, Image)
|
|
assert photo.url == "https://files.mastodon.social/media_attachments/files/017/642/079/original/" \
|
|
"f51b0aee0ee1f2e1.jpg"
|
|
assert photo.name == ""
|
|
assert photo.raw_content == ""
|
|
assert photo.height == 0
|
|
assert photo.width == 0
|
|
assert photo.guid == ""
|
|
assert photo.handle == ""
|
|
|
|
def test_message_to_objects_comment(self):
|
|
entities = message_to_objects(ACTIVITYPUB_COMMENT, "https://diaspodon.fr/users/jaywink")
|
|
assert len(entities) == 1
|
|
comment = entities[0]
|
|
assert isinstance(comment, ActivitypubComment)
|
|
assert isinstance(comment, Comment)
|
|
assert comment.raw_content == '<p><span class="h-card"><a class="u-url mention" ' \
|
|
'href="https://dev.jasonrobinson.me/u/jaywink/">' \
|
|
'@<span>jaywink</span></a></span> boom</p>'
|
|
assert comment.id == "https://diaspodon.fr/users/jaywink/statuses/102356911717767237"
|
|
assert comment.actor_id == "https://diaspodon.fr/users/jaywink"
|
|
assert comment.target_id == "https://dev.jasonrobinson.me/content/653bad70-41b3-42c9-89cb-c4ee587e68e4/"
|
|
|
|
@pytest.mark.skip
|
|
def test_message_to_objects_like(self, mock_validate):
|
|
entities = message_to_objects(
|
|
DIASPORA_POST_LIKE, "alice@alice.diaspora.example.org", sender_key_fetcher=Mock()
|
|
)
|
|
assert len(entities) == 1
|
|
like = entities[0]
|
|
assert isinstance(like, DiasporaLike)
|
|
assert isinstance(like, Reaction)
|
|
assert like.target_guid == "((parent_guidparent_guidparent_guidparent_guid))"
|
|
assert like.guid == "((guidguidguidguidguidguid))"
|
|
assert like.handle == "alice@alice.diaspora.example.org"
|
|
assert like.participation == "reaction"
|
|
assert like.reaction == "like"
|
|
assert like.signature == "((signature))"
|
|
assert like._xml_tags == [
|
|
"parent_type", "guid", "parent_guid", "positive", "author",
|
|
]
|
|
mock_validate.assert_called_once_with()
|
|
|
|
def test_message_to_objects_profile(self):
|
|
entities = message_to_objects(ACTIVITYPUB_PROFILE, "http://example.com/1234")
|
|
assert len(entities) == 1
|
|
profile = entities[0]
|
|
assert profile.id == "https://diaspodon.fr/users/jaywink"
|
|
assert profile.inboxes == {
|
|
"private": "https://diaspodon.fr/users/jaywink/inbox",
|
|
"public": "https://diaspodon.fr/inbox",
|
|
}
|
|
assert profile.handle == ""
|
|
assert profile.name == "Jason Robinson"
|
|
assert profile.image_urls == {
|
|
"large": "https://diaspodon.fr/system/accounts/avatars/000/033/155/original/pnc__picked_media_be51984c-4"
|
|
"3e9-4266-9b9a-b74a61ae4167.jpg?1538505110",
|
|
"medium": "https://diaspodon.fr/system/accounts/avatars/000/033/155/original/pnc__picked_media_be51984c-4"
|
|
"3e9-4266-9b9a-b74a61ae4167.jpg?1538505110",
|
|
"small": "https://diaspodon.fr/system/accounts/avatars/000/033/155/original/pnc__picked_media_be51984c-4"
|
|
"3e9-4266-9b9a-b74a61ae4167.jpg?1538505110",
|
|
}
|
|
assert profile.gender == ""
|
|
assert profile.raw_content == "<p>Temp account while implementing AP for Socialhome.</p><p><a href=\"" \
|
|
"https://jasonrobinson.me\" rel=\"nofollow noopener\" target=\"_blank\">" \
|
|
"<span class=\"invisible\">https://</span><span class=\"\">jasonrobinson." \
|
|
"me</span><span class=\"invisible\"></span></a> / <a href=\"https://social" \
|
|
"home.network\" rel=\"nofollow noopener\" target=\"_blank\"><span class=\"i" \
|
|
"nvisible\">https://</span><span class=\"\">socialhome.network</span><span c" \
|
|
"lass=\"invisible\"></span></a> / <a href=\"https://feneas.org\" rel=\"nofoll" \
|
|
"ow noopener\" target=\"_blank\"><span class=\"invisible\">https://</span><spa" \
|
|
"n class=\"\">feneas.org</span><span class=\"invisible\"></span></a></p>"
|
|
assert profile.location == ""
|
|
assert profile.public is True
|
|
assert profile.nsfw is False
|
|
assert profile.tag_list == []
|
|
|
|
def test_message_to_objects_profile__diaspora_guid_extracted(self):
|
|
entities = message_to_objects(
|
|
ACTIVITYPUB_PROFILE_WITH_DIASPORA_GUID, "https://friendica.feneas.org/profile/feneas",
|
|
)
|
|
assert len(entities) == 1
|
|
profile = entities[0]
|
|
assert profile.id == "https://friendica.feneas.org/profile/feneas"
|
|
assert profile.guid == "76158462365bd347844d248732383358"
|
|
|
|
def test_message_to_objects_receivers_are_saved(self):
|
|
# noinspection PyTypeChecker
|
|
entities = message_to_objects(
|
|
ACTIVITYPUB_POST,
|
|
"https://diaspodon.fr/users/jaywink",
|
|
)
|
|
entity = entities[0]
|
|
|
|
assert set(entity._receivers) == {
|
|
UserType(
|
|
id='https://diaspodon.fr/users/jaywink', receiver_variant=ReceiverVariant.FOLLOWERS,
|
|
),
|
|
UserType(
|
|
id='https://dev.jasonrobinson.me/p/d4574854-a5d7-42be-bfac-f70c16fcaa97/',
|
|
receiver_variant=ReceiverVariant.ACTOR,
|
|
)
|
|
}
|
|
|
|
def test_message_to_objects_retraction(self):
|
|
entities = message_to_objects(ACTIVITYPUB_RETRACTION, "https://friendica.feneas.org/profile/jaywink")
|
|
assert len(entities) == 1
|
|
entity = entities[0]
|
|
assert isinstance(entity, ActivitypubRetraction)
|
|
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.entity_type == "Object"
|
|
|
|
def test_message_to_objects_retraction__share(self):
|
|
entities = message_to_objects(ACTIVITYPUB_RETRACTION_SHARE, "https://mastodon.social/users/jaywink")
|
|
assert len(entities) == 1
|
|
entity = entities[0]
|
|
assert isinstance(entity, ActivitypubRetraction)
|
|
assert entity.actor_id == "https://mastodon.social/users/jaywink"
|
|
assert entity.target_id == "https://mastodon.social/users/jaywink/statuses/102571932479036987/activity"
|
|
assert entity.entity_type == "Object"
|
|
|
|
@pytest.mark.skip
|
|
def test_message_to_objects_reshare_extra_properties(self):
|
|
entities = message_to_objects(DIASPORA_RESHARE_WITH_EXTRA_PROPERTIES, "alice@example.org")
|
|
assert len(entities) == 1
|
|
entity = entities[0]
|
|
assert isinstance(entity, DiasporaReshare)
|
|
assert entity.raw_content == "Important note here"
|
|
assert entity.entity_type == "Comment"
|
|
|
|
@patch("federation.entities.activitypub.mappers.logger.error")
|
|
def test_invalid_entity_logs_an_error(self, mock_logger):
|
|
entities = message_to_objects(ACTIVITYPUB_PROFILE_INVALID, "http://example.com/1234")
|
|
assert len(entities) == 0
|
|
assert mock_logger.called
|
|
|
|
def test_adds_source_protocol_to_entity(self):
|
|
entities = message_to_objects(ACTIVITYPUB_PROFILE, "http://example.com/1234")
|
|
assert entities[0]._source_protocol == "activitypub"
|
|
|
|
@pytest.mark.skip
|
|
# since calamus turns the whole payload into objects, the source payload is not kept
|
|
def test_source_object(self):
|
|
entities = message_to_objects(ACTIVITYPUB_PROFILE, "http://example.com/1234")
|
|
entity = entities[0]
|
|
assert entity._source_object == ACTIVITYPUB_PROFILE
|
|
|
|
@pytest.mark.skip
|
|
def test_element_to_objects_calls_retrieve_remote_profile(self, mock_retrieve, mock_validate):
|
|
message_to_objects(DIASPORA_POST_COMMENT, "alice@alice.diaspora.example.org")
|
|
mock_retrieve.assert_called_once_with("alice@alice.diaspora.example.org")
|
|
|
|
@pytest.mark.skip
|
|
def test_element_to_objects_verifies_handles_are_the_same(self, mock_check):
|
|
message_to_objects(DIASPORA_POST_SIMPLE, "bob@example.org")
|
|
mock_check.assert_called_once_with("bob@example.org", "alice@alice.diaspora.example.org")
|
|
|
|
@pytest.mark.skip
|
|
def test_element_to_objects_returns_no_entity_if_handles_are_different(self):
|
|
entities = message_to_objects(DIASPORA_POST_SIMPLE, "bob@example.org")
|
|
assert not entities
|
|
|
|
|
|
class TestGetOutboundEntity:
|
|
def test_already_fine_entities_are_returned_as_is(self, private_key):
|
|
entity = ActivitypubAccept()
|
|
entity.validate = Mock()
|
|
assert get_outbound_entity(entity, private_key) == entity
|
|
entity = ActivitypubFollow()
|
|
entity.validate = Mock()
|
|
assert get_outbound_entity(entity, private_key) == entity
|
|
entity = ActivitypubProfile()
|
|
entity.validate = Mock()
|
|
assert get_outbound_entity(entity, private_key) == entity
|
|
|
|
@patch.object(ActivitypubAccept, "validate", new=Mock())
|
|
def test_accept_is_converted_to_activitypubaccept(self, private_key):
|
|
entity = Accept()
|
|
assert isinstance(get_outbound_entity(entity, private_key), ActivitypubAccept)
|
|
|
|
@patch.object(ActivitypubFollow, "validate", new=Mock())
|
|
def test_follow_is_converted_to_activitypubfollow(self, private_key):
|
|
entity = Follow()
|
|
assert isinstance(get_outbound_entity(entity, private_key), ActivitypubFollow)
|
|
|
|
@patch.object(ActivitypubProfile, "validate", new=Mock())
|
|
def test_profile_is_converted_to_activitypubprofile(self, private_key):
|
|
entity = Profile()
|
|
assert isinstance(get_outbound_entity(entity, private_key), ActivitypubProfile)
|
|
|
|
def test_entity_is_validated__fail(self, private_key):
|
|
entity = Share(
|
|
actor_id="https://localhost.local/foo",
|
|
id="https://localhost.local/bar",
|
|
created_at=datetime.now(),
|
|
)
|
|
with pytest.raises(ValueError):
|
|
get_outbound_entity(entity, private_key)
|
|
|
|
def test_entity_is_validated__success(self, private_key):
|
|
entity = Share(
|
|
actor_id="https://localhost.local/foo",
|
|
id="https://localhost.local/bar",
|
|
created_at=datetime.now(),
|
|
target_id="https://localhost.local/bar",
|
|
)
|
|
get_outbound_entity(entity, private_key)
|