2023-02-01 21:19:41 +00:00
""" Unit tests for webmention.py. """
2017-08-15 14:39:22 +00:00
import copy
2023-12-05 16:51:27 +00:00
from datetime import timedelta
2023-12-04 18:28:45 +00:00
from unittest . mock import ANY , patch
2017-08-15 14:39:22 +00:00
2023-05-31 00:24:49 +00:00
from flask import g , get_flashed_messages
2023-07-09 15:28:47 +00:00
from google . cloud import ndb
2024-01-01 22:00:52 +00:00
from granary import as1 , as2 , atom , microformats2 , rss
2023-06-20 18:22:54 +00:00
from oauth_dropins . webutil import util
2023-09-19 18:15:49 +00:00
from oauth_dropins . webutil import appengine_info
2024-01-01 22:47:03 +00:00
from oauth_dropins . webutil . testutil import NOW , NOW_SECONDS , requests_response
2019-12-25 07:26:58 +00:00
from oauth_dropins . webutil . util import json_dumps , json_loads
2017-08-15 14:39:22 +00:00
import requests
2023-04-05 01:02:41 +00:00
from werkzeug . exceptions import BadGateway , BadRequest
2017-08-15 14:39:22 +00:00
2023-05-30 23:36:18 +00:00
# import first so that Fake is defined before URL routes are registered
from . import testutil
2023-11-27 19:51:52 +00:00
from activitypub import ActivityPub
2023-10-31 19:49:15 +00:00
import common
2023-06-20 18:22:54 +00:00
from common import CONTENT_TYPE_HTML
2023-12-01 00:31:41 +00:00
from flask_app import app
2023-06-20 18:22:54 +00:00
from models import Follower , Object
2023-12-04 18:28:45 +00:00
import web
2024-01-05 02:32:39 +00:00
from web import TASKS_LOCATION , Web
2023-06-02 19:55:07 +00:00
from . import test_activitypub
2023-11-30 05:06:55 +00:00
from . testutil import Fake , TestCase
2023-06-05 20:20:07 +00:00
FULL_REDIR = requests_response (
status = 302 ,
redirected_url = ' http://localhost/.well-known/webfinger?resource=acct:user.com@user.com ' )
2017-08-15 14:39:22 +00:00
2023-01-24 00:09:25 +00:00
ACTOR_HTML = """ \
< html >
< body class = " h-card " >
2023-03-17 23:42:45 +00:00
< a class = " p-name u-url " rel = " me " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2023-01-24 00:09:25 +00:00
< a href = " http://localhost/ " > < / a >
< / body >
< / html >
"""
2023-07-25 04:28:37 +00:00
ACTOR_HTML_RESP = requests_response ( ACTOR_HTML , url = ' https://user.com/ ' )
2023-11-29 20:00:48 +00:00
ACTOR_HTML_METAFORMATS = """ \
< html >
< head >
< title > Ms . ☕ Baz < / title >
< meta property = " article:author " content = " / " / >
< / head >
< / html >
"""
2023-01-24 00:09:25 +00:00
ACTOR_MF2 = {
' type ' : [ ' h-card ' ] ,
' properties ' : {
2023-03-17 23:42:45 +00:00
' url ' : [ ' https://user.com/ ' ] ,
2023-01-24 00:09:25 +00:00
' name ' : [ ' Ms. ☕ Baz ' ] ,
} ,
}
2023-04-05 01:02:41 +00:00
ACTOR_MF2_REL_URLS = {
* * ACTOR_MF2 ,
' rel-urls ' : { ' https://user.com/ ' : { ' rels ' : [ ' me ' ] , ' text ' : ' Ms. ☕ Baz ' } }
}
2023-12-04 19:34:19 +00:00
ACTOR_MF2_REL_FEED_URL = {
* * ACTOR_MF2 ,
' rel-urls ' : {
' https://foo ' : { ' rels ' : [ ' me ' ] , ' text ' : ' Ms. ☕ Baz ' } ,
2024-01-10 20:52:14 +00:00
' https://foo/feed ' : { ' rels ' : [ ' alternate ' ] , ' type ' : atom . CONTENT_TYPE } ,
2023-12-04 19:34:19 +00:00
} ,
}
2023-01-29 17:45:03 +00:00
ACTOR_AS1_UNWRAPPED = {
' objectType ' : ' person ' ,
2023-07-10 17:14:12 +00:00
' id ' : ' https://user.com/ ' ,
2023-03-17 23:42:45 +00:00
' url ' : ' https://user.com/ ' ,
2023-07-10 17:14:12 +00:00
' displayName ' : ' Ms. ☕ Baz ' ,
2023-01-29 17:45:03 +00:00
}
2024-01-08 21:03:44 +00:00
ACTOR_AS1_UNWRAPPED_URLS = {
* * ACTOR_AS1_UNWRAPPED ,
' urls ' : [ { ' value ' : ' https://user.com/ ' , ' displayName ' : ' Ms. ☕ Baz ' } ] ,
}
2023-01-24 00:09:25 +00:00
ACTOR_AS2 = {
2024-01-11 23:16:33 +00:00
' type ' : ' Application ' ,
2023-03-17 23:42:45 +00:00
' id ' : ' http://localhost/user.com ' ,
' url ' : ' http://localhost/r/https://user.com/ ' ,
2023-01-24 00:09:25 +00:00
' name ' : ' Ms. ☕ Baz ' ,
2024-01-11 23:16:33 +00:00
' summary ' : ' [<a href= " https://fed.brid.gy/web/user.com " >bridged</a> from <a href= " https://user.com/ " >user.com</a> by <a href= " https://fed.brid.gy/ " >Bridgy Fed</a>] ' ,
2023-03-17 23:42:45 +00:00
' preferredUsername ' : ' user.com ' ,
2023-08-19 22:11:59 +00:00
' inbox ' : ' http://localhost/user.com/inbox ' ,
' outbox ' : ' http://localhost/user.com/outbox ' ,
2023-01-24 00:09:25 +00:00
}
ACTOR_AS2_FULL = {
* * ACTOR_AS2 ,
' @context ' : [
' https://www.w3.org/ns/activitystreams ' ,
' https://w3id.org/security/v1 ' ,
] ,
' attachment ' : [ {
2023-03-17 23:42:45 +00:00
' name ' : ' Web site ' ,
2023-01-24 00:09:25 +00:00
' type ' : ' PropertyValue ' ,
2023-10-24 23:02:16 +00:00
' value ' : ' <a rel= " me " href= " https://user.com " ><span class= " invisible " >https://</span>user.com</a> ' ,
2023-01-24 00:09:25 +00:00
} ] ,
2023-03-17 23:42:45 +00:00
' inbox ' : ' http://localhost/user.com/inbox ' ,
' outbox ' : ' http://localhost/user.com/outbox ' ,
' following ' : ' http://localhost/user.com/following ' ,
' followers ' : ' http://localhost/user.com/followers ' ,
' endpoints ' : {
' sharedInbox ' : ' http://localhost/inbox ' ,
} ,
}
2023-01-24 00:09:25 +00:00
2019-01-04 15:17:45 +00:00
REPOST_HTML = """ \
< html >
< body class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-url " href = " https://user.com/repost " > < / a >
2023-04-18 02:20:22 +00:00
< a class = " u-repost-of p-name " href = " https://mas.to/toot/id " > reposted ! < / a >
2023-04-05 01:02:41 +00:00
< a class = " u-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2019-01-04 15:17:45 +00:00
< a href = " http://localhost/ " > < / a >
< / body >
< / html >
"""
2023-07-25 04:28:37 +00:00
REPOST = requests_response ( REPOST_HTML , url = ' https://user.com/repost ' )
2023-04-05 01:02:41 +00:00
REPOST_MF2 = util . parse_mf2 ( REPOST_HTML ) [ ' items ' ] [ 0 ]
2019-01-04 15:17:45 +00:00
REPOST_AS2 = {
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Announce ' ,
2023-03-17 23:42:45 +00:00
' id ' : ' http://localhost/r/https://user.com/repost ' ,
' url ' : ' http://localhost/r/https://user.com/repost ' ,
2019-01-04 15:17:45 +00:00
' name ' : ' reposted! ' ,
2023-03-17 23:42:45 +00:00
' object ' : ' https://mas.to/toot/id ' ,
2022-08-24 00:37:50 +00:00
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
2019-01-04 15:17:45 +00:00
' cc ' : [
2023-03-17 23:42:45 +00:00
' https://mas.to/author ' ,
' https://mas.to/bystander ' ,
' https://mas.to/recipient ' ,
2022-08-24 00:37:50 +00:00
as2 . PUBLIC_AUDIENCE ,
2019-01-04 15:17:45 +00:00
] ,
2023-03-19 16:32:12 +00:00
' actor ' : ' http://localhost/user.com ' ,
2019-01-04 15:17:45 +00:00
}
2023-04-19 21:37:42 +00:00
REPOST_HCITE_HTML = """ \
< html >
< body class = " h-entry " >
2023-04-19 23:21:21 +00:00
< a class = " u-url p-name " href = " https://user.com/repost " > reposted ! < / a >
2023-04-19 21:37:42 +00:00
< div class = " u-repost-of h-cite " >
2023-04-19 23:21:21 +00:00
< a class = " p-author h-card " href = " https://mas.to/@foo " > Mr . Foo < / a > : < / p >
2023-04-19 21:37:42 +00:00
< a class = " u-url " href = " https://mas.to/toot/id " > a post < / a >
< / div >
< a class = " u-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
< a href = " http://localhost/ " > < / a >
< / body >
< / html >
"""
2023-07-25 04:28:37 +00:00
REPOST_HCITE = requests_response ( REPOST_HTML , url = ' https://user.com/repost ' )
2023-04-19 21:37:42 +00:00
2023-03-20 18:23:49 +00:00
WEBMENTION_REL_LINK = requests_response (
' <html><head><link rel= " webmention " href= " /webmention " ></html> ' )
WEBMENTION_NO_REL_LINK = requests_response ( ' <html></html> ' )
2017-08-15 14:39:22 +00:00
2023-04-17 22:36:29 +00:00
DELETE_AS1 = {
' objectType ' : ' activity ' ,
' verb ' : ' delete ' ,
' id ' : ' https://user.com/post#bridgy-fed-delete ' ,
' actor ' : ' http://localhost/user.com ' ,
' object ' : ' https://user.com/post ' ,
}
DELETE_AS2 = {
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Delete ' ,
' id ' : ' http://localhost/r/https://user.com/post#bridgy-fed-delete ' ,
' actor ' : ' http://localhost/user.com ' ,
' object ' : ' http://localhost/r/https://user.com/post ' ,
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
}
2021-07-11 15:48:28 +00:00
2023-06-05 20:20:07 +00:00
TOOT_HTML = requests_response ( """ \
2017-09-02 03:49:00 +00:00
< html >
< meta >
2023-03-17 23:42:45 +00:00
< link href = ' https://mas.to/toot/atom ' rel = ' alternate ' type = ' application/atom+xml ' >
< link href = ' https://mas.to/toot/id ' rel = ' alternate ' type = ' application/activity+json ' >
2017-09-02 03:49:00 +00:00
< / meta >
< / html >
2023-07-25 04:28:37 +00:00
""" , url= ' https://mas.to/toot ' )
2023-06-05 20:20:07 +00:00
TOOT_AS2_DATA = {
' @context ' : [ ' https://www.w3.org/ns/activitystreams ' ] ,
' type ' : ' Article ' ,
' id ' : ' https://mas.to/toot/id ' ,
' content ' : ' Lots of ☕ words... ' ,
' actor ' : { ' url ' : ' https://mas.to/author ' } ,
' to ' : [ ' https://mas.to/recipient ' , as2 . PUBLIC_AUDIENCE ] ,
' cc ' : [ ' https://mas.to/bystander ' , as2 . PUBLIC_AUDIENCE ] ,
}
TOOT_AS2 = requests_response (
TOOT_AS2_DATA , url = ' https://mas.to/toot/id ' ,
content_type = as2 . CONTENT_TYPE + ' ; charset=utf-8 ' )
REPLY_HTML = """ \
2017-08-23 15:14:51 +00:00
< html >
< body >
2017-08-15 14:39:22 +00:00
< div class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-url " href = " https://user.com/reply " > < / a >
2017-10-02 14:53:06 +00:00
< p class = " e-content p-name " >
2020-06-06 15:39:44 +00:00
< a class = " u-in-reply-to " href = " http://not/fediverse " > < / a >
2023-03-17 23:42:45 +00:00
< a class = " u-in-reply-to " href = " https://mas.to/toot " > foo ☕ bar < / a >
2018-11-27 15:27:00 +00:00
< a href = " http://localhost/ " > < / a >
2017-08-15 14:39:22 +00:00
< / p >
2023-03-17 23:42:45 +00:00
< a class = " p-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2017-08-15 14:39:22 +00:00
< / div >
2017-08-23 15:14:51 +00:00
< / body >
< / html >
2017-09-03 22:26:41 +00:00
"""
2023-07-25 04:28:37 +00:00
REPLY = requests_response ( REPLY_HTML , url = ' https://user.com/reply ' )
2023-06-05 20:20:07 +00:00
REPLY_MF2 = util . parse_mf2 ( REPLY_HTML ) [ ' items ' ] [ 0 ]
REPLY_AS1 = microformats2 . json_to_object ( REPLY_MF2 )
2023-07-10 18:37:40 +00:00
REPLY_AS1 [ ' id ' ] = ' https://user.com/reply '
REPLY_AS1 [ ' author ' ] [ ' id ' ] = ' https://user.com/ '
2023-06-05 20:20:07 +00:00
CREATE_REPLY_AS1 = {
' objectType ' : ' activity ' ,
' verb ' : ' post ' ,
' id ' : ' https://user.com/reply#bridgy-fed-create ' ,
2023-07-10 18:37:40 +00:00
' actor ' : ACTOR_AS1_UNWRAPPED ,
2023-06-05 20:20:07 +00:00
' object ' : REPLY_AS1 ,
2023-07-10 18:37:40 +00:00
' published ' : ' 2022-01-02T03:04:05+00:00 ' ,
2023-06-05 20:20:07 +00:00
}
REPLY_AS2 = as2 . from_as1 ( REPLY_AS1 )
2017-08-15 14:39:22 +00:00
2023-06-05 20:20:07 +00:00
LIKE_HTML = """ \
2017-10-18 02:39:00 +00:00
< html >
< body class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-url " href = " https://user.com/like " > < / a >
< a class = " u-like-of " href = " https://mas.to/toot " > < / a >
< ! - - < a class = " u-like-of p-name " href = " https://mas.to/toot " > liked ! < / a > - - >
< a class = " p-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2018-11-27 15:27:00 +00:00
< a href = " http://localhost/ " > < / a >
2017-10-18 02:39:00 +00:00
< / body >
< / html >
"""
2023-07-25 04:28:37 +00:00
LIKE = requests_response ( LIKE_HTML , url = ' https://user.com/like ' )
2023-06-05 20:20:07 +00:00
LIKE_MF2 = util . parse_mf2 ( LIKE_HTML ) [ ' items ' ] [ 0 ]
ACTOR = TestCase . as2_resp ( {
2023-06-20 18:22:54 +00:00
' type ' : ' Person ' ,
2023-06-16 04:22:20 +00:00
' name ' : ' Mrs. ☕ Foo ' ,
2023-06-05 20:20:07 +00:00
' id ' : ' https://mas.to/mrs-foo ' ,
' inbox ' : ' https://mas.to/inbox ' ,
} )
AS2_CREATE = {
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Create ' ,
' id ' : ' http://localhost/r/https://user.com/reply#bridgy-fed-create ' ,
' actor ' : ' http://localhost/user.com ' ,
2023-07-10 17:14:12 +00:00
' published ' : ' 2022-01-02T03:04:05+00:00 ' ,
2023-06-05 20:20:07 +00:00
' object ' : {
' type ' : ' Note ' ,
' id ' : ' http://localhost/r/https://user.com/reply ' ,
' url ' : ' http://localhost/r/https://user.com/reply ' ,
' name ' : ' foo ☕ bar ' ,
' content ' : """ \
2020-06-06 15:39:44 +00:00
< a class = " u-in-reply-to " href = " http://not/fediverse " > < / a >
2023-03-17 23:42:45 +00:00
< a class = " u-in-reply-to " href = " https://mas.to/toot " > foo ☕ bar < / a >
2020-06-06 15:39:44 +00:00
< a href = " http://localhost/ " > < / a > """ ,
2023-10-20 20:37:38 +00:00
' contentMap ' : {
' en ' : """ \
< a class = " u-in-reply-to " href = " http://not/fediverse " > < / a >
< a class = " u-in-reply-to " href = " https://mas.to/toot " > foo ☕ bar < / a >
< a href = " http://localhost/ " > < / a > """ ,
} ,
2023-06-05 20:20:07 +00:00
' inReplyTo ' : ' https://mas.to/toot/id ' ,
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
' cc ' : [
' https://mas.to/author ' ,
' https://mas.to/bystander ' ,
' https://mas.to/recipient ' ,
as2 . PUBLIC_AUDIENCE ,
] ,
2023-10-11 23:17:43 +00:00
' attributedTo ' : ' http://localhost/user.com ' ,
2023-06-05 20:20:07 +00:00
' tag ' : [ {
' type ' : ' Mention ' ,
' href ' : ' https://mas.to/author ' ,
} ] ,
} ,
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
}
2023-07-10 18:26:09 +00:00
AS2_UPDATE = copy . deepcopy ( AS2_CREATE )
AS2_UPDATE . update ( {
2023-06-05 20:20:07 +00:00
' id ' : ' http://localhost/r/https://user.com/reply#bridgy-fed-update-2022-01-02T03:04:05+00:00 ' ,
' type ' : ' Update ' ,
2023-07-10 18:26:09 +00:00
} )
2023-07-10 17:14:12 +00:00
del AS2_UPDATE [ ' published ' ]
2023-06-05 20:20:07 +00:00
# we should generate this if it's not already in mf2 because Mastodon
# requires it for updates
AS2_UPDATE [ ' object ' ] [ ' updated ' ] = NOW . isoformat ( )
FOLLOW_HTML = """ \
2018-10-25 04:28:52 +00:00
< html >
< body class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-url " href = " https://user.com/follow " > < / a >
< a class = " u-follow-of " href = " https://mas.to/mrs-foo " > < / a >
< a class = " p-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2018-11-27 15:27:00 +00:00
< a href = " http://localhost/ " > < / a >
2018-10-25 04:28:52 +00:00
< / body >
< / html >
"""
2023-07-25 04:28:37 +00:00
FOLLOW = requests_response ( FOLLOW_HTML , url = ' https://user.com/follow ' )
2023-06-05 20:20:07 +00:00
FOLLOW_MF2 = util . parse_mf2 ( FOLLOW_HTML ) [ ' items ' ] [ 0 ]
FOLLOW_AS1 = microformats2 . json_to_object ( FOLLOW_MF2 )
FOLLOW_AS2 = {
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Follow ' ,
' id ' : ' http://localhost/r/https://user.com/follow ' ,
' url ' : ' http://localhost/r/https://user.com/follow ' ,
' object ' : ' https://mas.to/mrs-foo ' ,
' actor ' : ' http://localhost/user.com ' ,
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
}
FOLLOW_FRAGMENT_HTML = """ \
2022-11-18 23:28:34 +00:00
< html >
< body >
< article class = h - entry id = 1 >
< h1 > Ignored < / h1 >
< / article >
< article class = h - entry id = 2 >
2023-03-17 23:42:45 +00:00
< a class = " u-url " href = " https://user.com/follow#2 " > < / a >
< a class = " u-follow-of " href = " https://mas.to/mrs-foo " > < / a >
< a class = " p-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2022-11-18 23:28:34 +00:00
< a href = " http://localhost/ " > < / a >
< / article >
< / body >
< / html >
"""
2023-06-05 20:20:07 +00:00
FOLLOW_FRAGMENT = requests_response (
2023-07-25 04:28:37 +00:00
FOLLOW_FRAGMENT_HTML , url = ' https://user.com/follow ' )
2023-06-05 20:20:07 +00:00
FOLLOW_FRAGMENT_MF2 = \
util . parse_mf2 ( FOLLOW_FRAGMENT_HTML , id = ' 2 ' ) [ ' items ' ] [ 0 ]
FOLLOW_FRAGMENT_AS2 = {
* * FOLLOW_AS2 ,
' id ' : ' http://localhost/r/https://user.com/follow#2 ' ,
' url ' : ' http://localhost/r/https://user.com/follow#2 ' ,
}
2022-11-18 23:28:34 +00:00
2023-06-05 20:20:07 +00:00
NOTE_HTML = """ \
2018-11-13 15:26:50 +00:00
< html >
< body class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-url " href = " https://user.com/post " > < / a >
2018-11-13 15:26:50 +00:00
< p class = " e-content p-name " > hello i am a post < / p >
2023-03-17 23:42:45 +00:00
< a class = " p-author h-card " href = " https://user.com/ " >
2021-10-23 04:17:45 +00:00
< p class = " p-name " > Ms . ☕ < span class = " p-nickname " > Baz < / span > < / p >
< / a >
2018-11-27 15:27:00 +00:00
< a href = " http://localhost/ " > < / a >
2018-11-13 15:26:50 +00:00
< / body >
< / html >
"""
2023-07-25 04:28:37 +00:00
NOTE = requests_response ( NOTE_HTML , url = ' https://user.com/post ' )
2023-06-05 20:20:07 +00:00
NOTE_MF2 = util . parse_mf2 ( NOTE_HTML ) [ ' items ' ] [ 0 ]
NOTE_AS1 = microformats2 . json_to_object ( NOTE_MF2 )
2023-07-10 17:14:12 +00:00
NOTE_AS1 . update ( {
' author ' : {
* * NOTE_AS1 [ ' author ' ] ,
' id ' : ' https://user.com/ ' ,
} ,
' id ' : ' https://user.com/post ' ,
} )
2023-06-05 20:20:07 +00:00
NOTE_AS2 = {
' type ' : ' Note ' ,
' id ' : ' http://localhost/r/https://user.com/post ' ,
' url ' : ' http://localhost/r/https://user.com/post ' ,
2023-10-11 23:17:43 +00:00
' attributedTo ' : ' http://localhost/user.com ' ,
2023-06-05 20:20:07 +00:00
' name ' : ' hello i am a post ' ,
' content ' : ' hello i am a post ' ,
2023-10-20 20:37:38 +00:00
' contentMap ' : { ' en ' : ' hello i am a post ' } ,
2023-06-05 20:20:07 +00:00
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
}
CREATE_AS1 = {
' objectType ' : ' activity ' ,
' verb ' : ' post ' ,
' id ' : ' https://user.com/post#bridgy-fed-create ' ,
2023-07-09 02:19:57 +00:00
' actor ' : ACTOR_AS1_UNWRAPPED ,
2023-06-05 20:20:07 +00:00
' object ' : NOTE_AS1 ,
2023-07-09 02:19:57 +00:00
' published ' : ' 2022-01-02T03:04:05+00:00 ' ,
2023-06-05 20:20:07 +00:00
}
CREATE_AS2 = {
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Create ' ,
' id ' : ' http://localhost/r/https://user.com/post#bridgy-fed-create ' ,
' actor ' : ' http://localhost/user.com ' ,
' object ' : NOTE_AS2 ,
2023-07-09 02:19:57 +00:00
' published ' : ' 2022-01-02T03:04:05+00:00 ' ,
2023-06-05 20:20:07 +00:00
' to ' : [ as2 . PUBLIC_AUDIENCE ] ,
}
UPDATE_AS2 = copy . deepcopy ( CREATE_AS2 )
UPDATE_AS2 . update ( {
' type ' : ' Update ' ,
' id ' : ' http://localhost/r/https://user.com/post#bridgy-fed-update-2022-01-02T03:04:05+00:00 ' ,
} )
2023-07-09 14:53:33 +00:00
del UPDATE_AS2 [ ' published ' ]
2023-06-05 20:20:07 +00:00
UPDATE_AS2 [ ' object ' ] [ ' updated ' ] = NOW . isoformat ( )
NOT_FEDIVERSE = requests_response ( """ \
2020-06-06 15:39:44 +00:00
< html >
2023-11-11 22:52:21 +00:00
< body class = " h-entry " >
< a class = " u-author h-card " href = " https://not/ " > Mr . Not < / a >
< div class = " e-content " > foo < / div >
< / body >
2020-06-06 15:39:44 +00:00
< / html >
2023-07-25 04:28:37 +00:00
""" , url= ' http://not/fediverse ' )
2023-06-21 03:59:32 +00:00
ACTIVITYPUB_GETS = [
REPLY ,
NOT_FEDIVERSE , # AP
NOT_FEDIVERSE , # Web
TOOT_AS2 , # AP
ACTOR ,
]
2023-06-05 20:20:07 +00:00
@patch ( ' requests.post ' )
@patch ( ' requests.get ' )
class WebTest ( TestCase ) :
def setUp ( self ) :
super ( ) . setUp ( )
2023-06-21 03:59:32 +00:00
obj = Object ( id = ' https://user.com/ ' , mf2 = ACTOR_MF2 , source_protocol = ' web ' )
obj . put ( )
2023-11-20 05:21:27 +00:00
self . user = self . make_user ( ' user.com ' , cls = Web , has_redirects = True , obj = obj )
2018-11-13 15:26:50 +00:00
2023-07-10 18:37:40 +00:00
self . mrs_foo = ndb . Key ( ActivityPub , ' https://mas.to/mrs-foo ' )
2023-04-18 02:20:22 +00:00
def assert_deliveries ( self , mock_post , inboxes , data , ignore = ( ) ) :
2023-07-10 17:14:12 +00:00
self . assertEqual ( len ( inboxes ) , len ( mock_post . call_args_list ) ,
mock_post . call_args_list )
2023-04-17 23:56:44 +00:00
calls = { } # maps inbox URL to JSON data
for args , kwargs in mock_post . call_args_list :
self . assertEqual ( as2 . CONTENT_TYPE , kwargs [ ' headers ' ] [ ' Content-Type ' ] )
rsa_key = kwargs [ ' auth ' ] . header_signer . _rsa . _key
2023-11-20 05:21:27 +00:00
self . assertEqual ( self . user . private_pem ( ) , rsa_key . exportKey ( ) )
2023-04-17 23:56:44 +00:00
calls [ args [ 0 ] ] = json_loads ( kwargs [ ' data ' ] )
2023-01-29 17:45:03 +00:00
for inbox in inboxes :
2023-04-17 23:56:44 +00:00
got = calls [ inbox ]
2023-04-17 22:36:29 +00:00
as1 . get_object ( got ) . pop ( ' publicKey ' , None )
2023-04-18 02:20:22 +00:00
self . assert_equals ( data , got , inbox , ignore = ignore )
2023-01-29 17:45:03 +00:00
2023-03-21 02:17:55 +00:00
def assert_object ( self , id , * * props ) :
return super ( ) . assert_object ( id , delivered_protocol = ' activitypub ' , * * props )
2023-07-09 15:28:47 +00:00
def make_followers ( self ) :
self . followers = [ ]
for id , kwargs , actor in [
( ' https://mastodon/aaa ' , { } , None ) ,
( ' https://mastodon/bbb ' , { } , {
' publicInbox ' : ' https://public/inbox ' ,
' inbox ' : ' https://unused ' ,
} ) ,
( ' https://mastodon/ccc ' , { } , {
' endpoints ' : {
' sharedInbox ' : ' https://shared/inbox ' ,
} ,
} ) ,
( ' https://mastodon/ddd ' , { } , {
' inbox ' : ' https://inbox ' ,
} ) ,
( ' https://mastodon/ggg ' , { ' status ' : ' inactive ' } , {
' inbox ' : ' https://unused/2 ' ,
} ) ,
( ' https://mastodon/hhh ' , { } , {
# dupe of ddd; should be de-duped
' inbox ' : ' https://inbox ' ,
} ) ,
] :
from_ = self . make_user ( id , cls = ActivityPub , obj_as2 = actor )
2023-11-20 05:21:27 +00:00
f = Follower . get_or_create ( to = self . user , from_ = from_ , * * kwargs )
2023-07-09 15:28:47 +00:00
if f . status != ' inactive ' :
self . followers . append ( from_ . key )
2023-06-09 17:58:28 +00:00
def test_put_validates_domain_id ( self , * _ ) :
for bad in (
' AbC.cOm ' ,
' foo ' ,
' @user.com ' ,
' @user.com@user.com ' ,
' acct:user.com ' ,
' acct:@user.com@user.com ' ,
' acc:me@user.com ' ,
2023-06-23 19:22:37 +00:00
' ap.brid.gy ' ,
' localhost ' ,
2023-06-09 17:58:28 +00:00
) :
with self . assertRaises ( AssertionError ) :
Web ( id = bad ) . put ( )
2023-10-19 22:01:19 +00:00
def test_get_or_create_lower_cases_domain ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( ' ' )
2023-06-09 17:58:28 +00:00
user = Web . get_or_create ( ' AbC.oRg ' )
self . assertEqual ( ' abc.org ' , user . key . id ( ) )
self . assert_entities_equal ( user , Web . get_by_id ( ' abc.org ' ) )
self . assertIsNone ( Web . get_by_id ( ' AbC.oRg ' ) )
2023-10-19 22:01:19 +00:00
def test_get_or_create_unicode_domain ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( ' ' )
2023-06-09 17:58:28 +00:00
user = Web . get_or_create ( ' ☃.net ' )
self . assertEqual ( ' ☃.net ' , user . key . id ( ) )
self . assert_entities_equal ( user , Web . get_by_id ( ' ☃.net ' ) )
2023-11-26 04:07:14 +00:00
def test_get_or_create_home_page_url ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( ' ' )
user = Web . get_or_create ( ' https://foo.com/ ' )
self . assertEqual ( ' foo.com ' , user . key . id ( ) )
self . assert_entities_equal ( user , Web . get_by_id ( ' foo.com ' ) )
2023-10-19 22:01:19 +00:00
def test_get_or_create_scripts_leading_trailing_dots ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( ' ' )
2023-08-18 20:30:26 +00:00
user = Web . get_or_create ( ' ..foo.bar. ' )
self . assertEqual ( ' foo.bar ' , user . key . id ( ) )
self . assert_entities_equal ( user , Web . get_by_id ( ' foo.bar ' ) )
self . assertIsNone ( Web . get_by_id ( ' ..foo.bar. ' ) )
2024-01-06 23:57:11 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
def test_get_or_create_existing_no_poll_feed_task ( self , mock_create_task , _ , __ ) :
2023-12-04 19:34:19 +00:00
user = Web . get_or_create ( ' user.com ' )
2024-01-06 23:57:11 +00:00
mock_create_task . assert_not_called ( )
2023-12-04 19:34:19 +00:00
2024-01-06 23:57:11 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
def test_get_or_create_new_creates_poll_feed_task ( self , mock_create_task ,
mock_get , __ ) :
common . RUN_TASKS_INLINE = False
mock_get . return_value = ACTOR_HTML_RESP
2023-12-04 19:34:19 +00:00
2024-01-06 23:57:11 +00:00
user = Web . get_or_create ( ' new.com ' )
self . assert_task ( mock_create_task , ' poll-feed ' , ' /queue/poll-feed ' ,
domain = ' new.com ' )
2023-11-26 23:37:39 +00:00
def test_get_or_create_existing_opted_out ( self , * _ ) :
self . user . obj . mf2 [ ' properties ' ] [ ' summary ' ] = ' #nobridge '
self . user . obj . put ( )
self . user . put ( )
self . assertIsNone ( Web . get_or_create ( ' user.com ' ) )
2023-06-14 20:34:29 +00:00
def test_bad_source_url ( self , * mocks ) :
2023-06-21 03:59:32 +00:00
orig_count = Object . query ( ) . count ( )
2024-01-04 02:32:57 +00:00
for data in [
b ' ' ,
{ ' source ' : ' bad ' } ,
{ ' source ' : ' https:// ' } ,
{ ' source ' : ' http://user.com/not/https ' } ,
] :
2023-10-18 04:50:19 +00:00
got = self . post ( ' /webmention ' , data = data )
2023-06-02 18:10:04 +00:00
self . assertEqual ( 400 , got . status_code )
2023-06-21 03:59:32 +00:00
self . assertEqual ( orig_count , Object . query ( ) . count ( ) )
2019-08-13 14:29:25 +00:00
2023-06-14 20:34:29 +00:00
def test_username ( self , * mocks ) :
2023-11-20 05:21:27 +00:00
self . assertEqual ( ' user.com ' , self . user . username ( ) )
2023-06-14 20:34:29 +00:00
2023-11-20 05:21:27 +00:00
self . user . obj = Object ( id = ' a ' , as2 = {
2023-06-14 20:34:29 +00:00
' type ' : ' Person ' ,
' name ' : ' foo ' ,
' url ' : [ ' bar ' ] ,
' preferredUsername ' : ' baz ' ,
2023-06-16 04:22:20 +00:00
} )
2023-11-20 05:21:27 +00:00
self . user . direct = True
self . assertEqual ( ' user.com ' , self . user . username ( ) )
2023-06-14 20:34:29 +00:00
# bad acct: URI, util.parse_acct_uri raises ValueError
# https://console.cloud.google.com/errors/detail/CPLmrpzFs4qTUA;time=P30D?project=bridgy-federated
2023-11-20 05:21:27 +00:00
self . user . obj . as2 [ ' url ' ] . append ( ' acct:@user.com ' )
self . assertEqual ( ' user.com ' , self . user . username ( ) )
2023-06-14 20:34:29 +00:00
2023-11-20 05:21:27 +00:00
self . user . obj . as2 [ ' url ' ] . append ( ' acct:alice@foo.com ' )
self . assertEqual ( ' user.com ' , self . user . username ( ) )
2023-06-14 20:34:29 +00:00
2023-11-20 05:21:27 +00:00
self . user . obj . as2 [ ' url ' ] . append ( ' acct:alice@user.com ' )
self . assertEqual ( ' alice ' , self . user . username ( ) )
2023-06-14 20:34:29 +00:00
2023-11-20 05:21:27 +00:00
self . user . direct = False
self . assertEqual ( ' user.com ' , self . user . username ( ) )
2023-06-14 20:34:29 +00:00
2023-05-31 00:24:49 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
2023-04-06 16:16:25 +00:00
def test_make_task ( self , mock_create_task , mock_get , mock_post ) :
2023-10-31 19:49:15 +00:00
common . RUN_TASKS_INLINE = False
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ NOTE , ACTOR ]
2023-04-06 16:16:25 +00:00
params = {
' source ' : ' https://user.com/post ' ,
' target ' : ' https://fed.brid.gy/ ' ,
}
2023-10-18 04:50:19 +00:00
got = self . post ( ' /webmention ' , data = params )
2023-04-06 16:16:25 +00:00
self . assertEqual ( 202 , got . status_code )
2023-09-29 20:49:17 +00:00
self . assert_task ( mock_create_task , ' webmention ' , ' /queue/webmention ' ,
2023-09-13 21:36:24 +00:00
* * params )
2023-04-06 16:16:25 +00:00
2023-12-04 05:46:31 +00:00
self . assertEqual ( NOW , self . user . key . get ( ) . last_webmention_in )
2023-04-06 16:16:25 +00:00
def test_no_user ( self , mock_get , mock_post ) :
2023-06-21 03:59:32 +00:00
orig_count = Object . query ( ) . count ( )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /webmention ' , data = { ' source ' : ' https://nope.com/post ' } )
2023-04-06 16:16:25 +00:00
self . assertEqual ( 400 , got . status_code )
2023-06-21 03:59:32 +00:00
self . assertEqual ( orig_count , Object . query ( ) . count ( ) )
2023-04-06 16:16:25 +00:00
2022-12-21 05:52:26 +00:00
def test_source_fetch_fails ( self , mock_get , mock_post ) :
2023-06-21 03:59:32 +00:00
orig_count = Object . query ( ) . count ( )
2022-12-21 05:52:26 +00:00
mock_get . side_effect = (
2023-07-25 04:28:37 +00:00
requests_response ( REPLY_HTML , status = 405 ) ,
2022-12-21 05:52:26 +00:00
)
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' ,
2023-04-06 16:16:25 +00:00
data = { ' source ' : ' https://user.com/post ' } )
2022-12-21 05:52:26 +00:00
self . assertEqual ( 502 , got . status_code )
2023-06-21 03:59:32 +00:00
self . assertEqual ( orig_count , Object . query ( ) . count ( ) )
2022-12-21 05:52:26 +00:00
2018-03-27 14:04:33 +00:00
def test_no_source_entry ( self , mock_get , mock_post ) :
2023-06-21 03:59:32 +00:00
orig_count = Object . query ( ) . count ( )
2018-03-27 14:04:33 +00:00
mock_get . return_value = requests_response ( """
< html >
< body >
2018-11-27 15:27:00 +00:00
< p > nothing to see here except < a href = " http://localhost/ " > link < / a > < / p >
2018-03-27 14:04:33 +00:00
< / body >
2023-07-25 04:28:37 +00:00
< / html > """ , url= ' https://user.com/post ' )
2018-03-27 14:04:33 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/post ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-04-06 16:16:25 +00:00
self . assertEqual ( 304 , got . status_code )
2023-06-21 03:59:32 +00:00
self . assertEqual ( orig_count , Object . query ( ) . count ( ) )
2018-03-27 14:04:33 +00:00
2023-03-17 23:42:45 +00:00
mock_get . assert_has_calls ( ( self . req ( ' https://user.com/post ' ) , ) )
2018-03-27 14:04:33 +00:00
2018-11-27 15:44:27 +00:00
def test_no_targets ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( """
< html >
< body class = " h-entry " >
2023-07-10 19:11:29 +00:00
< p class = " e-content " > no one to send to ! < / p >
2018-11-27 15:44:27 +00:00
< / body >
2023-07-10 19:11:29 +00:00
< a href = " http://localhost/ " > < / a >
2023-07-25 04:28:37 +00:00
< / html > """ , url= ' https://user.com/post ' )
2018-11-27 15:44:27 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/post ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
2018-11-27 15:44:27 +00:00
2023-03-17 23:42:45 +00:00
mock_get . assert_has_calls ( ( self . req ( ' https://user.com/post ' ) , ) )
2018-11-27 15:44:27 +00:00
2019-08-13 14:29:25 +00:00
def test_bad_target_url ( self , mock_get , mock_post ) :
mock_get . side_effect = (
2023-04-02 02:13:51 +00:00
requests_response (
2023-11-11 22:52:21 +00:00
REPLY_HTML . replace ( ' https://mas.to/toot ' , ' bad:nope ' ) \
. replace ( ' http://not/fediverse ' , ' ' ) ,
2023-04-02 02:13:51 +00:00
content_type = CONTENT_TYPE_HTML , url = ' https://user.com/reply ' ) ,
2023-06-21 03:59:32 +00:00
ValueError ( ' foo bar ' ) , # AS2 fetch
ValueError ( ' foo bar ' ) , # HTML fetch
2023-01-29 15:31:22 +00:00
)
2019-08-13 14:29:25 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' ,
2023-11-11 22:52:21 +00:00
data = { ' source ' : ' https://user.com/reply ' } )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
self . assert_object ( ' https://user.com/reply ' ,
source_protocol = ' web ' ,
type = ' comment ' ,
2023-07-09 15:28:47 +00:00
labels = [ ] ,
2024-01-08 21:03:44 +00:00
ignore = [ ' mf2 ' , ' our_as1 ' ] ,
2023-07-09 15:28:47 +00:00
)
self . assert_object ( ' https://user.com/reply#bridgy-fed-create ' ,
source_protocol = ' web ' ,
our_as1 = CREATE_REPLY_AS1 ,
type = ' post ' ,
labels = [ ' activity ' , ' user ' ] ,
ignore = [ ' our_as1 ' ] ,
2023-06-21 03:59:32 +00:00
status = ' ignored ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-06-21 03:59:32 +00:00
)
2019-08-13 14:29:25 +00:00
2022-12-21 05:52:26 +00:00
def test_target_fetch_fails ( self , mock_get , mock_post ) :
2023-06-21 03:59:32 +00:00
mock_get . side_effect = [
2023-04-02 02:13:51 +00:00
requests_response (
2023-06-21 03:59:32 +00:00
REPLY_HTML . replace ( ' https://mas.to/toot ' , ' bad:nope ' ) ,
2023-07-25 04:28:37 +00:00
url = ' https://user.com/post ' ) ,
2023-06-21 03:59:32 +00:00
# http://not/fediverse AP protocol discovery
requests . Timeout ( ' foo bar ' ) ,
# http://not/fediverse web protocol discovery
requests . Timeout ( ' foo bar ' ) ,
]
2019-08-13 14:23:26 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' ,
2023-04-06 16:16:25 +00:00
data = { ' source ' : ' https://user.com/reply ' } )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
2019-08-13 14:23:26 +00:00
2021-08-16 18:47:31 +00:00
def test_target_fetch_has_no_content_type ( self , mock_get , mock_post ) :
2023-06-21 03:59:32 +00:00
Object ( id = ' http://not/fediverse ' , mf2 = NOTE_MF2 , source_protocol = ' web ' ) . put ( )
no_content_type = requests_response ( REPLY_HTML , content_type = ' ' )
2020-05-21 06:35:38 +00:00
mock_get . side_effect = (
2023-06-05 20:20:07 +00:00
requests_response ( REPLY_HTML , url = ' https://user.com/reply ' ) ,
2023-06-21 03:59:32 +00:00
# requests:
no_content_type , # https://mas.to/toot AP protocol discovery
no_content_type , # https://mas.to/toot Web protocol discovery
no_content_type , # https://user.com/ webmention discovery
no_content_type , # http://not/fediverse webmention discovery
2020-05-21 06:35:38 +00:00
)
2023-12-05 20:56:20 +00:00
got = self . post ( ' /queue/webmention ' , data = { ' source ' : ' https://user.com/reply ' } )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
mock_post . assert_not_called ( )
2020-05-21 06:35:38 +00:00
2023-05-23 16:31:09 +00:00
def test_missing_backlink ( self , mock_get , mock_post ) :
2023-06-21 03:59:32 +00:00
orig_count = Object . query ( ) . count ( )
2018-11-27 15:27:00 +00:00
mock_get . return_value = requests_response (
2023-06-05 20:20:07 +00:00
REPLY_HTML . replace ( ' <a href= " http://localhost/ " ></a> ' , ' ' ) ,
2023-07-25 04:28:37 +00:00
url = ' https://user.com/reply ' )
2018-11-27 15:27:00 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-04-06 16:16:25 +00:00
self . assertEqual ( 304 , got . status_code )
2023-06-21 03:59:32 +00:00
self . assertEqual ( orig_count , Object . query ( ) . count ( ) )
2018-11-27 15:27:00 +00:00
2023-03-17 23:42:45 +00:00
mock_get . assert_has_calls ( ( self . req ( ' https://user.com/reply ' ) , ) )
2018-11-27 15:27:00 +00:00
2022-11-12 14:57:29 +00:00
def test_backlink_without_trailing_slash ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response (
2023-06-05 20:20:07 +00:00
REPLY_HTML . replace ( ' <a href= " http://localhost/ " ></a> ' ,
2023-06-20 18:22:54 +00:00
' <a href= " http://localhost " ></a> ' ) ,
2023-04-02 02:13:51 +00:00
content_type = CONTENT_TYPE_HTML , url = ' https://user.com/reply ' )
2022-11-12 14:57:29 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2022-11-12 14:57:29 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
2022-11-12 14:57:29 +00:00
2023-01-24 00:09:25 +00:00
def test_create_reply ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
mock_get . side_effect = ACTIVITYPUB_GETS
2023-06-21 03:59:32 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2017-10-10 01:12:17 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2017-10-10 01:12:17 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/reply ' ) ,
2022-11-24 16:20:04 +00:00
self . as2_req ( ' http://not/fediverse ' ) ,
2023-06-21 03:59:32 +00:00
self . req ( ' http://not/fediverse ' ) ,
2023-03-17 23:42:45 +00:00
self . as2_req ( ' https://mas.to/toot ' ) ,
self . as2_req ( ' https://mas.to/author ' ) ,
2017-10-20 14:49:25 +00:00
) )
2017-10-10 01:12:17 +00:00
2023-06-05 20:20:07 +00:00
self . assert_deliveries ( mock_post , [ ' https://mas.to/inbox ' ] , AS2_CREATE )
2017-08-23 15:14:51 +00:00
2023-03-17 23:42:45 +00:00
self . assert_object ( ' https://user.com/reply ' ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-07-10 18:37:40 +00:00
our_as1 = REPLY_AS1 ,
2023-01-29 22:13:58 +00:00
type = ' comment ' ,
2023-04-02 02:13:51 +00:00
)
2023-07-10 18:37:40 +00:00
author = ndb . Key ( ActivityPub , ' https://mas.to/author ' )
2023-04-02 02:13:51 +00:00
self . assert_object ( ' https://user.com/reply#bridgy-fed-create ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-17 15:31:28 +00:00
notify = [ author ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-04-02 02:13:51 +00:00
status = ' complete ' ,
2023-06-05 20:20:07 +00:00
our_as1 = CREATE_REPLY_AS1 ,
2023-04-02 02:13:51 +00:00
delivered = [ ' https://mas.to/inbox ' ] ,
type = ' post ' ,
2023-01-29 15:31:22 +00:00
)
2017-10-10 00:29:50 +00:00
2023-01-24 00:09:25 +00:00
def test_update_reply ( self , mock_get , mock_post ) :
2023-04-18 02:24:18 +00:00
self . make_followers ( )
2023-02-24 03:17:26 +00:00
mf2 = {
2023-03-22 19:27:40 +00:00
' properties ' : {
' content ' : [ ' other ' ] ,
} ,
2023-02-24 03:17:26 +00:00
}
2023-06-15 22:09:03 +00:00
Object ( id = ' https://user.com/reply ' , status = ' complete ' , mf2 = mf2 ) . put ( )
2018-12-11 15:45:14 +00:00
2023-06-05 20:20:07 +00:00
mock_get . side_effect = ACTIVITYPUB_GETS
2018-12-11 15:45:14 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2018-12-11 15:45:14 +00:00
2023-04-18 02:24:18 +00:00
self . assertEqual ( 1 , mock_post . call_count )
2018-12-11 15:45:14 +00:00
args , kwargs = mock_post . call_args
2023-03-17 23:42:45 +00:00
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
2023-06-05 20:20:07 +00:00
self . assert_equals ( AS2_UPDATE , json_loads ( kwargs [ ' data ' ] ) )
2018-12-11 15:45:14 +00:00
2023-01-24 00:09:25 +00:00
def test_redo_repost_isnt_update ( self , mock_get , mock_post ) :
2022-11-15 05:53:50 +00:00
""" Like and Announce shouldn ' t use Update, they should just resend as is. """
2023-06-15 22:09:03 +00:00
Object ( id = ' https://user.com/repost ' , mf2 = { } , status = ' complete ' ) . put ( )
2022-11-15 05:53:50 +00:00
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ REPOST , TOOT_AS2 , ACTOR ]
2022-11-15 05:53:50 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/repost ' ,
2022-11-15 05:53:50 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-04-19 21:37:42 +00:00
self . assert_deliveries ( mock_post , [ ' https://mas.to/inbox ' ] , REPOST_AS2 ,
ignore = [ ' cc ' ] )
2022-11-15 05:53:50 +00:00
2023-01-24 00:09:25 +00:00
def test_skip_update_if_content_unchanged ( self , mock_get , mock_post ) :
2021-10-10 20:47:25 +00:00
""" https://github.com/snarfed/bridgy-fed/issues/78 """
2023-07-24 21:07:44 +00:00
self . store_object ( id = ' https://user.com/reply ' , mf2 = REPLY_MF2 )
self . store_object ( id = ' https://user.com/reply#bridgy-fed-create ' ,
status = ' complete ' )
2023-04-02 02:13:51 +00:00
2023-06-05 20:20:07 +00:00
mock_get . side_effect = ACTIVITYPUB_GETS
2021-10-10 20:47:25 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2021-10-10 20:47:25 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-04-02 02:13:51 +00:00
self . assertEqual ( 204 , got . status_code )
2021-10-10 20:47:25 +00:00
mock_post . assert_not_called ( )
2023-06-06 02:03:20 +00:00
def test_force_with_content_unchanged_sends_create ( self , mock_get , mock_post ) :
2023-06-15 22:09:03 +00:00
Object ( id = ' https://user.com/reply ' , mf2 = REPLY_MF2 ) . put ( )
2023-06-06 02:03:20 +00:00
mock_get . side_effect = ACTIVITYPUB_GETS
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-06-06 02:03:20 +00:00
' source ' : ' https://user.com/reply ' ,
' target ' : ' https://fed.brid.gy/ ' ,
' force ' : ' ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-06-06 02:03:20 +00:00
args , kwargs = mock_post . call_args
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
self . assert_equals ( AS2_CREATE , json_loads ( kwargs [ ' data ' ] ) )
2023-01-24 00:09:25 +00:00
def test_create_reply_attributed_to_id_only ( self , mock_get , mock_post ) :
2018-12-11 15:45:14 +00:00
""" Based on PeerTube ' s AS2.
https : / / github . com / snarfed / bridgy - fed / issues / 40
"""
2023-06-21 03:59:32 +00:00
toot_as2_data = copy . deepcopy ( TOOT_AS2_DATA )
del toot_as2_data [ ' actor ' ]
toot_as2_data [ ' attributedTo ' ] = {
2018-12-11 15:45:14 +00:00
' type ' : ' Person ' ,
2023-03-17 23:42:45 +00:00
' id ' : ' https://mas.to/author ' ,
2023-03-19 16:32:12 +00:00
}
2018-12-11 15:45:14 +00:00
2023-06-21 03:59:32 +00:00
mock_get . side_effect = [
REPLY ,
NOT_FEDIVERSE , # AP
NOT_FEDIVERSE , # Web
self . as2_resp ( toot_as2_data ) , # AP
ACTOR ,
]
mock_post . return_value = requests_response ( ' abc xyz ' )
2018-12-11 15:45:14 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2018-12-11 15:45:14 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/reply ' ) ,
2022-11-24 16:20:04 +00:00
self . as2_req ( ' http://not/fediverse ' ) ,
2023-06-21 03:59:32 +00:00
self . req ( ' http://not/fediverse ' ) ,
2023-03-17 23:42:45 +00:00
self . as2_req ( ' https://mas.to/toot ' ) ,
self . as2_req ( ' https://mas.to/author ' ) ,
2018-12-11 15:45:14 +00:00
) )
args , kwargs = mock_post . call_args
2023-03-17 23:42:45 +00:00
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
2023-06-05 20:20:07 +00:00
self . assert_equals ( AS2_CREATE , json_loads ( kwargs [ ' data ' ] ) )
2018-12-11 15:45:14 +00:00
2023-07-09 15:28:47 +00:00
def test_repost ( self , mock_get , mock_post ) :
self . _test_repost ( REPOST_HTML , REPOST_AS2 , mock_get , mock_post )
2023-04-19 21:37:42 +00:00
2023-07-09 15:28:47 +00:00
def test_repost_composite_hcite ( self , mock_get , mock_post ) :
self . _test_repost ( REPOST_HCITE_HTML , REPOST_AS2 , mock_get , mock_post )
2023-04-19 21:37:42 +00:00
2023-07-09 15:28:47 +00:00
def _test_repost ( self , html , expected_as2 , mock_get , mock_post ) :
2023-04-18 02:20:22 +00:00
self . make_followers ( )
2023-04-17 23:56:44 +00:00
2023-12-05 20:39:40 +00:00
REPOSTED_ACTOR = self . as2_resp ( {
' type ' : ' Person ' ,
' name ' : ' Mas To Foo ' ,
' id ' : ' https://mas.to/@foo ' ,
' inbox ' : ' https://mas.to/inbox ' ,
} )
2023-04-19 21:37:42 +00:00
mock_get . side_effect = [
2023-07-25 04:28:37 +00:00
requests_response ( html , url = ' https://user.com/repost ' ) ,
2023-06-05 20:20:07 +00:00
TOOT_AS2 ,
ACTOR ,
2023-12-05 20:39:40 +00:00
REPOSTED_ACTOR ,
2023-04-19 21:37:42 +00:00
]
2017-10-17 04:48:41 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/repost ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2017-10-17 04:48:41 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/repost ' ) ,
2023-04-18 02:20:22 +00:00
self . as2_req ( ' https://mas.to/toot/id ' ) ,
2023-03-17 23:42:45 +00:00
self . as2_req ( ' https://mas.to/author ' ) ,
2017-10-20 14:49:25 +00:00
) )
2017-10-17 04:48:41 +00:00
2023-07-11 20:12:49 +00:00
inboxes = ( ' https://inbox/ ' , ' https://public/inbox ' ,
2023-04-18 02:20:22 +00:00
' https://shared/inbox ' , ' https://mas.to/inbox ' )
2023-04-19 21:37:42 +00:00
self . assert_deliveries ( mock_post , inboxes , expected_as2 , ignore = [ ' cc ' ] )
2022-11-24 16:20:04 +00:00
for args , kwargs in mock_get . call_args_list [ 1 : ] :
with self . subTest ( url = args [ 0 ] ) :
rsa_key = kwargs [ ' auth ' ] . header_signer . _rsa . _key
2023-11-20 05:21:27 +00:00
self . assertEqual ( self . user . private_pem ( ) , rsa_key . exportKey ( ) )
2023-04-17 23:56:44 +00:00
2023-04-19 21:37:42 +00:00
mf2 = util . parse_mf2 ( html ) [ ' items ' ] [ 0 ]
2023-07-09 15:28:47 +00:00
author_key = ndb . Key ( ' ActivityPub ' , ' https://mas.to/author ' )
2023-03-17 23:42:45 +00:00
self . assert_object ( ' https://user.com/repost ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-17 15:31:28 +00:00
notify = [ author_key ] ,
feed = self . followers ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-01-29 15:39:15 +00:00
status = ' complete ' ,
2023-04-19 21:37:42 +00:00
mf2 = mf2 ,
2023-04-18 02:20:22 +00:00
delivered = inboxes ,
2023-01-29 22:13:58 +00:00
type = ' share ' ,
2023-04-18 02:20:22 +00:00
object_ids = [ ' https://mas.to/toot/id ' ] ,
2023-07-09 15:28:47 +00:00
labels = [ ' user ' , ' activity ' , ' notification ' , ' feed ' ] ,
2023-01-29 15:39:15 +00:00
)
2017-10-17 04:48:41 +00:00
2023-01-24 00:09:25 +00:00
def test_link_rel_alternate_as2 ( self , mock_get , mock_post ) :
2023-03-08 21:10:41 +00:00
mock_get . side_effect = [
2023-06-05 20:20:07 +00:00
REPLY ,
2023-06-21 03:59:32 +00:00
NOT_FEDIVERSE , # AP
NOT_FEDIVERSE , # Web
TOOT_HTML , # AP
TOOT_AS2 , # AP via rel-alternate
2023-06-05 20:20:07 +00:00
ACTOR ,
2023-03-08 21:10:41 +00:00
]
2017-10-20 14:49:25 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/reply ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2017-10-20 14:49:25 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/reply ' ) ,
2022-11-24 16:20:04 +00:00
self . as2_req ( ' http://not/fediverse ' ) ,
2023-06-21 03:59:32 +00:00
self . req ( ' http://not/fediverse ' ) ,
2023-03-17 23:42:45 +00:00
self . as2_req ( ' https://mas.to/toot ' ) ,
self . as2_req ( ' https://mas.to/toot/id ' , headers = as2 . CONNEG_HEADERS ) ,
self . as2_req ( ' https://mas.to/author ' ) ,
2017-10-20 14:49:25 +00:00
) )
args , kwargs = mock_post . call_args
2023-03-17 23:42:45 +00:00
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
2023-06-05 20:20:07 +00:00
self . assert_equals ( AS2_CREATE , json_loads ( kwargs [ ' data ' ] ) )
2017-10-20 14:49:25 +00:00
2023-10-25 20:23:11 +00:00
def test_like_stored_object ( self , mock_get , mock_post ) :
Object ( id = ' https://mas.to/toot ' , source_protocol = ' ap ' ) . put ( )
2023-03-22 19:27:40 +00:00
Object ( id = ' https://user.com/ ' , mf2 = ACTOR_MF2 ) . put ( )
mock_get . side_effect = [
2023-06-05 20:20:07 +00:00
LIKE ,
2023-11-26 04:07:14 +00:00
ACTOR_HTML_RESP ,
2023-03-22 19:27:40 +00:00
]
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-22 19:27:40 +00:00
' source ' : ' https://user.com/like ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
2023-03-22 19:27:40 +00:00
mock_get . assert_has_calls ( (
self . req ( ' https://user.com/like ' ) ,
) )
mock_post . assert_not_called ( )
2023-04-02 02:13:51 +00:00
self . assert_object ( ' https://user.com/like ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-06-05 20:20:07 +00:00
mf2 = LIKE_MF2 ,
2023-04-02 02:13:51 +00:00
type = ' like ' ,
2023-07-10 18:26:09 +00:00
labels = [ ' activity ' , ' user ' ] ,
status = ' ignored ' ,
2023-04-02 02:13:51 +00:00
)
2023-03-22 19:27:40 +00:00
2023-07-06 14:38:15 +00:00
def test_post_type_discovery_multiple_types ( self , mock_get , mock_post ) :
self . make_followers ( )
mock_get . return_value = requests_response (
NOTE_HTML . replace ( ' <a href= " http://localhost/ " ></a> ' , """
< a class = " u-like-of " href = " https://alice.com/post " > < / a >
< a class = " u-bookmark-of " href = " http://bob.com/post " > < / a >
< a href = " http://localhost/ " > < / a >
2023-07-25 04:28:37 +00:00
""" ), url= ' https://user.com/post ' )
2023-07-06 14:38:15 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-07-06 14:38:15 +00:00
' source ' : ' https://user.com/multiple ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-07-06 14:38:15 +00:00
2023-07-11 20:12:49 +00:00
inboxes = [ ' https://inbox/ ' , ' https://public/inbox ' , ' https://shared/inbox ' ]
2023-10-20 20:37:38 +00:00
expected = {
2023-07-06 14:38:15 +00:00
* * NOTE_AS2 ,
' attributedTo ' : None ,
' type ' : ' Create ' ,
' actor ' : ' http://localhost/user.com ' ,
# TODO: this is an awkward wart left over from the multi-type mf2.
# remove it eventually.
' object ' : {
' targetUrl ' : ' http://bob.com/post ' ,
' to ' : [ ' https://www.w3.org/ns/activitystreams#Public ' ] ,
} ,
2023-10-20 20:37:38 +00:00
}
del expected [ ' contentMap ' ]
self . assert_deliveries ( mock_post , inboxes , expected )
2023-07-06 14:38:15 +00:00
2023-01-24 00:09:25 +00:00
def test_create_default_url_to_wm_source ( self , mock_get , mock_post ) :
2017-12-13 02:07:12 +00:00
""" Source post has no u-url. AS2 id should default to webmention source. """
missing_url = requests_response ( """ \
< html >
< body class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-repost-of p-name " href = " https://mas.to/toot " > reposted ! < / a >
< a class = " p-author h-card " href = " https://user.com/ " > Ms . ☕ Baz < / a >
2018-11-27 15:27:00 +00:00
< a href = " http://localhost/ " > < / a >
2017-12-13 02:07:12 +00:00
< / body >
< / html >
2023-07-25 04:28:37 +00:00
""" , url= ' https://user.com/repost ' )
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ missing_url , TOOT_AS2 , ACTOR ]
2023-06-21 03:59:32 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2017-12-13 02:07:12 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/repost ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2017-12-13 02:07:12 +00:00
args , kwargs = mock_post . call_args
2023-03-17 23:42:45 +00:00
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
self . assert_equals ( REPOST_AS2 , json_loads ( kwargs [ ' data ' ] ) )
2017-12-13 02:07:12 +00:00
2023-01-24 00:09:25 +00:00
def test_create_author_only_url ( self , mock_get , mock_post ) :
2018-01-12 15:30:24 +00:00
""" Mf2 author property is just a URL. We should run full authorship.
https : / / indieweb . org / authorship
"""
repost = requests_response ( """ \
2018-01-11 15:32:13 +00:00
< html >
< body class = " h-entry " >
2023-03-17 23:42:45 +00:00
< a class = " u-repost-of p-name " href = " https://mas.to/toot " > reposted ! < / a >
< a class = " u-author " href = " https://user.com/ " > < / a >
2018-11-27 15:27:00 +00:00
< a href = " http://localhost/ " > < / a >
2018-01-11 15:32:13 +00:00
< / body >
< / html >
2023-07-25 04:28:37 +00:00
""" , url= ' https://user.com/repost ' )
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ repost , ACTOR , TOOT_AS2 , ACTOR ]
2023-06-21 03:59:32 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2018-01-11 15:32:13 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/repost ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2018-01-11 15:32:13 +00:00
args , kwargs = mock_post . call_args
2023-03-17 23:42:45 +00:00
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
self . assert_equals ( REPOST_AS2 , json_loads ( kwargs [ ' data ' ] ) )
2018-01-11 15:32:13 +00:00
2023-07-24 18:32:07 +00:00
def test_create_no_author ( self , mock_get , mock_post ) :
""" No mf2 author. We should default to the user ' s homepage. """
mock_get . side_effect = [
requests_response ( """ \
< html >
< body class = " h-entry " >
< a class = " u-repost-of p-name " href = " https://mas.to/toot/id " > reposted ! < / a >
< a href = " http://localhost/ " > < / a >
< / body >
< / html >
2023-07-25 04:28:37 +00:00
""" , url= ' https://user.com/repost ' ),
2023-07-24 18:32:07 +00:00
NOT_FEDIVERSE ,
TOOT_AS2 ,
ACTOR ,
]
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-07-24 18:32:07 +00:00
' source ' : ' https://user.com/repost ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-07-24 18:32:07 +00:00
repost_mf2 = copy . deepcopy ( REPOST_MF2 )
repost_mf2 [ ' properties ' ] [ ' author ' ] = [ ' https://user.com/ ' ]
self . assert_object ( ' https://user.com/repost ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-24 18:32:07 +00:00
source_protocol = ' web ' ,
mf2 = repost_mf2 , # includes author https://user.com/
type = ' share ' ,
labels = [ ' activity ' , ' user ' ] ,
notify = [ ndb . Key ( ' ActivityPub ' , ' https://mas.to/author ' ) ] ,
delivered = [ ' https://mas.to/inbox ' ] ,
status = ' complete ' ,
)
2023-07-25 04:28:37 +00:00
def test_create_non_domain_author ( self , mock_get , mock_post ) :
""" Author is a page on the user ' s domain. """
self . make_followers ( )
mock_get . side_effect = [
requests_response ( NOTE_HTML . replace (
' <a class= " p-author h-card " href= " https://user.com/ " > ' ,
' <a class= " p-author h-card " href= " https://user.com/hcard " > '
) , url = ' https://user.com/post ' ) ,
ACTOR_HTML_RESP ,
TOOT_AS2 ,
ACTOR ,
]
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-07-25 04:28:37 +00:00
' source ' : ' https://user.com/post ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-07-25 04:28:37 +00:00
inboxes = [ ' https://inbox/ ' , ' https://public/inbox ' , ' https://shared/inbox ' ]
self . assert_object ( ' https://user.com/post#bridgy-fed-create ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-25 04:28:37 +00:00
source_protocol = ' web ' ,
our_as1 = CREATE_AS1 ,
type = ' post ' ,
labels = [ ' activity ' , ' user ' ] ,
delivered = inboxes ,
status = ' complete ' ,
)
2023-10-10 16:57:10 +00:00
def test_create_post_use_instead_strip_www ( self , mock_get , mock_post ) :
2023-11-20 05:21:27 +00:00
self . user . obj . mf2 = {
2023-10-10 16:57:10 +00:00
' type ' : [ ' h-card ' ] ,
' properties ' : {
# this is the key part to test; Object.as1 uses this as id
' url ' : [ ' https://www.user.com/ ' ] ,
' name ' : [ ' Ms. ☕ Baz ' ] ,
} ,
}
2023-11-20 05:21:27 +00:00
self . user . obj . put ( )
self . make_user ( ' www.user.com ' , cls = Web , use_instead = self . user . key )
2023-10-10 16:57:10 +00:00
self . make_followers ( )
note_html = NOTE_HTML . replace ( ' https://user.com/ ' , ' https://www.user.com/ ' )
mock_get . side_effect = [
requests_response ( note_html , url = ' https://www.user.com/post ' ) ,
ACTOR ,
]
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-10-10 16:57:10 +00:00
' source ' : ' https://www.user.com/post ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-10-10 16:57:10 +00:00
mock_get . assert_has_calls ( (
self . req ( ' https://www.user.com/post ' ) ,
) )
inboxes = ( ' https://inbox/ ' , ' https://public/inbox ' , ' https://shared/inbox ' )
create_as2 = copy . deepcopy ( CREATE_AS2 )
create_as2 [ ' id ' ] = ' http://localhost/r/https://www.user.com/post#bridgy-fed-create '
create_as2 [ ' object ' ] . update ( {
' id ' : ' http://localhost/r/https://www.user.com/post ' ,
' url ' : ' http://localhost/r/https://www.user.com/post ' ,
} )
self . assert_deliveries ( mock_post , inboxes , create_as2 )
2023-04-06 16:16:25 +00:00
def test_create_post ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ NOTE , ACTOR ]
2023-01-31 06:02:23 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
self . make_followers ( )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/post ' ,
2023-01-31 06:02:23 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-01-31 06:02:23 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/post ' ) ,
2023-01-31 06:02:23 +00:00
) )
2023-07-11 20:12:49 +00:00
inboxes = ( ' https://inbox/ ' , ' https://public/inbox ' , ' https://shared/inbox ' )
2023-06-05 20:20:07 +00:00
self . assert_deliveries ( mock_post , inboxes , CREATE_AS2 )
2023-01-31 06:02:23 +00:00
2023-04-02 02:13:51 +00:00
self . assert_object ( ' https://user.com/post ' ,
2023-07-10 17:14:12 +00:00
our_as1 = NOTE_AS1 ,
2023-07-17 15:31:28 +00:00
feed = self . followers ,
2023-01-31 06:02:23 +00:00
type = ' note ' ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-01-31 06:02:23 +00:00
)
2023-04-02 02:13:51 +00:00
self . assert_object ( ' https://user.com/post#bridgy-fed-create ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-01-29 15:39:15 +00:00
status = ' complete ' ,
2023-06-05 20:20:07 +00:00
our_as1 = CREATE_AS1 ,
2023-04-02 02:13:51 +00:00
delivered = inboxes ,
type = ' post ' ,
2023-01-31 06:02:23 +00:00
)
2023-04-06 16:16:25 +00:00
def test_update_post ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ NOTE , ACTOR ]
2023-01-31 06:02:23 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-06-15 22:09:03 +00:00
mf2 = copy . deepcopy ( NOTE_MF2 )
mf2 [ ' properties ' ] [ ' content ' ] = ' different '
2023-11-20 05:21:27 +00:00
Object ( id = ' https://user.com/post ' , users = [ self . user . key ] , mf2 = mf2 ) . put ( )
2023-01-31 06:02:23 +00:00
self . make_followers ( )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/post ' ,
2023-01-31 06:02:23 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-01-31 06:02:23 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/post ' ) ,
2023-01-31 06:02:23 +00:00
) )
2023-07-11 20:12:49 +00:00
inboxes = ( ' https://inbox/ ' , ' https://public/inbox ' , ' https://shared/inbox ' )
2023-06-05 20:20:07 +00:00
self . assert_deliveries ( mock_post , inboxes , UPDATE_AS2 )
2023-01-31 06:02:23 +00:00
2023-04-02 02:13:51 +00:00
update_as1 = {
' objectType ' : ' activity ' ,
' verb ' : ' update ' ,
' id ' : ' https://user.com/post#bridgy-fed-update-2022-01-02T03:04:05+00:00 ' ,
2023-07-09 14:53:33 +00:00
' actor ' : ACTOR_AS1_UNWRAPPED ,
2023-04-02 02:13:51 +00:00
' object ' : {
2023-06-05 20:20:07 +00:00
* * NOTE_AS1 ,
2023-04-02 02:13:51 +00:00
' updated ' : ' 2022-01-02T03:04:05+00:00 ' ,
} ,
}
self . assert_object (
f ' https://user.com/post#bridgy-fed-update-2022-01-02T03:04:05+00:00 ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-04-02 02:13:51 +00:00
status = ' complete ' ,
our_as1 = update_as1 ,
delivered = inboxes ,
type = ' update ' ,
labels = [ ' user ' , ' activity ' ] ,
)
2023-01-29 22:13:58 +00:00
2023-01-24 00:09:25 +00:00
def test_create_with_image ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
create_html = NOTE_HTML . replace (
2018-11-24 06:17:37 +00:00
' </body> ' , ' <img class= " u-photo " src= " http://im/age " /> \n </body> ' )
mock_get . side_effect = [
2023-04-02 02:13:51 +00:00
requests_response ( create_html , url = ' https://user.com/post ' ,
content_type = CONTENT_TYPE_HTML ) ,
2023-06-05 20:20:07 +00:00
ACTOR ,
2018-11-24 06:17:37 +00:00
]
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-06-16 04:22:20 +00:00
Follower . get_or_create (
2023-11-20 05:21:27 +00:00
to = self . user ,
2023-06-23 19:22:37 +00:00
from_ = self . make_user ( ' http://a ' , cls = ActivityPub ,
obj_as2 = { ' inbox ' : ' https://inbox ' } ) )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/post ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2018-11-24 06:17:37 +00:00
2023-07-11 20:12:49 +00:00
self . assertEqual ( ( ' https://inbox/ ' , ) , mock_post . call_args [ 0 ] )
2023-06-05 20:20:07 +00:00
create = copy . deepcopy ( CREATE_AS2 )
2018-11-24 06:17:37 +00:00
create [ ' object ' ] . update ( {
2022-11-05 21:57:50 +00:00
' image ' : { ' url ' : ' http://im/age ' , ' type ' : ' Image ' } ,
2018-11-24 06:17:37 +00:00
' attachment ' : [ { ' url ' : ' http://im/age ' , ' type ' : ' Image ' } ] ,
} )
2023-03-19 04:45:27 +00:00
self . assert_equals ( create , json_loads ( mock_post . call_args [ 1 ] [ ' data ' ] ) )
2018-11-24 06:17:37 +00:00
2023-01-24 00:09:25 +00:00
def test_follow ( self , mock_get , mock_post ) :
2023-07-10 17:14:12 +00:00
mock_get . side_effect = [ FOLLOW , ACTOR , WEBMENTION_REL_LINK ]
2018-10-25 04:28:52 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/follow ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2018-10-25 04:28:52 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/follow ' ) ,
self . as2_req ( ' https://mas.to/mrs-foo ' ) ,
2018-10-25 04:28:52 +00:00
) )
2023-06-05 20:20:07 +00:00
self . assert_deliveries ( mock_post , [ ' https://mas.to/inbox ' ] , FOLLOW_AS2 )
2018-10-25 04:28:52 +00:00
2023-06-06 21:50:20 +00:00
obj = self . assert_object ( ' https://user.com/follow ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-17 15:31:28 +00:00
notify = [ self . mrs_foo ] ,
2023-06-06 21:50:20 +00:00
source_protocol = ' web ' ,
status = ' complete ' ,
mf2 = FOLLOW_MF2 ,
delivered = [ ' https://mas.to/inbox ' ] ,
type = ' follow ' ,
object_ids = [ ' https://mas.to/mrs-foo ' ] ,
2023-07-10 17:14:12 +00:00
labels = [ ' user ' , ' activity ' , ' notification ' ] ,
2023-06-06 21:50:20 +00:00
)
2023-06-16 04:22:20 +00:00
to = self . assert_user ( ActivityPub , ' https://mas.to/mrs-foo ' , obj_as2 = {
' name ' : ' Mrs. ☕ Foo ' ,
2023-06-07 21:24:00 +00:00
' id ' : ' https://mas.to/mrs-foo ' ,
' inbox ' : ' https://mas.to/inbox ' ,
2023-06-16 04:22:20 +00:00
' type ' : ' Person ' ,
2023-06-07 21:24:00 +00:00
} )
2018-10-25 04:28:52 +00:00
2022-11-12 06:43:33 +00:00
followers = Follower . query ( ) . fetch ( )
self . assertEqual ( 1 , len ( followers ) )
2023-11-20 05:21:27 +00:00
self . assertEqual ( self . user . key , followers [ 0 ] . from_ )
2023-06-06 21:50:20 +00:00
self . assertEqual ( to . key , followers [ 0 ] . to )
self . assert_equals ( obj . key , followers [ 0 ] . follow )
2022-11-12 06:43:33 +00:00
2023-01-24 00:09:25 +00:00
def test_follow_no_actor ( self , mock_get , mock_post ) :
2023-11-20 05:21:27 +00:00
self . user . obj_key = Object ( id = ' a ' , as2 = ACTOR_AS2 ) . put ( )
self . user . put ( )
2022-11-27 04:20:08 +00:00
2023-06-05 20:20:07 +00:00
html = FOLLOW_HTML . replace (
2023-03-17 23:42:45 +00:00
' <a class= " p-author h-card " href= " https://user.com/ " >Ms. ☕ Baz</a> ' , ' ' )
2023-04-02 02:13:51 +00:00
follow = requests_response ( html , url = ' https://user.com/follow ' ,
content_type = CONTENT_TYPE_HTML )
2022-11-27 04:20:08 +00:00
2023-07-10 18:26:09 +00:00
mock_get . side_effect = [
follow ,
ACTOR_HTML_RESP , # authorship on follower
ACTOR , # followee AS2
]
2022-11-27 04:20:08 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/follow ' ,
2022-11-27 04:20:08 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2022-11-27 04:20:08 +00:00
args , kwargs = mock_post . call_args
2023-03-17 23:42:45 +00:00
self . assertEqual ( ( ' https://mas.to/inbox ' , ) , args )
2023-06-05 20:20:07 +00:00
self . assert_equals ( FOLLOW_AS2 , json_loads ( kwargs [ ' data ' ] ) )
2022-11-27 04:20:08 +00:00
2023-05-17 21:52:39 +00:00
def test_follow_no_target ( self , mock_get , mock_post ) :
self . make_followers ( )
2023-06-05 20:20:07 +00:00
html = FOLLOW_HTML . replace (
2023-05-17 21:52:39 +00:00
' <a class= " u-follow-of " href= " https://mas.to/mrs-foo " ></a> ' ,
' <a class= " u-follow-of " ></a> ' )
follow = requests_response ( html , url = ' https://user.com/follow ' ,
content_type = CONTENT_TYPE_HTML )
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ follow , ACTOR ]
2023-05-17 21:52:39 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-05-17 21:52:39 +00:00
' source ' : ' https://user.com/follow ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-07-13 21:19:01 +00:00
self . assertEqual ( 204 , got . status_code )
2023-05-17 21:52:39 +00:00
mock_post . assert_not_called ( )
2023-01-24 00:09:25 +00:00
def test_follow_fragment ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ FOLLOW_FRAGMENT , ACTOR ]
2022-11-18 10:18:35 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/follow#2 ' ,
2022-11-18 10:18:35 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assert_equals ( 202 , got . status_code )
2022-11-18 10:18:35 +00:00
mock_get . assert_has_calls ( (
2023-04-03 14:53:15 +00:00
self . req ( ' https://user.com/follow ' ) ,
2023-03-17 23:42:45 +00:00
self . as2_req ( ' https://mas.to/mrs-foo ' ) ,
2022-11-18 10:18:35 +00:00
) )
2023-04-17 23:56:44 +00:00
self . assert_deliveries ( mock_post , [ ' https://mas.to/inbox ' ] ,
2023-06-05 20:20:07 +00:00
FOLLOW_FRAGMENT_AS2 )
2022-11-18 10:18:35 +00:00
2023-06-20 18:22:54 +00:00
self . assert_object ( ' https://user.com/follow#2 ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-17 15:31:28 +00:00
notify = [ self . mrs_foo ] ,
2023-06-20 18:22:54 +00:00
source_protocol = ' web ' ,
status = ' complete ' ,
mf2 = FOLLOW_FRAGMENT_MF2 ,
delivered = [ ' https://mas.to/inbox ' ] ,
type = ' follow ' ,
object_ids = [ ' https://mas.to/mrs-foo ' ] ,
2023-07-10 18:37:40 +00:00
labels = [ ' user ' , ' activity ' , ' notification ' , ] ,
2023-06-20 18:22:54 +00:00
)
2022-11-18 10:18:35 +00:00
followers = Follower . query ( ) . fetch ( )
2022-11-18 23:28:34 +00:00
self . assert_equals ( 1 , len ( followers ) )
2023-11-20 05:21:27 +00:00
self . assert_equals ( self . user . key , followers [ 0 ] . from_ )
2023-06-06 21:50:20 +00:00
self . assert_equals ( ActivityPub ( id = ' https://mas.to/mrs-foo ' ) . key ,
followers [ 0 ] . to )
2022-11-18 23:28:34 +00:00
2023-04-24 04:13:11 +00:00
def test_follow_multiple ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
html = FOLLOW_HTML . replace (
2023-04-24 04:13:11 +00:00
' <a class= " u-follow-of " href= " https://mas.to/mrs-foo " ></a> ' ,
' <a class= " u-follow-of " href= " https://mas.to/mrs-foo " ></a> '
' <a class= " u-follow-of " href= " https://mas.to/mr-biff " ></a> ' )
mock_get . side_effect = [
requests_response (
html , url = ' https://user.com/follow ' ,
content_type = CONTENT_TYPE_HTML ) ,
2023-07-10 19:00:42 +00:00
ACTOR ,
2023-04-24 04:13:11 +00:00
self . as2_resp ( {
2023-06-20 18:22:54 +00:00
' objectType ' : ' Person ' ,
2023-04-24 04:13:11 +00:00
' displayName ' : ' Mr. ☕ Biff ' ,
' id ' : ' https://mas.to/mr-biff ' ,
' inbox ' : ' https://mas.to/inbox/biff ' ,
} ) ,
]
mock_post . return_value = requests_response ( ' unused ' )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-04-24 04:13:11 +00:00
' source ' : ' https://user.com/follow ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-04-24 04:13:11 +00:00
mock_get . assert_has_calls ( (
self . req ( ' https://user.com/follow ' ) ,
2023-06-21 03:59:32 +00:00
self . as2_req ( ' https://mas.to/mrs-foo ' ) ,
2023-07-10 19:00:42 +00:00
self . as2_req ( ' https://mas.to/mr-biff ' ) ,
2023-04-24 04:13:11 +00:00
) )
2023-10-07 22:08:02 +00:00
self . assertCountEqual ( [
( ( ' https://mas.to/inbox ' , ) , FOLLOW_AS2 ) ,
( ( ' https://mas.to/inbox/biff ' , ) , {
* * FOLLOW_AS2 ,
' object ' : ' https://mas.to/mr-biff ' ,
} ) ,
] , [ ( args , json_loads ( kwargs [ ' data ' ] ) )
for args , kwargs in mock_post . call_args_list ] )
2023-04-24 04:13:11 +00:00
mf2 = util . parse_mf2 ( html ) [ ' items ' ] [ 0 ]
2023-07-10 19:00:42 +00:00
mr_biff = ndb . Key ( ActivityPub , ' https://mas.to/mr-biff ' )
2023-06-06 21:50:20 +00:00
obj = self . assert_object ( ' https://user.com/follow ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-17 15:31:28 +00:00
notify = [ self . mrs_foo , mr_biff ] ,
2023-06-06 21:50:20 +00:00
source_protocol = ' web ' ,
status = ' complete ' ,
mf2 = mf2 ,
delivered = [ ' https://mas.to/inbox ' ,
' https://mas.to/inbox/biff ' ] ,
type = ' follow ' ,
object_ids = [ ' https://mas.to/mrs-foo ' ,
' https://mas.to/mr-biff ' ] ,
2023-07-10 19:00:42 +00:00
labels = [ ' user ' , ' activity ' , ' notification ' , ] ,
2023-06-06 21:50:20 +00:00
)
2023-04-24 04:13:11 +00:00
followers = Follower . query ( ) . fetch ( )
self . assertEqual ( 2 , len ( followers ) )
2023-11-20 05:21:27 +00:00
self . assertEqual ( self . user . key , followers [ 0 ] . from_ )
2023-06-06 21:50:20 +00:00
self . assertEqual ( ActivityPub ( id = ' https://mas.to/mr-biff ' ) . key ,
followers [ 0 ] . to )
self . assert_equals ( obj . key , followers [ 0 ] . follow )
2023-04-24 04:13:11 +00:00
2023-11-20 05:21:27 +00:00
self . assertEqual ( self . user . key , followers [ 1 ] . from_ )
2023-06-06 21:50:20 +00:00
self . assertEqual ( ActivityPub ( id = ' https://mas.to/mrs-foo ' ) . key ,
followers [ 1 ] . to )
self . assert_equals ( obj . key , followers [ 1 ] . follow )
2023-04-24 04:13:11 +00:00
2023-01-24 00:09:25 +00:00
def test_error_fragment_missing ( self , mock_get , mock_post ) :
2023-04-02 02:13:51 +00:00
mock_get . return_value = requests_response (
2023-06-05 20:20:07 +00:00
FOLLOW_FRAGMENT_HTML , url = ' https://user.com/follow ' ,
2023-04-02 02:13:51 +00:00
content_type = CONTENT_TYPE_HTML )
2022-11-18 23:28:34 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/follow#3 ' ,
2022-11-18 23:28:34 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-04-06 16:16:25 +00:00
self . assert_equals ( 304 , got . status_code )
2023-04-03 14:53:15 +00:00
mock_get . assert_has_calls ( (
self . req ( ' https://user.com/follow ' ) ,
) )
2022-11-18 10:18:35 +00:00
2023-04-17 22:36:29 +00:00
def test_delete ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( ' " unused " ' , status = 410 ,
url = ' http://final/delete ' )
mock_post . return_value = requests_response ( ' unused ' , status = 200 )
Object ( id = ' https://user.com/post#bridgy-fed-create ' ,
2023-06-05 20:20:07 +00:00
mf2 = NOTE_MF2 , status = ' complete ' ) . put ( )
2023-04-17 22:36:29 +00:00
self . make_followers ( )
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-04-17 22:36:29 +00:00
' source ' : ' https://user.com/post ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code , got . text )
2023-04-17 22:36:29 +00:00
2023-07-11 20:12:49 +00:00
inboxes = ( ' https://inbox/ ' , ' https://public/inbox ' , ' https://shared/inbox ' )
2023-04-17 22:36:29 +00:00
self . assert_deliveries ( mock_post , inboxes , DELETE_AS2 )
self . assert_object ( ' https://user.com/post#bridgy-fed-delete ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-04-17 22:36:29 +00:00
status = ' complete ' ,
2023-07-03 16:05:18 +00:00
our_as1 = {
* * DELETE_AS1 ,
2023-11-29 20:00:48 +00:00
' actor ' : ACTOR_AS1_UNWRAPPED ,
2023-07-03 16:05:18 +00:00
} ,
2023-04-17 22:36:29 +00:00
delivered = inboxes ,
type = ' delete ' ,
object_ids = [ ' https://user.com/post ' ] ,
labels = [ ' user ' , ' activity ' ] ,
2023-06-20 18:22:54 +00:00
)
2023-04-17 22:36:29 +00:00
def test_delete_no_object ( self , mock_get , mock_post ) :
mock_get . side_effect = [
requests_response ( ' " unused " ' , status = 410 , url = ' http://final/delete ' ) ,
]
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-04-17 22:36:29 +00:00
' source ' : ' https://user.com/post ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
self . assertEqual ( 304 , got . status_code , got . text )
mock_post . assert_not_called ( )
def test_delete_incomplete_response ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( ' " unused " ' , status = 410 ,
url = ' http://final/delete ' )
2023-06-15 22:09:03 +00:00
Object ( id = ' https://user.com/post#bridgy-fed-create ' ,
mf2 = NOTE_MF2 , status = ' in progress ' )
2023-04-17 22:36:29 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-04-17 22:36:29 +00:00
' source ' : ' https://user.com/post ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
self . assertEqual ( 304 , got . status_code , got . text )
mock_post . assert_not_called ( )
2023-01-24 00:09:25 +00:00
def test_error ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ FOLLOW , ACTOR ]
2020-11-13 17:50:14 +00:00
mock_post . return_value = requests_response (
2023-03-17 23:42:45 +00:00
' abc xyz ' , status = 405 , url = ' https://mas.to/inbox ' )
2020-11-13 17:50:14 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/follow ' ,
2021-07-11 15:48:28 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2020-11-13 17:50:14 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/follow ' ) ,
self . as2_req ( ' https://mas.to/mrs-foo ' ) ,
2020-11-13 17:50:14 +00:00
) )
2023-06-05 20:20:07 +00:00
self . assert_deliveries ( mock_post , [ ' https://mas.to/inbox ' ] , FOLLOW_AS2 )
2020-11-13 17:50:14 +00:00
2023-03-17 23:42:45 +00:00
self . assert_object ( ' https://user.com/follow ' ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-07-17 15:31:28 +00:00
notify = [ self . mrs_foo ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-01-29 15:39:15 +00:00
status = ' failed ' ,
2023-06-05 20:20:07 +00:00
mf2 = FOLLOW_MF2 ,
2023-03-17 23:42:45 +00:00
failed = [ ' https://mas.to/inbox ' ] ,
2023-01-29 22:13:58 +00:00
type = ' follow ' ,
2023-03-17 23:42:45 +00:00
object_ids = [ ' https://mas.to/mrs-foo ' ] ,
2023-07-10 18:37:40 +00:00
labels = [ ' user ' , ' activity ' , ' notification ' , ] ,
2023-06-20 18:22:54 +00:00
)
2020-11-13 17:50:14 +00:00
2023-06-23 18:05:12 +00:00
def test_repost_twitter_blocklisted ( self , * mocks ) :
self . _test_repost_blocklisted_error ( ' https://twitter.com/foo ' , * mocks )
def test_repost_bridgy_fed_blocklisted ( self , * mocks ) :
self . _test_repost_blocklisted_error ( ' https://fed.brid.gy/foo ' , * mocks )
def _test_repost_blocklisted_error ( self , orig_url , mock_get , mock_post ) :
2022-11-14 15:07:33 +00:00
""" Reposts of non-fediverse (ie blocklisted) sites aren ' t yet supported. """
2023-06-23 18:05:12 +00:00
repost_html = REPOST_HTML . replace ( ' https://mas.to/toot ' , orig_url )
2023-07-25 04:28:37 +00:00
repost_resp = requests_response ( repost_html , url = ' https://user.com/repost ' )
2022-11-14 15:07:33 +00:00
mock_get . side_effect = [ repost_resp ]
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/repost ' ,
2022-11-14 15:07:33 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-06-21 03:59:32 +00:00
self . assertEqual ( 204 , got . status_code )
2023-04-19 23:21:21 +00:00
mock_post . assert_not_called ( )
2023-01-24 00:09:25 +00:00
2023-04-06 16:16:25 +00:00
def test_update_profile ( self , mock_get , mock_post ) :
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ ACTOR_HTML_RESP ]
2023-01-24 00:09:25 +00:00
mock_post . return_value = requests_response ( ' abc xyz ' )
2023-11-20 05:21:27 +00:00
Follower . get_or_create ( to = self . user , from_ = self . make_user (
2023-06-23 19:22:37 +00:00
' http://ccc ' , cls = ActivityPub , obj_as2 = {
2023-06-06 21:50:20 +00:00
' endpoints ' : {
' sharedInbox ' : ' https://shared/inbox ' ,
} ,
} ) )
2023-11-20 05:21:27 +00:00
Follower . get_or_create ( to = self . user , from_ = self . make_user (
2023-06-23 19:22:37 +00:00
' http://ddd ' , cls = ActivityPub , obj_as2 = { ' inbox ' : ' https://inbox ' } ) )
2023-01-24 00:09:25 +00:00
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-03-17 23:42:45 +00:00
' source ' : ' https://user.com/ ' ,
2023-01-24 00:09:25 +00:00
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-01-24 00:09:25 +00:00
mock_get . assert_has_calls ( (
2023-03-17 23:42:45 +00:00
self . req ( ' https://user.com/ ' ) ,
2023-01-24 00:09:25 +00:00
) )
2023-03-17 23:42:45 +00:00
id = ' https://user.com/#update-2022-01-02T03:04:05+00:00 '
2023-02-07 20:43:36 +00:00
wrapped_id = f ' http://localhost/r/ { id } '
2023-04-02 02:13:51 +00:00
expected_as2 = {
2023-01-24 00:09:25 +00:00
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Update ' ,
2023-02-07 20:43:36 +00:00
' id ' : wrapped_id ,
2023-03-17 23:42:45 +00:00
' actor ' : ' http://localhost/user.com ' ,
2023-01-24 04:53:34 +00:00
' object ' : {
2023-04-02 02:13:51 +00:00
* * ACTOR_AS2 ,
' attachment ' : ACTOR_AS2_FULL [ ' attachment ' ] ,
2023-06-05 20:20:07 +00:00
' updated ' : NOW . isoformat ( ) ,
2023-11-24 17:20:52 +00:00
' to ' : [ ' https://www.w3.org/ns/activitystreams#Public ' ] ,
2023-01-24 04:53:34 +00:00
} ,
2023-01-24 00:09:25 +00:00
' to ' : [ ' https://www.w3.org/ns/activitystreams#Public ' ] ,
2023-04-02 02:13:51 +00:00
}
2023-07-11 20:12:49 +00:00
self . assert_deliveries ( mock_post , ( ' https://shared/inbox ' , ' https://inbox/ ' ) ,
2023-04-02 02:13:51 +00:00
expected_as2 )
2023-01-24 04:53:34 +00:00
2023-06-10 14:16:54 +00:00
# updated Web user
2023-10-25 20:23:11 +00:00
expected_actor_as2 = {
' type ' : ' Person ' ,
' id ' : ' https://user.com/ ' ,
' url ' : ' https://user.com/ ' ,
' name ' : ' Ms. ☕ Baz ' ,
' attachment ' : [ {
' name ' : ' Ms. ☕ Baz ' ,
' type ' : ' PropertyValue ' ,
' value ' : ' <a rel= " me " href= " https://user.com " ><span class= " invisible " >https://</span>user.com</a> ' ,
} ] ,
' updated ' : ' 2022-01-02T03:04:05+00:00 ' ,
}
self . assert_user ( Web , ' user.com ' , obj_as2 = expected_actor_as2 , direct = True ,
has_redirects = True )
2023-07-09 02:19:57 +00:00
2023-04-05 01:02:41 +00:00
# homepage object
2023-07-09 02:19:57 +00:00
actor = {
' objectType ' : ' person ' ,
' id ' : ' https://user.com/ ' ,
' url ' : ' https://user.com/ ' ,
' urls ' : [ { ' displayName ' : ' Ms. ☕ Baz ' , ' value ' : ' https://user.com/ ' } ] ,
' displayName ' : ' Ms. ☕ Baz ' ,
' updated ' : ' 2022-01-02T03:04:05+00:00 ' ,
}
2023-04-05 01:02:41 +00:00
self . assert_object ( ' https://user.com/ ' ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-07-09 02:19:57 +00:00
our_as1 = actor ,
2023-04-05 01:02:41 +00:00
type = ' person ' ,
)
# update activity
2023-04-02 02:13:51 +00:00
expected_as1 = {
' objectType ' : ' activity ' ,
' verb ' : ' update ' ,
' id ' : id ,
2023-07-09 02:19:57 +00:00
' actor ' : actor ,
' object ' : actor ,
2023-04-02 02:13:51 +00:00
}
2023-02-07 20:23:08 +00:00
self . assert_object ( id ,
2023-11-20 05:21:27 +00:00
users = [ self . user . key ] ,
2023-05-30 19:15:36 +00:00
source_protocol = ' web ' ,
2023-01-29 15:39:15 +00:00
status = ' complete ' ,
2023-04-02 02:13:51 +00:00
our_as1 = expected_as1 ,
2023-07-11 20:12:49 +00:00
delivered = [ ' https://inbox/ ' , ' https://shared/inbox ' ] ,
2023-01-29 22:13:58 +00:00
type = ' update ' ,
2023-03-17 23:42:45 +00:00
object_ids = [ ' https://user.com/ ' ] ,
2023-02-01 20:22:04 +00:00
labels = [ ' user ' , ' activity ' ] ,
2023-01-29 15:39:15 +00:00
)
2023-02-07 03:23:25 +00:00
2023-11-29 21:21:13 +00:00
def test_update_profile_homepage_no_mf2_or_metaformats ( self , mock_get , mock_post ) :
mock_get . return_value = requests_response ( """
< html >
< body >
< p > nothing to see here except < a href = " http://localhost/ " > link < / a > < / p >
< / body >
< / html > """ , url= ' https://user.com/ ' )
mock_post . return_value = requests_response ( ' OK ' )
Follower . get_or_create ( to = self . user , from_ = self . make_user (
' http://ddd ' , cls = ActivityPub , obj_as2 = { ' inbox ' : ' https://inbox ' } ) )
got = self . post ( ' /queue/webmention ' , data = {
' source ' : ' https://user.com/ ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
self . assertEqual ( 202 , got . status_code )
mock_get . assert_has_calls ( (
self . req ( ' https://user.com/ ' ) ,
) )
id = ' https://user.com/#update-2022-01-02T03:04:05+00:00 '
wrapped_id = f ' http://localhost/r/ { id } '
update_as2 = {
' @context ' : ' https://www.w3.org/ns/activitystreams ' ,
' type ' : ' Update ' ,
' id ' : wrapped_id ,
' actor ' : ' http://localhost/user.com ' ,
' object ' : {
* * ACTOR_AS2_FULL ,
' name ' : ' user.com ' ,
' updated ' : NOW . isoformat ( ) ,
' to ' : [ ' https://www.w3.org/ns/activitystreams#Public ' ] ,
} ,
' to ' : [ ' https://www.w3.org/ns/activitystreams#Public ' ] ,
}
del update_as2 [ ' object ' ] [ ' endpoints ' ]
del update_as2 [ ' object ' ] [ ' followers ' ]
del update_as2 [ ' object ' ] [ ' following ' ]
self . assert_deliveries ( mock_post , ( ' https://inbox/ ' , ) , update_as2 )
# updated Web user
self . assert_user ( Web , ' user.com ' , direct = True , has_redirects = True , obj_as2 = {
' type ' : ' Person ' ,
' id ' : ' https://user.com/ ' ,
' url ' : ' https://user.com/ ' ,
' name ' : ' user.com ' ,
' attachment ' : [ {
' name ' : ' Link ' ,
' type ' : ' PropertyValue ' ,
' value ' : ' <a rel= " me " href= " https://user.com " ><span class= " invisible " >https://</span>user.com</a> ' ,
} ] ,
' updated ' : NOW . isoformat ( ) ,
} )
2023-10-16 18:13:38 +00:00
def test_like_actor_is_not_source_domain ( self , mock_get , mock_post ) :
like_html = LIKE_HTML . replace (
' class= " p-author h-card " href= " https://user.com/ " ' ,
' class= " p-author h-card " href= " https://eve.com/ " ' )
mock_get . side_effect = [
requests_response ( like_html , url = ' https://user.com/like ' ) ,
TOOT_AS2 ,
ACTOR ,
]
with self . assertLogs ( ) as logs :
2023-10-18 04:50:19 +00:00
got = self . post ( ' /queue/webmention ' , data = {
2023-10-16 18:13:38 +00:00
' source ' : ' https://user.com/like ' ,
' target ' : ' https://fed.brid.gy/ ' ,
} )
2023-10-31 19:49:15 +00:00
self . assertEqual ( 202 , got . status_code )
2023-10-16 18:13:38 +00:00
self . assertIn (
" WARNING:models:actor https://user.com/ isn ' t https://user.com/like ' s author or actor [ ' https://eve.com/ ' ] " ,
logs . output )
2023-12-03 05:18:22 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
2024-01-01 20:22:23 +00:00
def test_poll_feed_atom ( self , mock_create_task , mock_get , _ ) :
2023-12-03 05:18:22 +00:00
common . RUN_TASKS_INLINE = False
2024-01-01 20:22:23 +00:00
self . user . obj . mf2 = ACTOR_MF2_REL_FEED_URL
self . user . obj . put ( )
2023-12-03 05:18:22 +00:00
2024-01-01 20:22:23 +00:00
feed = """ \
2023-12-03 05:18:22 +00:00
< ? xml version = " 1.0 " encoding = " UTF-8 " ? >
< entry xmlns = " http://www.w3.org/2005/Atom " >
2024-01-11 21:00:56 +00:00
< link rel = " alternate " type = " text/html " href = " https://user.com/post " / >
< content > I hereby ☕ post < / content >
2023-12-03 05:18:22 +00:00
< / entry >
2024-01-01 20:22:23 +00:00
"""
mock_get . return_value = requests_response (
feed , headers = { ' Content-Type ' : atom . CONTENT_TYPE } )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
2023-12-03 05:18:22 +00:00
self . assertEqual ( 200 , got . status_code )
2024-01-06 22:56:37 +00:00
self . assertEqual ( NOW , self . user . key . get ( ) . last_polled_feed )
2024-01-01 20:22:23 +00:00
mock_get . assert_has_calls ( (
2024-01-10 20:52:14 +00:00
self . req ( ' https://foo/feed ' ) ,
2024-01-01 20:22:23 +00:00
) )
obj = self . assert_object ( ' https://user.com/post ' ,
users = [ self . user . key ] ,
source_protocol = ' web ' ,
status = ' new ' ,
atom = feed ,
our_as1 = {
' objectType ' : ' activity ' ,
' verb ' : ' post ' ,
' id ' : ' https://user.com/post ' ,
' url ' : ' https://user.com/post ' ,
2024-01-10 00:02:41 +00:00
' actor ' : { ' id ' : ' https://user.com/ ' } ,
2024-01-01 20:22:23 +00:00
' object ' : {
' objectType ' : ' note ' ,
' id ' : ' https://user.com/post ' ,
' url ' : ' https://user.com/post ' ,
2024-01-10 00:02:41 +00:00
' author ' : { ' id ' : ' https://user.com/ ' } ,
2024-01-01 20:22:23 +00:00
' content ' : ' I hereby ☕ post ' ,
} ,
2024-01-01 22:00:52 +00:00
' feed_index ' : 0 ,
2024-01-01 20:22:23 +00:00
} ,
type = ' post ' ,
object_ids = [ ' https://user.com/post ' ] ,
labels = [ ' user ' , ' activity ' ] ,
)
2023-12-03 05:18:22 +00:00
self . assert_task ( mock_create_task , ' receive ' , ' /queue/receive ' ,
2024-01-01 20:22:23 +00:00
obj = obj . key . urlsafe ( ) ,
2023-12-03 05:18:22 +00:00
authed_as = ' user.com ' )
2024-01-01 22:47:03 +00:00
expected_eta = NOW_SECONDS + web . MIN_FEED_POLL_PERIOD . total_seconds ( )
self . assert_task ( mock_create_task , ' poll-feed ' , ' /queue/poll-feed ' ,
domain = ' user.com ' , eta_seconds = expected_eta )
2024-01-01 22:00:52 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
def test_poll_feed_rss ( self , mock_create_task , mock_get , _ ) :
common . RUN_TASKS_INLINE = False
self . user . obj . mf2 = {
* * ACTOR_MF2 ,
' rel-urls ' : {
' https://foo/rss ' : { ' rels ' : [ ' alternate ' ] , ' type ' : rss . CONTENT_TYPE } ,
} ,
}
self . user . obj . put ( )
feed = """ \
< ? xml version = ' 1.0 ' encoding = ' UTF-8 ' ? >
< rss version = " 2.0 " >
< channel >
< item >
< guid > http : / / post / a < / guid >
< description > I hereby ☕ post a < / description >
< pubDate > Tue , 04 Dec 2012 00 : 00 : 00 + 0000 < / pubDate >
< / item >
< item >
< guid > http : / / post / b < / guid >
< description > I hereby ☕ post b < / description >
< pubDate > Tue , 05 Dec 2012 00 : 00 : 00 + 0000 < / pubDate >
< / item >
< item >
< guid > http : / / post / c < / guid >
< description > I hereby ☕ post c < / description >
< pubDate > Tue , 08 Dec 2012 00 : 00 : 00 + 0000 < / pubDate >
< / item >
< / channel >
< / rss >
"""
mock_get . return_value = requests_response (
feed , headers = { ' Content-Type ' : rss . CONTENT_TYPE } )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 200 , got . status_code )
2024-01-06 22:56:37 +00:00
self . assertEqual ( NOW , self . user . key . get ( ) . last_polled_feed )
2024-01-01 22:00:52 +00:00
mock_get . assert_has_calls ( (
self . req ( ' https://foo/rss ' ) ,
) )
for i , ( id , day ) in enumerate ( [ ( ' a ' , 4 ) , ( ' b ' , 5 ) , ( ' c ' , 8 ) ] ) :
url = f ' http://post/ { id } '
obj = self . assert_object (
url ,
users = [ self . user . key ] ,
source_protocol = ' web ' ,
status = ' new ' ,
rss = feed ,
our_as1 = {
' objectType ' : ' activity ' ,
' verb ' : ' post ' ,
' id ' : url ,
' url ' : url ,
2024-01-10 00:02:41 +00:00
' actor ' : { ' id ' : ' https://user.com/ ' } ,
2024-01-01 22:00:52 +00:00
' object ' : {
' objectType ' : ' note ' ,
' id ' : url ,
' url ' : url ,
2024-01-10 00:02:41 +00:00
' author ' : { ' id ' : ' https://user.com/ ' } ,
2024-01-01 22:00:52 +00:00
' content ' : f ' I hereby ☕ post { id } ' ,
' published ' : f ' 2012-12-0 { day } T00:00:00+00:00 ' ,
} ,
' feed_index ' : i ,
} ,
type = ' post ' ,
object_ids = [ url ] ,
labels = [ ' user ' , ' activity ' ] ,
)
self . assert_task ( mock_create_task , ' receive ' , ' /queue/receive ' ,
obj = obj . key . urlsafe ( ) ,
authed_as = ' user.com ' )
2024-01-01 22:47:03 +00:00
# delay is average of 1d and 3d between posts
expected_eta = NOW_SECONDS + timedelta ( days = 2 ) . total_seconds ( )
self . assert_task ( mock_create_task , ' poll-feed ' , ' /queue/poll-feed ' ,
domain = ' user.com ' , eta_seconds = expected_eta )
2024-01-09 20:00:44 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
def test_poll_feed_xml_content_type ( self , mock_create_task , mock_get , _ ) :
common . RUN_TASKS_INLINE = False
self . user . obj . mf2 = ACTOR_MF2_REL_FEED_URL
self . user . obj . put ( )
feed = """ \
< ? xml version = " 1.0 " encoding = " UTF-8 " ? >
< entry xmlns = " http://www.w3.org/2005/Atom " >
< uri > https : / / user . com / post < / uri >
< content > I hereby ☕ post < / content >
< / entry >
"""
mock_get . return_value = requests_response (
feed , headers = { ' Content-Type ' : ' application/xml; charset=utf-8 ' } )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 200 , got . status_code )
self . assertEqual ( NOW , self . user . key . get ( ) . last_polled_feed )
mock_get . assert_has_calls ( (
2024-01-10 20:52:14 +00:00
self . req ( ' https://foo/feed ' ) ,
2024-01-09 20:00:44 +00:00
) )
assert Object . get_by_id ( ' https://user.com/post ' )
2024-01-11 21:00:56 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
def test_poll_feed_use_url_as_id ( self , mock_create_task , mock_get , _ ) :
common . RUN_TASKS_INLINE = False
self . user . obj . mf2 = ACTOR_MF2_REL_FEED_URL
self . user . obj . put ( )
feed = """ \
< ? xml version = " 1.0 " encoding = " UTF-8 " ? >
< entry xmlns = " http://www.w3.org/2005/Atom " >
< id > tag : user . com , 2999 : abc < / id >
< link rel = " alternate " type = " text/html " href = " https://user.com/post " / >
< content > I hereby ☕ post < / content >
< / entry >
"""
mock_get . return_value = requests_response (
feed , headers = { ' Content-Type ' : atom . CONTENT_TYPE } )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 200 , got . status_code )
self . assertEqual ( NOW , self . user . key . get ( ) . last_polled_feed )
mock_get . assert_has_calls ( (
self . req ( ' https://foo/feed ' ) ,
) )
obj = self . assert_object ( ' https://user.com/post ' ,
users = [ self . user . key ] ,
source_protocol = ' web ' ,
status = ' new ' ,
atom = feed ,
our_as1 = {
' objectType ' : ' activity ' ,
' verb ' : ' post ' ,
' id ' : ' https://user.com/post ' ,
' url ' : ' https://user.com/post ' ,
' actor ' : { ' id ' : ' https://user.com/ ' } ,
' object ' : {
' objectType ' : ' note ' ,
' id ' : ' https://user.com/post ' ,
' url ' : ' https://user.com/post ' ,
' author ' : { ' id ' : ' https://user.com/ ' } ,
' content ' : ' I hereby ☕ post ' ,
} ,
' feed_index ' : 0 ,
} ,
type = ' post ' ,
object_ids = [ ' https://user.com/post ' ] ,
labels = [ ' user ' , ' activity ' ] ,
)
self . assert_task ( mock_create_task , ' receive ' , ' /queue/receive ' ,
obj = obj . key . urlsafe ( ) ,
authed_as = ' user.com ' )
2024-01-06 23:04:22 +00:00
def test_poll_feed_fails ( self , mock_get , _ ) :
common . RUN_TASKS_INLINE = False
2024-01-10 20:52:14 +00:00
self . user . obj . mf2 = ACTOR_MF2_REL_FEED_URL
2024-01-06 23:04:22 +00:00
self . user . obj . put ( )
mock_get . side_effect = requests . ConnectionError ( )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 504 , got . status_code )
self . assertIsNone ( self . user . key . get ( ) . last_polled_feed )
2024-01-10 20:52:14 +00:00
def test_poll_feed_unsupported_content_types ( self , mock_get , _ ) :
2024-01-06 23:04:22 +00:00
common . RUN_TASKS_INLINE = False
2024-01-10 20:52:14 +00:00
self . user . obj . mf2 = ACTOR_MF2_REL_FEED_URL
2024-01-06 23:04:22 +00:00
self . user . obj . put ( )
2024-01-10 20:39:19 +00:00
for content_type in None , ' text/plain ' :
mock_get . return_value = requests_response (
' nope ' , headers = { ' Content-Type ' : content_type } )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 200 , got . status_code )
self . assertIsNone ( self . user . key . get ( ) . last_polled_feed )
2024-01-06 23:04:22 +00:00
2024-01-10 20:52:14 +00:00
def test_poll_feed_mismatched_content_type ( self , mock_get , _ ) :
common . RUN_TASKS_INLINE = False
self . user . obj . mf2 = ACTOR_MF2_REL_FEED_URL
self . user . obj . put ( )
mock_get . return_value = requests_response (
' <rss version= " 2.0 " ></rss> ' , headers = { ' Content-Type ' : atom . CONTENT_TYPE } )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 502 , got . status_code )
self . assertIsNone ( self . user . key . get ( ) . last_polled_feed )
2024-01-06 23:57:11 +00:00
@patch ( ' oauth_dropins.webutil.appengine_config.tasks_client.create_task ' )
def test_poll_feed_last_webmention_in_noop ( self , mock_create_task , mock_get , _ ) :
common . RUN_TASKS_INLINE = False
self . user . last_webmention_in = NOW
self . user . put ( )
self . user . obj . mf2 = {
* * ACTOR_MF2 ,
' rel-urls ' : {
' https://foo/rss ' : { ' rels ' : [ ' alternate ' ] , ' type ' : rss . CONTENT_TYPE } ,
} ,
}
self . user . obj . put ( )
got = self . post ( ' /queue/poll-feed ' , data = { ' domain ' : ' user.com ' } )
self . assertEqual ( 200 , got . status_code )
self . assertIsNone ( self . user . key . get ( ) . last_polled_feed )
mock_create_task . assert_not_called ( )
mock_get . assert_not_called ( )
2023-05-26 23:07:36 +00:00
def _test_verify ( self , redirects , hcard , actor , redirects_error = None ) :
2023-11-20 05:21:27 +00:00
self . user . has_redirects = False
self . user . put ( )
2023-06-14 20:46:13 +00:00
2023-11-20 05:21:27 +00:00
got = self . user . verify ( )
self . assertEqual ( self . user . key , got . key )
2023-05-26 23:07:36 +00:00
with self . subTest ( redirects = redirects , hcard = hcard , actor = actor ,
redirects_error = redirects_error ) :
2023-11-20 05:21:27 +00:00
self . assert_equals ( redirects , bool ( self . user . has_redirects ) )
self . assert_equals ( hcard , bool ( self . user . has_hcard ) )
2023-05-26 23:07:36 +00:00
if actor is None :
2023-11-20 05:21:27 +00:00
assert not self . user . obj or not self . user . obj . as1
2023-05-26 23:07:36 +00:00
else :
2023-11-20 05:21:27 +00:00
got = { k : v for k , v in self . user . obj . as1 . items ( )
2023-05-26 23:07:36 +00:00
if k in actor }
self . assert_equals ( actor , got )
2023-11-20 05:21:27 +00:00
self . assert_equals ( redirects_error , self . user . redirects_error )
2023-05-26 23:07:36 +00:00
def test_verify_neither ( self , mock_get , _ ) :
empty = requests_response ( ' ' )
mock_get . side_effect = [ empty , empty ]
2023-06-08 18:04:11 +00:00
self . _test_verify ( False , False , None , """ \
< pre > https : / / user . com / . well - known / webfinger ? resource = acct : user . com @user.com
returned HTTP 200 < / pre > """ )
2023-05-26 23:07:36 +00:00
def test_verify_redirect_strips_query_params ( self , mock_get , _ ) :
half_redir = requests_response (
status = 302 , redirected_url = ' http://localhost/.well-known/webfinger ' )
no_hcard = requests_response ( ' <html><body></body></html> ' )
mock_get . side_effect = [ half_redir , no_hcard ]
self . _test_verify ( False , False , None , """ \
Current vs expected : < pre > - http : / / localhost / . well - known / webfinger
+ https : / / fed . brid . gy / . well - known / webfinger ? resource = acct : user . com @user.com < / pre > """ )
def test_verify_multiple_redirects ( self , mock_get , _ ) :
two_redirs = requests_response (
status = 302 , redirected_url = [
' https://www.user.com/.well-known/webfinger?resource=acct:user.com@user.com ' ,
' http://localhost/.well-known/webfinger?resource=acct:user.com@user.com ' ,
] )
no_hcard = requests_response ( ' <html><body></body></html> ' )
mock_get . side_effect = [ two_redirs , no_hcard ]
self . _test_verify ( True , False , None )
def test_verify_redirect_404 ( self , mock_get , _ ) :
redir_404 = requests_response ( status = 404 , redirected_url = ' http://this/404s ' )
no_hcard = requests_response ( ' <html><body></body></html> ' )
mock_get . side_effect = [ redir_404 , no_hcard ]
self . _test_verify ( False , False , None , """ \
< pre > https : / / user . com / . well - known / webfinger ? resource = acct : user . com @user.com
redirected to :
http : / / this / 404 s
returned HTTP 404 < / pre > """ )
2023-06-08 18:04:11 +00:00
def test_verify_webfinger_urlencoded ( self , mock_get , _ ) :
mock_get . side_effect = [
requests_response (
status = 302 ,
redirected_url = ' http://localhost/.well-known/webfinger?resource=acct % 3Auser.com %40u ser.com ' ) ,
requests_response ( ' ' ) ,
]
self . _test_verify ( True , False , None )
2023-05-26 23:07:36 +00:00
def test_verify_no_hcard ( self , mock_get , _ ) :
mock_get . side_effect = [
2023-06-05 20:20:07 +00:00
FULL_REDIR ,
2023-05-26 23:07:36 +00:00
requests_response ( """
< body >
2023-11-29 20:00:48 +00:00
< div class = " h-review " >
2023-05-26 23:07:36 +00:00
< p class = " e-content " > foo bar < / p >
< / div >
< / body >
""" ),
]
self . _test_verify ( True , False , None )
def test_verify_non_representative_hcard ( self , mock_get , _ ) :
bad_hcard = requests_response (
' <html><body><a class= " h-card u-url " href= " https://a.b/ " >acct:me@user.com</a></body></html> ' ,
url = ' https://user.com/ ' ,
)
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ FULL_REDIR , bad_hcard ]
2023-05-26 23:07:36 +00:00
self . _test_verify ( True , False , None )
def test_verify_both_work ( self , mock_get , _ ) :
hcard = requests_response ( """
< html > < body class = " h-card " >
< a class = " u-url p-name " href = " / " > me < / a >
< a class = " u-url " href = " acct:myself@user.com " > Masto < / a >
< / body > < / html > """ ,
url = ' https://user.com/ ' ,
)
2023-06-05 20:20:07 +00:00
mock_get . side_effect = [ FULL_REDIR , hcard ]
2023-05-26 23:07:36 +00:00
self . _test_verify ( True , True , {
2023-06-16 04:22:20 +00:00
' objectType ' : ' person ' ,
' displayName ' : ' me ' ,
' url ' : ' https://user.com/ ' ,
' urls ' : [
{ ' value ' : ' https://user.com/ ' } ,
{ ' value ' : ' acct:myself@user.com ' } ,
] ,
2023-05-26 23:07:36 +00:00
} )
def test_verify_www_redirect ( self , mock_get , _ ) :
2023-11-15 22:23:08 +00:00
www_user = self . make_user ( ' www.user.com ' , cls = Web )
2023-05-26 23:07:36 +00:00
empty = requests_response ( ' ' )
mock_get . side_effect = [
requests_response ( status = 302 , redirected_url = ' https://www.user.com/ ' ) ,
empty , empty ,
]
got = www_user . verify ( )
self . assertEqual ( ' user.com ' , got . key . id ( ) )
2023-05-27 00:40:29 +00:00
root_user = Web . get_by_id ( ' user.com ' )
2023-05-26 23:07:36 +00:00
self . assertEqual ( root_user . key , www_user . key . get ( ) . use_instead )
2023-05-27 00:40:29 +00:00
self . assertEqual ( root_user . key , Web . get_or_create ( ' www.user.com ' ) . key )
2023-05-26 23:07:36 +00:00
def test_verify_actor_rel_me_links ( self , mock_get , _ ) :
mock_get . side_effect = [
2023-06-05 20:20:07 +00:00
FULL_REDIR ,
2023-05-26 23:07:36 +00:00
requests_response ( """
< body >
< div class = " h-card " >
< a class = " u-url " rel = " me " href = " /about-me " > Mrs . ☕ Foo < / a >
< a class = " u-url " rel = " me " href = " / " > should be ignored < / a >
< a class = " u-url " rel = " me " href = " http://one " title = " one title " >
one text
< / a >
< a class = " u-url " rel = " me " href = " https://two " title = " two title " > < / a >
< / div >
< / body >
""" , url= ' https://user.com/ ' ),
]
self . _test_verify ( True , True , {
2023-06-16 04:22:20 +00:00
' urls ' : [ {
' value ' : ' https://user.com/about-me ' ,
' displayName ' : ' Mrs. \u2615 Foo ' ,
} , {
' value ' : ' https://user.com/ ' ,
' displayName ' : ' should be ignored ' ,
} , {
' value ' : ' http://one ' ,
' displayName ' : ' one text ' ,
} , {
' value ' : ' https://two ' ,
' displayName ' : ' two title ' ,
} ] ,
} )
2023-05-26 23:07:36 +00:00
def test_verify_override_preferredUsername ( self , mock_get , _ ) :
mock_get . side_effect = [
2023-06-05 20:20:07 +00:00
FULL_REDIR ,
2023-05-26 23:07:36 +00:00
requests_response ( """
< body >
< a class = " h-card u-url " rel = " me " href = " /about-me " >
< span class = " p-nickname " > Nick < / span >
< / a >
< / body >
""" , url= ' https://user.com/ ' ),
2023-11-03 22:11:21 +00:00
NOT_FEDIVERSE ,
NOT_FEDIVERSE ,
2023-05-26 23:07:36 +00:00
]
2023-06-16 04:22:20 +00:00
self . _test_verify ( True , True , { } )
# preferredUsername stays y.z despite user's username. since Mastodon
# queries Webfinger for preferredUsername@fed.brid.gy
# https://github.com/snarfed/bridgy-fed/issues/77#issuecomment-949955109
2023-11-26 04:07:14 +00:00
postprocessed = ActivityPub . convert ( self . user . obj , from_user = self . user )
2023-06-16 04:22:20 +00:00
self . assertEqual ( ' user.com ' , postprocessed [ ' preferredUsername ' ] )
2023-05-26 23:07:36 +00:00
2023-06-01 01:34:33 +00:00
def test_web_url ( self , _ , __ ) :
2023-11-20 05:21:27 +00:00
self . assertEqual ( ' https://user.com/ ' , self . user . web_url ( ) )
2023-05-26 23:07:36 +00:00
2023-10-19 23:25:04 +00:00
def test_is_web_url ( self , * _ ) :
2023-11-20 05:21:27 +00:00
self . assertTrue ( self . user . is_web_url ( ' https://user.com/ ' ) )
self . assertTrue ( self . user . is_web_url ( ' https://www.user.com/ ' ) )
self . assertFalse ( self . user . is_web_url ( ' https://other.com/ ' ) )
2023-10-19 23:25:04 +00:00
2023-11-30 05:06:55 +00:00
def test_handle_as ( self , * _ ) :
2023-11-30 23:43:38 +00:00
self . user . ap_subdomain = ' web '
2023-11-30 05:06:55 +00:00
self . assertEqual ( ' @user.com@user.com ' , self . user . handle_as ( ActivityPub ) )
2023-06-01 01:34:33 +00:00
2023-11-20 05:21:27 +00:00
self . user . obj = Object ( id = ' a ' , as2 = { ' type ' : ' Person ' } )
2023-11-30 05:06:55 +00:00
self . assertEqual ( ' @user.com@user.com ' , self . user . handle_as ( ActivityPub ) )
2023-06-01 01:34:33 +00:00
2023-11-20 05:21:27 +00:00
self . user . obj . as2 = { ' url ' : ' http://foo ' }
2023-11-30 05:06:55 +00:00
self . assertEqual ( ' @user.com@user.com ' , self . user . handle_as ( ActivityPub ) )
2023-06-01 01:34:33 +00:00
2023-11-30 00:41:03 +00:00
self . user . has_redirects = False
2023-11-30 05:06:55 +00:00
self . assertEqual ( ' @user.com@web.brid.gy ' , self . user . handle_as ( ActivityPub ) )
2023-11-30 00:41:03 +00:00
2023-11-20 05:21:27 +00:00
self . user . obj . as2 = { ' url ' : [ ' http://foo ' , ' acct:bar@foo ' , ' acct:baz@user.com ' ] }
2023-11-30 00:41:03 +00:00
self . user . has_redirects = True
2023-11-30 05:06:55 +00:00
self . assertEqual ( ' @baz@user.com ' , self . user . handle_as ( ActivityPub ) )
2023-05-26 23:07:36 +00:00
2023-11-30 00:41:03 +00:00
self . user . has_redirects = False
2023-11-30 05:06:55 +00:00
self . assertEqual ( ' @user.com@web.brid.gy ' , self . user . handle_as ( ActivityPub ) )
self . assertEqual ( ' fake:handle:user.com ' , self . user . handle_as ( Fake ) )
self . assertEqual ( ' user.com.web.brid.gy ' , self . user . handle_as ( ' atproto ' ) )
2023-06-01 01:34:33 +00:00
2023-11-30 23:43:38 +00:00
self . user . ap_subdomain = ' fed '
self . assertEqual ( ' @user.com@fed.brid.gy ' , self . user . handle_as ( ActivityPub ) )
2023-12-01 00:31:41 +00:00
def test_id_as ( self , * _ ) :
self . assertEqual ( ' http://localhost/user.com ' , self . user . id_as ( ActivityPub ) )
2023-06-01 01:34:33 +00:00
2023-12-01 00:31:41 +00:00
with app . test_request_context ( ' ' , base_url = ' https://web.brid.gy/ ' ) :
2023-12-01 04:08:41 +00:00
self . assertEqual ( ' https://web.brid.gy/user.com ' ,
2023-12-01 00:31:41 +00:00
self . user . id_as ( ActivityPub ) )
self . user . ap_subdomain = ' fed '
with app . test_request_context ( ' ' , base_url = ' https://web.brid.gy/ ' ) :
self . assertEqual ( ' https://fed.brid.gy/user.com ' ,
self . user . id_as ( ActivityPub ) )
2023-05-26 23:07:36 +00:00
2023-05-31 00:24:49 +00:00
def test_check_web_site ( self , mock_get , _ ) :
redir = ' http://localhost/.well-known/webfinger?resource=acct:user.com@user.com '
mock_get . side_effect = (
requests_response ( ' ' , status = 302 , redirected_url = redir ) ,
2023-10-19 22:01:19 +00:00
ACTOR_HTML_RESP ,
2023-05-31 00:24:49 +00:00
)
2023-10-18 04:50:19 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://user.com/ ' } )
2023-05-31 00:24:49 +00:00
self . assert_equals ( 302 , got . status_code )
self . assert_equals ( ' /web/user.com ' , got . headers [ ' Location ' ] )
user = Web . get_by_id ( ' user.com ' )
self . assertTrue ( user . has_hcard )
2023-06-16 04:22:20 +00:00
self . assertEqual ( ' person ' , user . obj . as1 [ ' objectType ' ] )
2023-05-31 00:24:49 +00:00
2023-06-09 17:58:28 +00:00
def test_check_web_site_unicode_domain ( self , mock_get , _ ) :
mock_get . side_effect = (
requests_response ( ' ' ) ,
requests_response ( ' ' ) ,
2023-10-19 22:01:19 +00:00
requests_response ( ' ' ) ,
2023-06-09 17:58:28 +00:00
)
2023-10-18 04:50:19 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://☃.net/ ' } )
2023-06-09 17:58:28 +00:00
self . assert_equals ( 302 , got . status_code )
self . assert_equals ( ' /web/ %E 2 %98% 83.net ' , got . headers [ ' Location ' ] )
self . assertIsNotNone ( Web . get_by_id ( ' ☃.net ' ) )
def test_check_web_site_lower_cases_domain ( self , mock_get , _ ) :
mock_get . side_effect = (
requests_response ( ' ' ) ,
requests_response ( ' ' ) ,
2023-10-19 22:01:19 +00:00
requests_response ( ' ' ) ,
2023-06-09 17:58:28 +00:00
)
2023-10-18 04:50:19 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://AbC.oRg/ ' } )
2023-06-09 17:58:28 +00:00
self . assert_equals ( 302 , got . status_code )
self . assert_equals ( ' /web/abc.org ' , got . headers [ ' Location ' ] )
self . assertIsNotNone ( Web . get_by_id ( ' abc.org ' ) )
self . assertIsNone ( Web . get_by_id ( ' AbC.oRg ' ) )
2023-05-31 00:24:49 +00:00
def test_check_web_site_bad_url ( self , _ , __ ) :
2023-10-18 04:50:19 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' !!! ' } )
2023-09-25 21:45:04 +00:00
self . assert_equals ( 400 , got . status_code )
self . assertEqual ( [ ' !!! is not a valid or supported web site ' ] ,
get_flashed_messages ( ) )
2023-05-31 00:24:49 +00:00
self . assertEqual ( 1 , Web . query ( ) . count ( ) )
2023-06-23 19:22:37 +00:00
def test_check_web_site_bridgy_fed_domain ( self , _ , __ ) :
2024-01-06 21:59:31 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://web.brid.gy/foo ' } )
2023-09-25 21:45:04 +00:00
self . assert_equals ( 400 , got . status_code )
self . assertEqual (
2024-01-06 21:59:31 +00:00
[ ' https://web.brid.gy/foo is not a valid or supported web site ' ] ,
2023-09-25 21:45:04 +00:00
get_flashed_messages ( ) )
self . assertEqual ( 1 , Web . query ( ) . count ( ) )
def test_check_web_site_blocklisted ( self , _ , __ ) :
2023-10-18 04:50:19 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://t.co/ ' } )
2023-09-25 21:45:04 +00:00
self . assert_equals ( 400 , got . status_code )
self . assertEqual ( [ ' https://t.co/ is not a valid or supported web site ' ] ,
2023-06-23 19:22:37 +00:00
get_flashed_messages ( ) )
self . assertEqual ( 1 , Web . query ( ) . count ( ) )
2023-10-19 22:01:19 +00:00
def test_check_webfinger_redirects_then_fails ( self , mock_get , _ ) :
2023-05-31 00:24:49 +00:00
redir = ' http://localhost/.well-known/webfinger?resource=acct:orig@orig '
mock_get . side_effect = (
2023-10-19 22:01:19 +00:00
ACTOR_HTML_RESP ,
2023-05-31 00:24:49 +00:00
requests_response ( ' ' , status = 302 , redirected_url = redir ) ,
requests_response ( ' ' , status = 503 ) ,
)
2023-10-18 04:50:19 +00:00
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://orig.co/ ' } )
2023-05-31 00:24:49 +00:00
self . assert_equals ( 200 , got . status_code , got . headers )
self . assertTrue ( get_flashed_messages ( ) [ 0 ] . startswith (
2023-06-09 17:58:28 +00:00
" Couldn ' t connect to https://orig.co/: " ) )
2023-05-31 00:24:49 +00:00
2023-10-19 22:01:19 +00:00
def test_check_web_site_fetch_fails ( self , mock_get , _ ) :
mock_get . return_value = requests_response ( ' ' , status = 503 )
got = self . post ( ' /web-site ' , data = { ' url ' : ' https://orig.co/ ' } )
self . assert_equals ( 200 , got . status_code , got . headers )
self . assertTrue ( get_flashed_messages ( ) [ 0 ] . startswith (
" Couldn ' t connect to https://orig.co/: " ) )
2023-04-05 01:02:41 +00:00
2023-05-31 00:24:49 +00:00
@patch ( ' requests.post ' )
@patch ( ' requests.get ' )
2023-07-10 17:14:12 +00:00
class WebUtilTest ( TestCase ) :
2023-04-05 01:02:41 +00:00
def setUp ( self ) :
super ( ) . setUp ( )
2023-11-20 05:21:27 +00:00
self . user = self . make_user ( ' user.com ' , cls = Web )
2023-04-05 01:02:41 +00:00
2023-06-13 21:30:00 +00:00
def test_key_for ( self , * _ ) :
for id in ' user.com ' , ' http://user.com ' , ' https://user.com/ ' :
self . assertEqual ( Web ( id = ' user.com ' ) . key , Web . key_for ( id ) )
2023-09-27 16:42:40 +00:00
for bad in ' ' , ' foo ' , ' https://foo/ ' , ' foo bar ' , ' user.json ' :
2023-07-23 06:32:55 +00:00
with self . subTest ( bad = bad ) :
self . assertIsNone ( Web . key_for ( bad ) )
2023-06-13 21:30:00 +00:00
2023-10-10 16:57:10 +00:00
# no stored user
self . assertEqual ( Web ( id = ' foo.com ' ) . key , Web . key_for ( ' foo.com ' ) )
def test_key_for_use_instead ( self , * _ ) :
2023-11-20 05:21:27 +00:00
Web ( id = ' www.user.com ' , use_instead = self . user . key ) . put ( )
self . assertEqual ( self . user . key , Web . key_for ( ' www.user.com ' ) )
2023-10-10 16:57:10 +00:00
2023-09-25 17:27:08 +00:00
def test_handle ( self , * _ ) :
2023-11-20 05:21:27 +00:00
self . assertEqual ( ' user.com ' , self . user . handle )
2023-09-25 17:27:08 +00:00
2023-06-13 20:17:11 +00:00
def test_owns_id ( self , * _ ) :
2023-06-14 20:46:13 +00:00
self . assertIsNone ( Web . owns_id ( ' http://foo.com ' ) )
self . assertIsNone ( Web . owns_id ( ' https://bar.com/ ' ) )
self . assertIsNone ( Web . owns_id ( ' https://bar.com/baz ' ) )
self . assertIsNone ( Web . owns_id ( ' https://bar/ ' ) )
2023-06-13 20:17:11 +00:00
self . assertFalse ( Web . owns_id ( ' at://did:plc:foo/bar/123 ' ) )
self . assertFalse ( Web . owns_id ( ' e45fab982 ' ) )
2023-06-14 20:46:13 +00:00
self . assertFalse ( Web . owns_id ( ' user.com ' ) )
2023-11-20 05:21:27 +00:00
self . user . has_redirects = True
self . user . put ( )
2023-06-14 20:46:13 +00:00
self . assertTrue ( Web . owns_id ( ' user.com ' ) )
2023-11-20 05:21:27 +00:00
self . user . key . delete ( )
2023-06-14 20:46:13 +00:00
self . assertIsNone ( Web . owns_id ( ' user.com ' ) )
2023-07-02 21:55:05 +00:00
self . assertFalse ( Web . owns_id ( ' https://twitter.com/foo ' ) )
self . assertFalse ( Web . owns_id ( ' https://fed.brid.gy/foo ' ) )
2023-09-22 19:14:50 +00:00
def test_owns_handle ( self , * _ ) :
self . assertIsNone ( Web . owns_handle ( ' foo.com ' ) )
self . assertIsNone ( Web . owns_handle ( ' foo.bar.com ' ) )
self . assertFalse ( Web . owns_handle ( ' foo ' ) )
self . assertFalse ( Web . owns_handle ( ' @foo ' ) )
self . assertFalse ( Web . owns_handle ( ' @foo.com ' ) )
self . assertFalse ( Web . owns_handle ( ' @foo@bar.com ' ) )
self . assertFalse ( Web . owns_handle ( ' foo@bar.com ' ) )
2023-09-22 20:11:15 +00:00
def test_handle_to_id ( self , * _ ) :
self . assertEqual ( ' foo.com ' , Web . handle_to_id ( ' foo.com ' ) )
2023-09-23 21:55:49 +00:00
def test_profile_id ( self , * _ ) :
self . assertEqual ( ' https://foo.com/ ' , Web ( id = ' foo.com ' ) . profile_id ( ) )
2023-04-05 01:02:41 +00:00
def test_fetch ( self , mock_get , __ ) :
mock_get . return_value = REPOST
obj = Object ( id = ' https://user.com/post ' )
2023-05-27 00:40:29 +00:00
Web . fetch ( obj )
2023-04-05 01:02:41 +00:00
self . assert_equals ( { * * REPOST_MF2 , ' url ' : ' https://user.com/repost ' } , obj . mf2 )
def test_fetch_redirect ( self , mock_get , __ ) :
2023-06-20 18:22:54 +00:00
mock_get . return_value = requests_response (
2023-07-25 04:28:37 +00:00
REPOST_HTML , redirected_url = ' http://new/url ' )
2023-04-05 01:02:41 +00:00
obj = Object ( id = ' https://orig/url ' )
2023-05-27 00:40:29 +00:00
Web . fetch ( obj )
2023-04-05 01:02:41 +00:00
self . assert_equals ( ' http://new/url ' , obj . mf2 [ ' url ' ] )
self . assert_equals ( { * * REPOST_MF2 , ' url ' : ' http://new/url ' } , obj . mf2 )
self . assertIsNone ( Object . get_by_id ( ' http://new/url ' ) )
def test_fetch_error ( self , mock_get , __ ) :
mock_get . return_value = requests_response ( REPOST_HTML , status = 405 )
2023-06-20 18:22:54 +00:00
with self . assertRaises ( BadGateway ) :
2023-05-27 00:40:29 +00:00
Web . fetch ( Object ( id = ' https://foo ' ) , gateway = True )
2023-04-05 01:02:41 +00:00
def test_fetch_run_authorship ( self , mock_get , __ ) :
mock_get . side_effect = [
# post
requests_response (
REPOST_HTML . replace (
' <a class= " p-author h-card " href= " https://user.com/ " >Ms. ☕ Baz</a> ' ,
' <a class= " u-author " href= " https://user.com/ " ></a> ' ) ,
content_type = CONTENT_TYPE_HTML , url = ' https://user.com/repost ' ) ,
# author URL
ACTOR ,
]
obj = Object ( id = ' https://user.com/repost ' )
2023-05-27 00:40:29 +00:00
Web . fetch ( obj )
2023-04-05 01:02:41 +00:00
self . assert_equals ( { * * REPOST_MF2 , ' url ' : ' https://user.com/repost ' } , obj . mf2 )
2023-07-29 01:58:48 +00:00
def test_fetch_default_missing_author_to_user ( self , mock_get , __ ) :
2023-07-10 18:26:09 +00:00
mock_get . return_value = requests_response ( """ \
< html >
< body class = " h-entry " >
< p class = " p-name " > hello i am a post < / p >
< / body >
< / html >
2023-07-25 04:28:37 +00:00
""" , url= ' https://user.com/post ' )
2023-07-10 18:26:09 +00:00
obj = Object ( id = ' https://user.com/post ' )
Web . fetch ( obj )
self . assert_equals ( {
' type ' : [ ' h-entry ' ] ,
' properties ' : {
' name ' : [ ' hello i am a post ' ] ,
' author ' : [ ' https://user.com/ ' ] ,
' url ' : [ ' https://user.com/post ' ] ,
} ,
' url ' : ' https://user.com/post ' ,
} , obj . mf2 )
2023-07-29 01:58:48 +00:00
def test_fetch_default_author_missing_url_to_user ( self , mock_get , __ ) :
mock_get . return_value = requests_response ( """ \
< html >
< body class = " h-entry " >
< p class = " p-author h-card " > Alice < / p >
< / body >
< / html >
""" , url= ' https://user.com/post ' )
obj = Object ( id = ' https://user.com/post ' )
Web . fetch ( obj )
self . assert_equals ( {
' type ' : [ ' h-entry ' ] ,
' properties ' : {
' author ' : [ {
' type ' : [ ' h-card ' ] ,
' properties ' : {
' name ' : [ ' Alice ' ] ,
' url ' : [ ' https://user.com/ ' ] ,
} ,
' value ' : ' Alice ' ,
} ] ,
' url ' : [ ' https://user.com/post ' ] ,
} ,
' url ' : ' https://user.com/post ' ,
} , obj . mf2 )
2023-07-10 18:26:09 +00:00
2023-11-27 00:22:24 +00:00
def test_load_id_is_url_not_uid ( self , mock_get , __ ) :
mock_get . return_value = requests_response ( NOTE_HTML . replace (
' <a href= " http://localhost/ " ></a> ' , """ \
< data class = " p-uid " value = " abc123 " > < / data >
< a href = " http://localhost/ " > < / a >
""" ))
obj = Web . load ( ' https://user.com/post ' )
self . assertEqual ( ' https://user.com/post ' , obj . key . id ( ) )
self . assertEqual ( ' https://user.com/post ' , obj . as1 [ ' id ' ] )
self . assertEqual ( NOTE_MF2 , obj . mf2 )
self . assertNotIn ( ' uid ' , obj . mf2 [ ' properties ' ] )
2023-04-05 01:02:41 +00:00
def test_fetch_user_homepage ( self , mock_get , __ ) :
2023-06-05 20:20:07 +00:00
mock_get . return_value = ACTOR_HTML_RESP
2023-04-05 01:02:41 +00:00
obj = Object ( id = ' https://user.com/ ' )
2023-05-27 00:40:29 +00:00
Web . fetch ( obj )
2023-04-05 01:02:41 +00:00
self . assert_equals ( {
* * ACTOR_MF2_REL_URLS ,
' url ' : ' https://user.com/ ' ,
} , obj . mf2 )
2024-01-08 21:03:44 +00:00
self . assert_equals ( ACTOR_AS1_UNWRAPPED_URLS , obj . as1 )
2023-04-05 01:02:41 +00:00
2023-11-29 20:00:48 +00:00
def test_fetch_user_homepage_metaformats ( self , mock_get , __ ) :
mock_get . return_value = requests_response (
ACTOR_HTML_METAFORMATS , url = ' https://user.com/ ' )
obj = Object ( id = ' https://user.com/ ' )
Web . fetch ( obj )
2023-11-29 21:21:13 +00:00
self . assert_equals ( ACTOR_MF2 , obj . mf2 , ignore = [ ' rel-urls ' , ' url ' ] )
self . assert_equals ( ACTOR_AS1_UNWRAPPED , obj . as1 , ignore = [ ' author ' ] )
def test_fetch_user_homepage_metaformats_mf2_but_no_hcard ( self , mock_get , __ ) :
html = ACTOR_HTML_METAFORMATS . replace ( ' </html> ' , """ \
< div class = " h-entry " > < span class = " e-content " > foo < / span > < / div >
< / html > """ )
mock_get . return_value = requests_response ( html , url = ' https://user.com/ ' )
obj = Object ( id = ' https://user.com/ ' )
Web . fetch ( obj )
self . assert_equals ( ACTOR_MF2 , obj . mf2 , ignore = [ ' rel-urls ' , ' url ' ] )
2023-11-29 20:00:48 +00:00
self . assert_equals ( ACTOR_AS1_UNWRAPPED , obj . as1 , ignore = [ ' author ' ] )
2023-04-05 01:02:41 +00:00
def test_fetch_user_homepage_no_hcard ( self , mock_get , __ ) :
2023-11-30 22:43:06 +00:00
mock_get . return_value = requests_response ( ' <html><body>foo<body><html> ' ,
url = ' https://user.com/ ' )
2023-04-05 01:02:41 +00:00
obj = Object ( id = ' https://user.com/ ' )
2023-11-30 22:43:06 +00:00
self . assertTrue ( Web . fetch ( obj ) )
self . assert_equals ( {
' type ' : [ ' h-card ' ] ,
' properties ' : {
' url ' : [ ' https://user.com/ ' ] ,
' name ' : [ ' user.com ' ] ,
} ,
} , obj . mf2 , ignore = [ ' rel-urls ' , ' url ' ] )
2023-04-05 01:02:41 +00:00
def test_fetch_user_homepage_non_representative_hcard ( self , mock_get , __ ) :
mock_get . return_value = requests_response (
' <html><body><a class= " h-card u-url " href= " https://a.b/ " >acct:me@y.z</a></body></html> ' ,
content_type = CONTENT_TYPE_HTML )
obj = Object ( id = ' https://user.com/ ' )
2023-06-03 14:28:01 +00:00
with self . assertRaises ( BadRequest ) :
Web . fetch ( obj )
2023-06-02 19:55:07 +00:00
def test_fetch_user_homepage_fail ( self , mock_get , __ ) :
mock_get . return_value = requests_response ( ' ' , status = 500 )
obj = Object ( id = ' https://user.com/ ' )
with self . assertRaises ( requests . HTTPError ) as e :
2023-05-27 00:40:29 +00:00
Web . fetch ( obj )
2023-06-02 19:55:07 +00:00
self . assertEqual ( 500 , e . status_code )
2023-04-05 01:02:41 +00:00
2023-07-14 19:45:47 +00:00
def test_fetch_not_html ( self , mock_get , __ ) :
mock_get . return_value = self . as2_resp ( { } )
obj = Object ( id = ' https://user.com/post ' )
self . assertFalse ( Web . fetch ( obj ) )
self . assertIsNone ( obj . as1 )
def test_fetch_non_url ( self , mock_get , __ ) :
obj = Object ( id = ' x y z ' )
self . assertFalse ( Web . fetch ( obj ) )
self . assertIsNone ( obj . as1 )
def test_fetch_no_mf2 ( self , mock_get , __ ) :
mock_get . return_value = requests_response (
' <html> \n <body>foo</body> \n </html> ' )
obj = Object ( id = ' https://user.com/post ' )
self . assertFalse ( Web . fetch ( obj ) )
self . assertIsNone ( obj . as1 )
2024-01-06 21:59:31 +00:00
def test_fetch_instance_actor ( self , _ , __ ) :
obj = Object ( id = f ' https:// { common . PRIMARY_DOMAIN } / ' )
self . assertTrue ( Web . fetch ( obj ) )
self . assertEqual ( obj . as2 ,
json_loads ( util . read ( ' static/instance-actor.as2.json ' ) ) )
2023-12-08 18:01:47 +00:00
def test_fetch_resolves_relative_urls ( self , mock_get , __ ) :
mock_get . return_value = requests_response ( """ \
< html >
< body class = " h-entry " >
< a class = " u-url " href = " repost " > < / a >
< a class = " u-repost-of " href = " /orig/post " > reposted ! < / a >
< a class = " u-author " href = " /me " > < / a >
< p class = " e-content " >
Hello < img src = " hi.jpg " class = " u-photo " > < a href = " /there " > there < / a > .
< / p >
< / body >
< / html >
""" , url= ' https://user.com/my/post ' )
obj = Object ( id = ' https://user.com/post ' )
Web . fetch ( obj )
self . assert_equals ( {
' type ' : [ ' h-entry ' ] ,
' properties ' : {
' url ' : [ ' https://user.com/my/repost ' ] ,
' repost-of ' : [ ' https://user.com/orig/post ' ] ,
' author ' : [ ' https://user.com/me ' ] ,
' photo ' : [ ' https://user.com/my/hi.jpg ' ] ,
' content ' : [ {
' html ' : ' Hello <img class= " u-photo " src= " https://user.com/my/hi.jpg " /> <a href= " https://user.com/there " >there</a>. ' ,
' value ' : ' Hello https://user.com/my/hi.jpg there. ' ,
} ] ,
} ,
' url ' : ' https://user.com/my/post ' ,
} , obj . mf2 )
2023-07-15 00:22:59 +00:00
def test_send_note_does_nothing ( self , mock_get , mock_post ) :
Follower . get_or_create (
to = self . make_user ( ' https://mas.to/bob ' , cls = ActivityPub ) ,
2023-11-20 05:21:27 +00:00
from_ = self . user )
2023-07-15 00:22:59 +00:00
self . assertFalse ( Web . send (
Object ( id = ' http://mas.to/note ' , as2 = test_activitypub . NOTE ) ,
2023-07-15 21:33:54 +00:00
' https://user.com/ ' ) )
mock_get . assert_not_called ( )
mock_post . assert_not_called ( )
def test_send_unrelated_repost_does_nothing ( self , mock_get , mock_post ) :
Follower . get_or_create (
to = self . make_user ( ' https://mas.to/bob ' , cls = ActivityPub ) ,
2023-11-20 05:21:27 +00:00
from_ = self . user )
2023-07-15 21:33:54 +00:00
self . assertFalse ( Web . send (
Object ( id = ' http://mas.to/note ' , as2 = {
* * test_activitypub . REPOST ,
' actor ' : ' https://mas.to/bob ' ,
} ) ,
' https://user.com/ ' ) )
mock_get . assert_not_called ( )
mock_post . assert_not_called ( )
def test_send_unrelated_reply_does_nothing ( self , mock_get , mock_post ) :
Follower . get_or_create (
to = self . make_user ( ' https://mas.to/bob ' , cls = ActivityPub ) ,
2023-11-20 05:21:27 +00:00
from_ = self . user )
2023-07-15 21:33:54 +00:00
self . assertFalse ( Web . send (
Object ( id = ' http://mas.to/note ' , as2 = {
* * test_activitypub . REPLY ,
' actor ' : ' https://mas.to/bob ' ,
} ) ,
' https://user.com/ ' ) )
2023-07-15 00:22:59 +00:00
mock_get . assert_not_called ( )
mock_post . assert_not_called ( )
def test_send_like ( self , mock_get , mock_post ) :
2023-04-05 01:02:41 +00:00
mock_get . return_value = WEBMENTION_REL_LINK
mock_post . return_value = requests_response ( )
2023-06-02 19:55:07 +00:00
obj = Object ( id = ' http://mas.to/like#ok ' , as2 = test_activitypub . LIKE ,
source_protocol = ' ui ' )
2023-05-27 00:40:29 +00:00
self . assertTrue ( Web . send ( obj , ' https://user.com/post ' ) )
2023-04-05 01:02:41 +00:00
self . assert_req ( mock_get , ' https://user.com/post ' )
args , kwargs = mock_post . call_args
self . assertEqual ( ( ' https://user.com/webmention ' , ) , args )
self . assertEqual ( {
2023-11-07 04:17:23 +00:00
' source ' : ' https://fed.brid.gy/convert/web/http://mas.to/like %23o k ' ,
2023-04-05 01:02:41 +00:00
' target ' : ' https://user.com/post ' ,
} , kwargs [ ' data ' ] )
def test_send_no_endpoint ( self , mock_get , mock_post ) :
mock_get . return_value = WEBMENTION_NO_REL_LINK
2023-11-06 20:18:11 +00:00
obj = Object ( id = ' http://mas.to/like#ok ' , as2 = test_activitypub . LIKE ,
source_protocol = ' activitypub ' )
2023-04-05 01:02:41 +00:00
2023-05-27 00:40:29 +00:00
self . assertFalse ( Web . send ( obj , ' https://user.com/post ' ) )
2023-04-05 01:02:41 +00:00
self . assert_req ( mock_get , ' https://user.com/post ' )
mock_post . assert_not_called ( )
2023-05-24 04:30:57 +00:00
2023-07-10 17:14:12 +00:00
def test_send_skips_accept_follow ( self , mock_get , mock_post ) :
obj = Object ( id = ' https://user.com/accept ' , as2 = test_activitypub . ACCEPT )
self . assertFalse ( Web . send ( obj , ' https://user.com/ ' ) )
mock_get . assert_not_called ( )
2023-07-23 06:02:12 +00:00
mock_post . assert_not_called ( )
def test_send_blocklisted ( self , mock_get , mock_post ) :
obj = Object ( id = ' http://mas.to/like#ok ' , as2 = {
* * test_activitypub . LIKE ,
' object ' : ' https://fed.brid.gy/foo ' ,
} )
self . assertFalse ( Web . send ( obj , ' https://fed.brid.gy/foo ' ) )
mock_get . assert_not_called ( )
2023-07-10 17:14:12 +00:00
mock_post . assert_not_called ( )
2023-06-04 22:11:52 +00:00
def test_send_errors ( self , mock_get , mock_post ) :
for err in [
requests . HTTPError ( response = util . Struct ( status_code = ' 429 ' , text = ' ' ) ) ,
requests . ConnectionError ( ) ,
] :
2023-07-09 15:28:47 +00:00
with self . subTest ( err = err ) :
mock_get . return_value = WEBMENTION_REL_LINK
mock_post . side_effect = err
obj = Object ( id = ' http://mas.to/like#ok ' , as2 = test_activitypub . LIKE ,
source_protocol = ' ui ' )
with self . assertRaises ( err . __class__ ) :
Web . send ( obj , ' https://user.com/post ' )
self . assert_req ( mock_get , ' https://user.com/post ' )
args , kwargs = mock_post . call_args
self . assertEqual ( ( ' https://user.com/webmention ' , ) , args )
self . assertEqual ( {
2023-11-07 04:17:23 +00:00
' source ' : ' https://fed.brid.gy/convert/web/http://mas.to/like %23o k ' ,
2023-07-09 15:28:47 +00:00
' target ' : ' https://user.com/post ' ,
} , kwargs [ ' data ' ] )
2023-06-04 22:11:52 +00:00
2023-10-24 23:09:28 +00:00
def test_convert ( self , _ , __ ) :
2023-05-24 04:30:57 +00:00
obj = Object ( id = ' http://orig ' , mf2 = ACTOR_MF2 )
self . assert_multiline_equals ( """ \
< ! DOCTYPE html >
< html >
< head > < meta charset = " utf-8 " >
< meta http - equiv = " refresh " content = " 0;url=https://user.com/ " > < / head >
< body class = " " >
< span class = " h-card " >
2023-07-10 19:11:29 +00:00
< data class = " p-uid " value = " https://user.com/ " > < / data >
2023-05-24 04:30:57 +00:00
< a class = " p-name u-url " href = " https://user.com/ " > Ms . ☕ Baz < / a >
< / span >
< / body >
< / html >
2023-11-26 04:07:14 +00:00
""" , Web.convert(obj, from_user=None), ignore_blanks=True)
2023-06-16 20:16:17 +00:00
2023-11-03 22:52:37 +00:00
def test_convert_translates_ids ( self , * _ ) :
self . store_object ( id = ' http://fed/post ' , source_protocol = ' activitypub ' )
self . assert_multiline_in ( """ \
< article class = " h-entry " >
< span class = " p-uid " > https : / / fa . brid . gy / convert / web / fake : reply < / span >
< span class = " p-author h-card " >
2023-11-07 04:17:23 +00:00
< data class = " p-uid " value = " https://fa.brid.gy/web/fake:alice " > < / data >
2023-11-03 22:52:37 +00:00
< span class = " p-name " > Ms . Alice < / span >
< / span >
< span class = " p-name " > < / span >
< div class = " " >
< / div >
2023-11-07 04:17:23 +00:00
< a class = " u-in-reply-to " href = " https://ap.brid.gy/convert/web/http://fed/post " > < / a >
2023-11-03 22:52:37 +00:00
< / article > """ , Web.convert(Object(our_as1= {
' objectType ' : ' activity ' ,
' verb ' : ' post ' ,
' object ' : {
' id ' : ' fake:reply ' ,
' objectType ' : ' note ' ,
' inReplyTo ' : ' http://fed/post ' ,
' author ' : {
' id ' : ' fake:alice ' ,
' displayName ' : ' Ms. Alice ' ,
} ,
} ,
2023-11-26 04:07:14 +00:00
} ) , from_user = None ) , ignore_blanks = True )
2023-11-03 22:52:37 +00:00
2023-06-21 14:22:03 +00:00
def test_target_for ( self , _ , __ ) :
2023-06-16 20:16:17 +00:00
self . assertIsNone ( Web . target_for ( Object ( id = ' x ' , source_protocol = ' web ' ) ) )
self . assertEqual ( ' http://foo ' , Web . target_for (
Object ( id = ' http://foo ' , source_protocol = ' web ' ) ) )
self . assertEqual ( ' http://foo ' , Web . target_for (
Object ( id = ' http://foo ' , source_protocol = ' web ' ) , shared = True ) )