kopia lustrzana https://github.com/snarfed/bridgy-fed
noop, lint fixes from flake8
remaining: $ flake8 --extend-ignore=E501 *.py tests/*.py "pyflakes" failed during execution due to "'FlakesChecker' object has no attribute 'NAMEDEXPR'" Run flake8 with greater verbosity to see more details activitypub.py:15:1: F401 'oauth_dropins.webutil.util.json_loads' imported but unused activitypub.py:36:1: F401 'web' imported but unused activitypub.py:48:1: E302 expected 2 blank lines, found 1 activitypub.py:51:9: F811 redefinition of unused 'web' from line 36 app.py:6:1: F401 'flask_app.app' imported but unused app.py:9:1: F401 'activitypub' imported but unused app.py:9:1: F401 'convert' imported but unused app.py:9:1: F401 'follow' imported but unused app.py:9:1: F401 'pages' imported but unused app.py:9:1: F401 'redirect' imported but unused app.py:9:1: F401 'superfeedr' imported but unused app.py:9:1: F401 'ui' imported but unused app.py:9:1: F401 'webfinger' imported but unused app.py:9:1: F401 'web' imported but unused app.py:9:1: F401 'xrpc_actor' imported but unused app.py:9:1: F401 'xrpc_feed' imported but unused app.py:9:1: F401 'xrpc_graph' imported but unused app.py:9:19: E401 multiple imports on one line models.py:19:1: F401 'oauth_dropins.webutil.util.json_loads' imported but unused models.py:364:31: E114 indentation is not a multiple of four (comment) models.py:364:31: E116 unexpected indentation (comment) protocol.py:17:1: F401 'oauth_dropins.webutil.util.json_loads' imported but unused redirect.py:26:1: F401 'oauth_dropins.webutil.util.json_loads' imported but unused web.py:18:1: F401 'oauth_dropins.webutil.util.json_loads' imported but unused webfinger.py:13:1: F401 'oauth_dropins.webutil.util.json_loads' imported but unused webfinger.py:110:13: E122 continuation line missing indentation or outdented webfinger.py:111:13: E122 continuation line missing indentation or outdented webfinger.py:131:13: E122 continuation line missing indentation or outdented webfinger.py:132:13: E122 continuation line missing indentation or outdented webfinger.py:133:13: E122 continuation line missing indentation or outdented webfinger.py:134:13: E122 continuation line missing indentation or outdented tests/__init__.py:2:1: F401 'oauth_dropins.webutil.tests' imported but unused tests/test_follow.py:11:1: F401 'oauth_dropins.webutil.util.json_dumps' imported but unused tests/test_follow.py:14:1: F401 '.testutil.Fake' imported but unused tests/test_models.py:156:15: E122 continuation line missing indentation or outdented tests/test_models.py:157:15: E122 continuation line missing indentation or outdented tests/test_models.py:158:11: E122 continuation line missing indentation or outdented tests/test_web.py:12:1: F401 'oauth_dropins.webutil.util.json_dumps' imported but unused tests/test_web.py:17:1: F401 '.testutil' imported but unused tests/test_web.py:1513:13: E128 continuation line under-indented for visual indent tests/test_web.py:1514:9: E124 closing bracket does not match visual indentation tests/testutil.py:106:1: E402 module level import not at top of file tests/testutil.py:107:1: E402 module level import not at top of file tests/testutil.py:108:1: E402 module level import not at top of file tests/testutil.py:109:1: E402 module level import not at top of file tests/testutil.py:110:1: E402 module level import not at top of file tests/testutil.py:301:24: E203 whitespace before ':' tests/testutil.py:301:25: E701 multiple statements on one line (colon) tests/testutil.py:301:25: E231 missing whitespace after ':'pull/561/head
rodzic
2493e566ed
commit
ab1c28ee4d
|
@ -28,7 +28,7 @@ from common import (
|
|||
redirect_wrap,
|
||||
TLD_BLOCKLIST,
|
||||
)
|
||||
from models import Follower, Object, PROTOCOLS, Target, User
|
||||
from models import Follower, Object, PROTOCOLS, User
|
||||
from protocol import Protocol
|
||||
|
||||
# TODO: remove this. we only need it to make sure Web is registered in PROTOCOLS
|
||||
|
@ -263,8 +263,8 @@ class ActivityPub(User, Protocol):
|
|||
key_actor = cls.load(keyId)
|
||||
except BadGateway:
|
||||
obj_id = as1.get_object(activity).get('id')
|
||||
if (activity.get('type') == 'Delete' and obj_id and
|
||||
keyId == fragmentless(obj_id)):
|
||||
if (activity.get('type') == 'Delete' and obj_id
|
||||
and keyId == fragmentless(obj_id)):
|
||||
logger.info('Object/actor being deleted is also keyId')
|
||||
key_actor = Object(id=keyId, source_protocol='activitypub', deleted=True)
|
||||
key_actor.put()
|
||||
|
@ -356,12 +356,13 @@ def signed_request(fn, url, data=None, log_data=True, headers=None, **kwargs):
|
|||
|
||||
# handle GET redirects manually so that we generate a new HTTP signature
|
||||
if resp.is_redirect and fn == util.requests_get:
|
||||
return signed_request(fn, resp.headers['Location'], data=data,
|
||||
headers=headers, log_data=log_data, **kwargs)
|
||||
return signed_request(fn, resp.headers['Location'], data=data,
|
||||
headers=headers, log_data=log_data, **kwargs)
|
||||
|
||||
type = common.content_type(resp)
|
||||
if (type and type != 'text/html' and
|
||||
(type.startswith('text/') or type.endswith('+json') or type.endswith('/json'))):
|
||||
(type.startswith('text/') or type.endswith('+json')
|
||||
or type.endswith('/json'))):
|
||||
logger.info(resp.text)
|
||||
|
||||
return resp
|
||||
|
@ -546,7 +547,7 @@ def postprocess_as2_actor(actor, wrap=True):
|
|||
url = g.user.web_url() if g.user else None
|
||||
urls = util.get_list(actor, 'url')
|
||||
if not urls and url:
|
||||
urls = [url]
|
||||
urls = [url]
|
||||
|
||||
domain = util.domain_from_link(urls[0], minimize=False)
|
||||
if wrap:
|
||||
|
@ -565,11 +566,11 @@ def postprocess_as2_actor(actor, wrap=True):
|
|||
|
||||
# Override the label for their home page to be "Web site"
|
||||
for att in util.get_list(actor, 'attachment'):
|
||||
if att.get('type') == 'PropertyValue':
|
||||
val = att.get('value', '')
|
||||
link = util.parse_html(val).find('a')
|
||||
if url and (val == url or link.get('href') == url):
|
||||
att['name'] = 'Web site'
|
||||
if att.get('type') == 'PropertyValue':
|
||||
val = att.get('value', '')
|
||||
link = util.parse_html(val).find('a')
|
||||
if url and (val == url or link.get('href') == url):
|
||||
att['name'] = 'Web site'
|
||||
|
||||
# required by pixelfed. https://github.com/snarfed/bridgy-fed/issues/39
|
||||
actor.setdefault('summary', '')
|
||||
|
@ -591,7 +592,7 @@ def actor(protocol, domain):
|
|||
if not g.user:
|
||||
try:
|
||||
obj = cls.load(f'https://{domain}/', gateway=True)
|
||||
except NoMicroformats as e:
|
||||
except NoMicroformats:
|
||||
obj = None
|
||||
g.user = cls.get_or_create(id=domain, obj=obj)
|
||||
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
"""Bridgy App Engine config.
|
||||
"""
|
||||
# suppress these INFO logs:
|
||||
# Sandbox prevented access to file "/usr/local/Caskroom/google-cloud-sdk"
|
||||
# If it is a static file, check that `application_readable: true` is set in your app.yaml
|
||||
import logging
|
||||
|
||||
|
||||
class StubsFilter(logging.Filter):
|
||||
def filter(self, record):
|
||||
msg = record.getMessage()
|
||||
if (msg.startswith('Sandbox prevented access to file') or
|
||||
msg.startswith('If it is a static file, check that')):
|
||||
return 0
|
||||
return 1
|
||||
"""Suppress these INFO logs:
|
||||
Sandbox prevented access to file "/usr/local/Caskroom/google-cloud-sdk"
|
||||
If it is a static file, check that `application_readable: true` is set in your app.yaml
|
||||
"""
|
||||
def filter(self, record):
|
||||
msg = record.getMessage()
|
||||
if (msg.startswith('Sandbox prevented access to file')
|
||||
or msg.startswith('If it is a static file, check that')):
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
logging.getLogger().addFilter(StubsFilter())
|
||||
|
|
72
common.py
72
common.py
|
@ -2,7 +2,6 @@
|
|||
"""Misc common utilities.
|
||||
"""
|
||||
import base64
|
||||
import copy
|
||||
from datetime import timedelta
|
||||
import logging
|
||||
import re
|
||||
|
@ -12,11 +11,8 @@ import urllib.parse
|
|||
import cachetools
|
||||
from Crypto.Util import number
|
||||
from flask import abort, g, make_response, request
|
||||
from granary import as1, as2, microformats2
|
||||
import mf2util
|
||||
from oauth_dropins.webutil import util, webmention
|
||||
from oauth_dropins.webutil.appengine_info import DEBUG
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
from werkzeug.exceptions import BadRequest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
@ -90,10 +86,10 @@ def long_to_base64(x):
|
|||
|
||||
def host_url(path_query=None):
|
||||
base = request.host_url
|
||||
if (util.domain_or_parent_in(request.host, OTHER_DOMAINS) or
|
||||
# when running locally against prod datastore
|
||||
(not DEBUG and request.host in LOCAL_DOMAINS)):
|
||||
base = f'https://{PRIMARY_DOMAIN}'
|
||||
if (util.domain_or_parent_in(request.host, OTHER_DOMAINS)
|
||||
# when running locally against prod datastore
|
||||
or (not DEBUG and request.host in LOCAL_DOMAINS)):
|
||||
base = f'https://{PRIMARY_DOMAIN}'
|
||||
|
||||
return urllib.parse.urljoin(base, path_query)
|
||||
|
||||
|
@ -105,26 +101,26 @@ def error(msg, status=400, exc_info=None, **kwargs):
|
|||
|
||||
|
||||
def pretty_link(url, text=None, **kwargs):
|
||||
"""Wrapper around util.pretty_link() that converts Mastodon user URLs to @-@.
|
||||
"""Wrapper around util.pretty_link() that converts Mastodon user URLs to @-@.
|
||||
|
||||
Eg for URLs like https://mastodon.social/@foo and
|
||||
https://mastodon.social/users/foo, defaults text to @foo@mastodon.social if
|
||||
it's not provided.
|
||||
Eg for URLs like https://mastodon.social/@foo and
|
||||
https://mastodon.social/users/foo, defaults text to @foo@mastodon.social if
|
||||
it's not provided.
|
||||
|
||||
Args:
|
||||
url: str
|
||||
text: str
|
||||
kwargs: passed through to :func:`webutil.util.pretty_link`
|
||||
"""
|
||||
if g.user and g.user.is_web_url(url):
|
||||
return g.user.user_page_link()
|
||||
Args:
|
||||
url: str
|
||||
text: str
|
||||
kwargs: passed through to :func:`webutil.util.pretty_link`
|
||||
"""
|
||||
if g.user and g.user.is_web_url(url):
|
||||
return g.user.user_page_link()
|
||||
|
||||
if text is None:
|
||||
match = re.match(r'https?://([^/]+)/(@|users/)([^/]+)$', url)
|
||||
if match:
|
||||
text = match.expand(r'@\3@\1')
|
||||
if text is None:
|
||||
match = re.match(r'https?://([^/]+)/(@|users/)([^/]+)$', url)
|
||||
if match:
|
||||
text = match.expand(r'@\3@\1')
|
||||
|
||||
return util.pretty_link(url, text=text, **kwargs)
|
||||
return util.pretty_link(url, text=text, **kwargs)
|
||||
|
||||
|
||||
def content_type(resp):
|
||||
|
@ -199,25 +195,25 @@ def redirect_unwrap(val):
|
|||
|
||||
|
||||
def webmention_endpoint_cache_key(url):
|
||||
"""Returns cache key for a cached webmention endpoint for a given URL.
|
||||
"""Returns cache key for a cached webmention endpoint for a given URL.
|
||||
|
||||
Just the domain by default. If the URL is the home page, ie path is / , the
|
||||
key includes a / at the end, so that we cache webmention endpoints for home
|
||||
pages separate from other pages. https://github.com/snarfed/bridgy/issues/701
|
||||
Just the domain by default. If the URL is the home page, ie path is / , the
|
||||
key includes a / at the end, so that we cache webmention endpoints for home
|
||||
pages separate from other pages. https://github.com/snarfed/bridgy/issues/701
|
||||
|
||||
Example: 'snarfed.org /'
|
||||
Example: 'snarfed.org /'
|
||||
|
||||
https://github.com/snarfed/bridgy-fed/issues/423
|
||||
https://github.com/snarfed/bridgy-fed/issues/423
|
||||
|
||||
Adapted from bridgy/util.py.
|
||||
"""
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
key = parsed.netloc
|
||||
if parsed.path in ('', '/'):
|
||||
key += ' /'
|
||||
Adapted from bridgy/util.py.
|
||||
"""
|
||||
parsed = urllib.parse.urlparse(url)
|
||||
key = parsed.netloc
|
||||
if parsed.path in ('', '/'):
|
||||
key += ' /'
|
||||
|
||||
# logger.debug(f'wm cache key {key}')
|
||||
return key
|
||||
# logger.debug(f'wm cache key {key}')
|
||||
return key
|
||||
|
||||
|
||||
@cachetools.cached(cachetools.TTLCache(50000, 60 * 60 * 2), # 2h expiration
|
||||
|
|
12
config.py
12
config.py
|
@ -14,10 +14,10 @@ SESSION_COOKIE_SAMESITE = 'Lax'
|
|||
CACHE_THRESHOLD = 3000
|
||||
|
||||
if appengine_info.DEBUG:
|
||||
ENV = 'development'
|
||||
CACHE_TYPE = 'NullCache'
|
||||
SECRET_KEY = 'sooper seekret'
|
||||
ENV = 'development'
|
||||
CACHE_TYPE = 'NullCache'
|
||||
SECRET_KEY = 'sooper seekret'
|
||||
else:
|
||||
ENV = 'production'
|
||||
CACHE_TYPE = 'SimpleCache'
|
||||
SECRET_KEY = util.read('flask_secret_key')
|
||||
ENV = 'production'
|
||||
CACHE_TYPE = 'SimpleCache'
|
||||
SECRET_KEY = util.read('flask_secret_key')
|
||||
|
|
|
@ -5,7 +5,6 @@ constants from the :class:`Protocol` subclasses.
|
|||
"""
|
||||
import logging
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
from flask import g, redirect, request
|
||||
from granary import as1
|
||||
|
@ -78,8 +77,8 @@ def convert(dest, _):
|
|||
if obj_id:
|
||||
# TODO: PROTOCOLS[src].load() this instead?
|
||||
obj_obj = Object.get_by_id(obj_id)
|
||||
if (obj_obj and obj_obj.as1 and
|
||||
not obj_obj.as1.keys() <= set(['id', 'url', 'objectType'])):
|
||||
if (obj_obj and obj_obj.as1
|
||||
and not obj_obj.as1.keys() <= set(['id', 'url', 'objectType'])):
|
||||
logger.info(f'{type} activity, redirecting to Object {obj_id}')
|
||||
return redirect(f'/{path_prefix}{obj_id}', code=301)
|
||||
|
||||
|
|
|
@ -15,8 +15,6 @@ from oauth_dropins.webutil import (
|
|||
util,
|
||||
)
|
||||
|
||||
import common
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.getLogger('lexrpc').setLevel(logging.INFO)
|
||||
logging.getLogger('negotiator').setLevel(logging.WARNING)
|
||||
|
@ -34,6 +32,7 @@ app.register_error_handler(Exception, flask_util.handle_exception)
|
|||
if appengine_info.LOCAL:
|
||||
flask_gae_static.init_app(app)
|
||||
|
||||
|
||||
@app.before_request
|
||||
def init_globals():
|
||||
"""Set request globals.
|
||||
|
@ -42,6 +41,7 @@ def init_globals():
|
|||
"""
|
||||
g.user = None
|
||||
|
||||
|
||||
# don't redirect API requests with blank path elements
|
||||
app.url_map.merge_slashes = False
|
||||
app.url_map.redirect_defaults = False
|
||||
|
|
|
@ -5,16 +5,13 @@ https://socialhub.activitypub.rocks/t/what-is-the-current-spec-for-remote-follow
|
|||
https://www.rfc-editor.org/rfc/rfc7033
|
||||
"""
|
||||
import logging
|
||||
import urllib.parse
|
||||
|
||||
from flask import g, redirect, request, session
|
||||
from granary import as2
|
||||
from oauth_dropins import indieauth
|
||||
from oauth_dropins.webutil import flask_util, util
|
||||
from oauth_dropins.webutil.flask_util import error, flash
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.flask_util import error, flash
|
||||
from oauth_dropins.webutil.testutil import NOW
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
|
||||
from activitypub import ActivityPub
|
||||
from flask_app import app
|
||||
|
|
|
@ -7,7 +7,6 @@ import random
|
|||
import urllib.parse
|
||||
|
||||
from arroba.mst import dag_cbor_cid
|
||||
from Crypto import Random
|
||||
from Crypto.PublicKey import ECC, RSA
|
||||
import dag_json
|
||||
from flask import g, request
|
||||
|
@ -18,10 +17,9 @@ from oauth_dropins.webutil.appengine_info import DEBUG
|
|||
from oauth_dropins.webutil.flask_util import error
|
||||
from oauth_dropins.webutil.models import ComputedJsonProperty, JsonProperty, StringIdModel
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
import requests
|
||||
|
||||
import common
|
||||
from common import base64_to_long, long_to_base64, redirect_unwrap, redirect_wrap
|
||||
from common import base64_to_long, long_to_base64, redirect_unwrap
|
||||
|
||||
# maps string label to Protocol subclass. populated by ProtocolUserMeta.
|
||||
# seed with old and upcoming protocols that don't have their own classes (yet).
|
||||
|
@ -475,8 +473,8 @@ class Object(StringIdModel):
|
|||
"""Returns a pretty actor link with their name and profile picture."""
|
||||
attrs = {'class': 'h-card u-author'}
|
||||
|
||||
if (self.source_protocol in ('web', 'webmention', 'ui') and g.user and
|
||||
(g.user.key in self.users or g.user.key.id() in self.domains)):
|
||||
if (self.source_protocol in ('web', 'webmention', 'ui') and g.user
|
||||
and (g.user.key in self.users or g.user.key.id() in self.domains)):
|
||||
# outbound; show a nice link to the user
|
||||
return g.user.user_page_link()
|
||||
|
||||
|
@ -498,6 +496,7 @@ class Object(StringIdModel):
|
|||
{util.ellipsize(name, chars=40)}
|
||||
</a>"""
|
||||
|
||||
|
||||
class AtpNode(StringIdModel):
|
||||
"""An AT Protocol (Bluesky) node.
|
||||
|
||||
|
|
13
pages.py
13
pages.py
|
@ -1,26 +1,20 @@
|
|||
"""UI pages."""
|
||||
import calendar
|
||||
import datetime
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
from flask import g, redirect, render_template, request
|
||||
from google.cloud.ndb.model import get_multi
|
||||
from flask import g, render_template, request
|
||||
from google.cloud.ndb.query import OR
|
||||
from google.cloud.ndb.stats import KindStat
|
||||
from granary import as1, as2, atom, microformats2, rss
|
||||
import humanize
|
||||
from oauth_dropins.webutil import flask_util, logs, util
|
||||
from oauth_dropins.webutil.flask_util import error, flash, redirect
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
from oauth_dropins.webutil.flask_util import error, redirect
|
||||
|
||||
import common
|
||||
from common import DOMAIN_RE
|
||||
from flask_app import app, cache
|
||||
from models import fetch_page, Follower, Object, PAGE_SIZE, PROTOCOLS, User
|
||||
from web import Web
|
||||
from models import fetch_page, Follower, Object, PAGE_SIZE, PROTOCOLS
|
||||
|
||||
FOLLOWERS_UI_LIMIT = 999
|
||||
|
||||
|
@ -192,7 +186,6 @@ def fetch_objects(query):
|
|||
to fetch the previous and next pages, respectively
|
||||
"""
|
||||
objects, new_before, new_after = fetch_page(query, Object)
|
||||
seen = set()
|
||||
|
||||
# synthesize human-friendly content for objects
|
||||
for i, obj in enumerate(objects):
|
||||
|
|
21
protocol.py
21
protocol.py
|
@ -3,18 +3,17 @@ import logging
|
|||
import threading
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from cachetools import cached, LRUCache
|
||||
from cachetools import LRUCache
|
||||
from flask import g, request
|
||||
from google.cloud import ndb
|
||||
from google.cloud.ndb import OR
|
||||
from granary import as1, as2
|
||||
import requests
|
||||
from granary import as1
|
||||
import werkzeug.exceptions
|
||||
|
||||
import common
|
||||
from common import error
|
||||
from models import Follower, Object, PROTOCOLS, Target
|
||||
from oauth_dropins.webutil import util, webmention
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
|
||||
SUPPORTED_TYPES = (
|
||||
|
@ -433,8 +432,8 @@ class Protocol:
|
|||
# deliver original posts and reposts to followers
|
||||
is_reply = (obj.type == 'comment' or
|
||||
(inner_obj and inner_obj.get('inReplyTo')))
|
||||
if (actor and actor_id and
|
||||
(obj.type == 'share' or obj.type in ('create', 'post') and not is_reply)):
|
||||
if ((obj.type == 'share' or obj.type in ('create', 'post') and not is_reply)
|
||||
and actor and actor_id):
|
||||
logger.info(f'Delivering to followers of {actor_id}')
|
||||
for f in Follower.query(Follower.to == from_cls(id=actor_id).key,
|
||||
Follower.status == 'active'):
|
||||
|
@ -580,11 +579,11 @@ class Protocol:
|
|||
if 'notification' not in obj.labels:
|
||||
obj.labels.append('notification')
|
||||
except BaseException as e:
|
||||
code, body = util.interpret_http_exception(e)
|
||||
if not code and not body:
|
||||
raise
|
||||
errors.append((code, body))
|
||||
obj.failed.append(target)
|
||||
code, body = util.interpret_http_exception(e)
|
||||
if not code and not body:
|
||||
raise
|
||||
errors.append((code, body))
|
||||
obj.failed.append(target)
|
||||
|
||||
obj.put()
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ import logging
|
|||
import re
|
||||
import urllib.parse
|
||||
|
||||
from flask import g, redirect, request
|
||||
from flask import g, request
|
||||
from granary import as2
|
||||
from negotiator import ContentNegotiator, AcceptParameters, ContentType
|
||||
from oauth_dropins.webutil import flask_util, util
|
||||
|
@ -28,7 +28,6 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
from activitypub import ActivityPub
|
||||
from flask_app import app, cache
|
||||
from common import CACHE_TIME, CONTENT_TYPE_HTML
|
||||
from models import Object, User
|
||||
from web import Web
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -6,14 +6,12 @@ from datetime import datetime, timedelta
|
|||
from hashlib import sha256
|
||||
import logging
|
||||
from unittest import skip
|
||||
from unittest.mock import ANY, call, patch
|
||||
import urllib.parse
|
||||
from unittest.mock import patch
|
||||
|
||||
from flask import g
|
||||
from google.cloud import ndb
|
||||
from granary import as2, microformats2
|
||||
from httpsig import HeaderSigner
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
import requests
|
||||
|
@ -26,10 +24,8 @@ from .testutil import Fake, TestCase
|
|||
import activitypub
|
||||
from activitypub import ActivityPub, postprocess_as2
|
||||
import common
|
||||
import models
|
||||
from models import Follower, Object
|
||||
import protocol
|
||||
from protocol import Protocol
|
||||
from web import Web
|
||||
|
||||
# have to import module, not attrs, to avoid circular import
|
||||
|
@ -49,7 +45,7 @@ ACTOR_BASE = {
|
|||
'https://www.w3.org/ns/activitystreams',
|
||||
'https://w3id.org/security/v1',
|
||||
],
|
||||
'type' : 'Person',
|
||||
'type': 'Person',
|
||||
'id': 'http://localhost/user.com',
|
||||
'url': 'http://localhost/r/https://user.com/',
|
||||
'preferredUsername': 'user.com',
|
||||
|
@ -387,7 +383,7 @@ class ActivityPubTest(TestCase):
|
|||
**REPLY,
|
||||
'actor': LIKE_ACTOR,
|
||||
}
|
||||
got = self._test_inbox_reply(reply, {
|
||||
self._test_inbox_reply(reply, {
|
||||
'as2': reply,
|
||||
'type': 'post',
|
||||
'labels': ['activity', 'notification'],
|
||||
|
@ -749,7 +745,6 @@ class ActivityPubTest(TestCase):
|
|||
self.assert_user(ActivityPub, 'https://user.com/actor',
|
||||
obj_as2=LIKE_ACTOR, direct=True)
|
||||
|
||||
|
||||
def test_inbox_follow_accept_with_id(self, *mocks):
|
||||
self._test_inbox_follow_accept(FOLLOW_WRAPPED, ACCEPT, *mocks)
|
||||
|
||||
|
@ -768,10 +763,6 @@ class ActivityPubTest(TestCase):
|
|||
object_ids=[FOLLOW['object']])
|
||||
|
||||
def test_inbox_follow_accept_with_object(self, *mocks):
|
||||
wrapped_user = {
|
||||
'id': FOLLOW_WRAPPED['object'],
|
||||
'url': FOLLOW_WRAPPED['object'],
|
||||
}
|
||||
unwrapped_user = {
|
||||
'id': FOLLOW['object'],
|
||||
'url': FOLLOW['object'],
|
||||
|
@ -905,7 +896,6 @@ class ActivityPubTest(TestCase):
|
|||
self.assertEqual('https://mas.to/users/swentel#followed-https://user.com/',
|
||||
follower.follow.get().as2['url'])
|
||||
|
||||
|
||||
def test_inbox_undo_follow(self, mock_head, mock_get, mock_post):
|
||||
follower = Follower(to=self.user.key,
|
||||
from_=ActivityPub.get_or_create(ACTOR['id']).key,
|
||||
|
@ -1065,7 +1055,6 @@ class ActivityPubTest(TestCase):
|
|||
# invalid signature, header changed
|
||||
protocol.seen_ids.clear()
|
||||
obj_key.delete()
|
||||
orig_date = headers['Date']
|
||||
|
||||
resp = self.client.post('/ap/sharedInbox', data=body, headers={**headers, 'Date': 'X'})
|
||||
self.assertEqual(401, resp.status_code)
|
||||
|
@ -1505,7 +1494,7 @@ class ActivityPubUtilsTest(TestCase):
|
|||
}, postprocess_as2({
|
||||
'tag': [
|
||||
{'name': 'bar', 'href': 'bar'},
|
||||
{'type': 'Tag','name': '#baz'},
|
||||
{'type': 'Tag', 'name': '#baz'},
|
||||
# should leave alone
|
||||
{'type': 'Mention', 'href': 'foo'},
|
||||
],
|
||||
|
@ -1644,7 +1633,7 @@ class ActivityPubUtilsTest(TestCase):
|
|||
allow_redirects=False),
|
||||
requests_response(status=200, allow_redirects=False),
|
||||
]
|
||||
resp = activitypub.signed_get('https://first')
|
||||
activitypub.signed_get('https://first')
|
||||
|
||||
first = mock_get.call_args_list[0][1]
|
||||
second = mock_get.call_args_list[1][1]
|
||||
|
@ -1697,7 +1686,7 @@ class ActivityPubUtilsTest(TestCase):
|
|||
|
||||
mock_get.assert_has_calls((
|
||||
self.as2_req('http://orig'),
|
||||
self.as2_req('http://as2', headers=common.as2.CONNEG_HEADERS),
|
||||
self.as2_req('http://as2', headers=as2.CONNEG_HEADERS),
|
||||
))
|
||||
|
||||
@patch('requests.get')
|
||||
|
@ -1708,7 +1697,7 @@ class ActivityPubUtilsTest(TestCase):
|
|||
|
||||
@patch('requests.get')
|
||||
def test_fetch_not_acceptable(self, mock_get):
|
||||
mock_get.return_value=NOT_ACCEPTABLE
|
||||
mock_get.return_value = NOT_ACCEPTABLE
|
||||
with self.assertRaises(BadGateway):
|
||||
ActivityPub.fetch(Object(id='http://orig'))
|
||||
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
"""Unit tests for common.py."""
|
||||
from unittest import mock
|
||||
|
||||
from flask import g
|
||||
from granary import as2
|
||||
from oauth_dropins.webutil import appengine_config, util
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
import requests
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from .testutil import Fake, TestCase
|
||||
|
||||
import common
|
||||
from flask_app import app
|
||||
from models import Object, User
|
||||
import protocol
|
||||
from web import Web
|
||||
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ from granary.tests.test_as1 import ACTOR, COMMENT, DELETE_OF_ID, UPDATE
|
|||
from models import Object
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
from oauth_dropins.webutil.util import parse_mf2
|
||||
import requests
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from . import testutil
|
||||
|
@ -191,7 +190,7 @@ class ConvertTest(testutil.TestCase):
|
|||
resp.headers['Location'])
|
||||
|
||||
def test_activitypub_to_web_update_no_inner_obj_serve_as_is(self):
|
||||
# UPDATE's object field is a full object
|
||||
# Update's object field is a full object
|
||||
Object(id='http://foo', our_as1=UPDATE).put()
|
||||
|
||||
resp = self.client.get('/convert/web/http://foo',
|
||||
|
@ -205,7 +204,7 @@ A ☕ reply
|
|||
""", resp.get_data(as_text=True), ignore_blanks=True)
|
||||
|
||||
def test_activitypub_to_web_update_inner_obj_too_minimal_serve_as_is(self):
|
||||
# UPDATE's object field is a full object
|
||||
# Update's object field is a full object
|
||||
Object(id='http://foo', our_as1=UPDATE).put()
|
||||
Object(id=UPDATE['object']['id'], as2={'id': 'foo'}).put()
|
||||
|
||||
|
@ -259,4 +258,3 @@ A ☕ reply
|
|||
resp = self.client.get(f'/convert/ap/http://nope.com/post',
|
||||
base_url='https://ap.brid.gy/')
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
|
|
|
@ -14,10 +14,7 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
from .testutil import Fake, TestCase
|
||||
|
||||
from activitypub import ActivityPub
|
||||
import common
|
||||
from common import redirect_unwrap
|
||||
from models import Follower, Object, User
|
||||
from web import Web
|
||||
from models import Follower, Object
|
||||
|
||||
WEBFINGER = requests_response({
|
||||
'subject': 'acct:foo@bar',
|
||||
|
@ -191,7 +188,7 @@ class FollowTest(TestCase):
|
|||
|
||||
def check(self, input, resp, expected_follow, mock_get, mock_post):
|
||||
self.assertEqual(302, resp.status_code)
|
||||
self.assertEqual('/web/alice.com/following',resp.headers['Location'])
|
||||
self.assertEqual('/web/alice.com/following', resp.headers['Location'])
|
||||
self.assertEqual([f'Followed <a href="https://bar/url">{input}</a>.'],
|
||||
get_flashed_messages())
|
||||
|
||||
|
|
|
@ -1,20 +1,15 @@
|
|||
# coding=utf-8
|
||||
"""Unit tests for models.py."""
|
||||
from unittest import mock
|
||||
|
||||
from arroba.mst import dag_cbor_cid
|
||||
from Crypto.PublicKey import ECC
|
||||
from flask import g, get_flashed_messages
|
||||
from granary import as2
|
||||
from flask import g
|
||||
from granary.tests.test_bluesky import ACTOR_PROFILE_BSKY
|
||||
from multiformats import CID
|
||||
from oauth_dropins.webutil.testutil import NOW, requests_response
|
||||
from oauth_dropins.webutil.testutil import NOW
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from .testutil import Fake, TestCase
|
||||
|
||||
import common
|
||||
from models import AtpNode, Follower, Object, OBJECT_EXPIRE_AGE, User
|
||||
from models import AtpNode, Follower, Object, OBJECT_EXPIRE_AGE
|
||||
import protocol
|
||||
from web import Web
|
||||
|
||||
|
@ -158,9 +153,9 @@ class ObjectTest(TestCase):
|
|||
title="Alice">
|
||||
<img class="profile" src="http://pic/" />
|
||||
Alice""", {'actor': {
|
||||
'name': 'Alice',
|
||||
'icon': {'type': 'Image', 'url': 'http://pic'},
|
||||
}}),
|
||||
'name': 'Alice',
|
||||
'icon': {'type': 'Image', 'url': 'http://pic'},
|
||||
}}),
|
||||
):
|
||||
obj = Object(id='x', as2=as2)
|
||||
self.assert_multiline_in(expected, obj.actor_link())
|
||||
|
|
|
@ -1,32 +1,26 @@
|
|||
"""Unit tests for pages.py."""
|
||||
from granary import as2, atom, microformats2, rss
|
||||
from granary.tests.test_bluesky import REPLY_BSKY
|
||||
from granary import atom, microformats2, rss
|
||||
from granary.tests.test_as1 import (
|
||||
ACTOR,
|
||||
COMMENT,
|
||||
FOLLOW_WITH_ACTOR,
|
||||
FOLLOW_WITH_OBJECT,
|
||||
LIKE,
|
||||
NOTE,
|
||||
)
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from .testutil import Fake, TestCase
|
||||
|
||||
from activitypub import ActivityPub
|
||||
import common
|
||||
from models import Object, Follower, User
|
||||
from web import Web
|
||||
from models import Object, Follower
|
||||
|
||||
from .test_web import ACTOR_AS2, ACTOR_HTML, ACTOR_MF2, REPOST_AS2
|
||||
from .test_web import ACTOR_AS2, REPOST_AS2
|
||||
|
||||
ACTOR_WITH_PREFERRED_USERNAME = {
|
||||
**ACTOR,
|
||||
'preferredUsername': 'me',
|
||||
}
|
||||
|
||||
|
||||
def contents(activities):
|
||||
return [(a.get('object') or a)['content'] for a in activities]
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ from app import app
|
|||
from models import Follower, Object, PROTOCOLS, User
|
||||
import protocol
|
||||
from protocol import Protocol
|
||||
import requests
|
||||
from ui import UIProtocol
|
||||
from web import Web
|
||||
|
||||
|
|
|
@ -5,20 +5,17 @@ from unittest.mock import patch
|
|||
|
||||
from granary import as2
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
import requests
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from . import testutil
|
||||
|
||||
from common import redirect_unwrap
|
||||
from flask_app import app, cache
|
||||
from models import Object, User
|
||||
from models import Object
|
||||
from web import Web
|
||||
|
||||
from .test_activitypub import ACTOR_BASE_FULL
|
||||
from .test_web import (
|
||||
ACTOR_AS2,
|
||||
ACTOR_AS2_FULL,
|
||||
ACTOR_HTML,
|
||||
REPOST_AS2,
|
||||
REPOST_HTML,
|
||||
|
@ -146,7 +143,7 @@ class RedirectTest(testutil.TestCase):
|
|||
self._test_as2(as2.CONTENT_TYPE)
|
||||
|
||||
resp = self.client.get('/r/https://user.com/bar',
|
||||
headers={'Accept': 'text/html'})
|
||||
headers={'Accept': 'text/html'})
|
||||
self.assertEqual(301, resp.status_code)
|
||||
self.assertEqual('https://user.com/bar', resp.headers['Location'])
|
||||
|
||||
|
@ -162,5 +159,5 @@ class RedirectTest(testutil.TestCase):
|
|||
Object(id='https://user.com/bar', as2={}, deleted=True).put()
|
||||
|
||||
resp = self.client.get('/r/https://user.com/bar',
|
||||
headers={'Accept': as2.CONTENT_TYPE})
|
||||
headers={'Accept': as2.CONTENT_TYPE})
|
||||
self.assertEqual(404, resp.status_code, resp.get_data(as_text=True))
|
||||
|
|
|
@ -4,28 +4,21 @@ import copy
|
|||
from unittest.mock import patch
|
||||
from urllib.parse import urlencode
|
||||
|
||||
import feedparser
|
||||
from flask import g, get_flashed_messages
|
||||
from granary import as1, as2, atom, microformats2
|
||||
from httpsig.sign import HeaderSigner
|
||||
from oauth_dropins.webutil import appengine_config, util
|
||||
from oauth_dropins.webutil.appengine_config import tasks_client
|
||||
from granary import as1, as2, microformats2
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.appengine_info import APP_ID
|
||||
from oauth_dropins.webutil.testutil import NOW, requests_response
|
||||
from oauth_dropins.webutil.util import json_dumps, json_loads
|
||||
import requests
|
||||
from requests import HTTPError
|
||||
from werkzeug.exceptions import BadGateway, BadRequest
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from . import testutil
|
||||
|
||||
from activitypub import ActivityPub, postprocess_as2
|
||||
from common import (
|
||||
CONTENT_TYPE_HTML,
|
||||
redirect_unwrap,
|
||||
)
|
||||
from models import Follower, Object, Target, User
|
||||
from common import CONTENT_TYPE_HTML
|
||||
from models import Follower, Object
|
||||
from web import TASKS_LOCATION, Web
|
||||
from . import test_activitypub
|
||||
from .testutil import TestCase
|
||||
|
@ -210,7 +203,7 @@ REPLY_HTML = """\
|
|||
</html>
|
||||
"""
|
||||
REPLY = requests_response(REPLY_HTML, content_type=CONTENT_TYPE_HTML,
|
||||
url='https://user.com/reply')
|
||||
url='https://user.com/reply')
|
||||
REPLY_MF2 = util.parse_mf2(REPLY_HTML)['items'][0]
|
||||
REPLY_AS1 = microformats2.json_to_object(REPLY_MF2)
|
||||
CREATE_REPLY_AS1 = {
|
||||
|
@ -238,7 +231,7 @@ LIKE = requests_response(LIKE_HTML, content_type=CONTENT_TYPE_HTML,
|
|||
LIKE_MF2 = util.parse_mf2(LIKE_HTML)['items'][0]
|
||||
|
||||
ACTOR = TestCase.as2_resp({
|
||||
'type' : 'Person',
|
||||
'type': 'Person',
|
||||
'name': 'Mrs. ☕ Foo',
|
||||
'id': 'https://mas.to/mrs-foo',
|
||||
'inbox': 'https://mas.to/inbox',
|
||||
|
@ -583,9 +576,6 @@ class WebTest(TestCase):
|
|||
self.assertEqual(502, got.status_code)
|
||||
|
||||
def test_target_fetch_has_no_content_type(self, mock_get, mock_post):
|
||||
html = REPLY_HTML.replace(
|
||||
'</body>',
|
||||
"<link href='http://as2' rel='alternate' type='application/activity+json'></body")
|
||||
mock_get.side_effect = (
|
||||
requests_response(REPLY_HTML, url='https://user.com/reply'),
|
||||
requests_response(REPLY_HTML, url='https://user.com/reply',
|
||||
|
@ -612,7 +602,7 @@ class WebTest(TestCase):
|
|||
def test_backlink_without_trailing_slash(self, mock_get, mock_post):
|
||||
mock_get.return_value = requests_response(
|
||||
REPLY_HTML.replace('<a href="http://localhost/"></a>',
|
||||
'<a href="http://localhost"></a>'),
|
||||
'<a href="http://localhost"></a>'),
|
||||
content_type=CONTENT_TYPE_HTML, url='https://user.com/reply')
|
||||
|
||||
got = self.client.post('/_ah/queue/webmention', data={
|
||||
|
@ -1148,17 +1138,17 @@ class WebTest(TestCase):
|
|||
self.assert_deliveries(mock_post, ['https://mas.to/inbox'],
|
||||
FOLLOW_FRAGMENT_AS2)
|
||||
|
||||
obj = self.assert_object('https://user.com/follow#2',
|
||||
users=[g.user.key],
|
||||
source_protocol='web',
|
||||
status='complete',
|
||||
mf2=FOLLOW_FRAGMENT_MF2,
|
||||
as1=FOLLOW_FRAGMENT_AS1,
|
||||
delivered=['https://mas.to/inbox'],
|
||||
type='follow',
|
||||
object_ids=['https://mas.to/mrs-foo'],
|
||||
labels=['user', 'activity'],
|
||||
)
|
||||
self.assert_object('https://user.com/follow#2',
|
||||
users=[g.user.key],
|
||||
source_protocol='web',
|
||||
status='complete',
|
||||
mf2=FOLLOW_FRAGMENT_MF2,
|
||||
as1=FOLLOW_FRAGMENT_AS1,
|
||||
delivered=['https://mas.to/inbox'],
|
||||
type='follow',
|
||||
object_ids=['https://mas.to/mrs-foo'],
|
||||
labels=['user', 'activity'],
|
||||
)
|
||||
|
||||
followers = Follower.query().fetch()
|
||||
self.assert_equals(1, len(followers))
|
||||
|
@ -1178,7 +1168,7 @@ class WebTest(TestCase):
|
|||
content_type=CONTENT_TYPE_HTML),
|
||||
ACTOR,
|
||||
self.as2_resp({
|
||||
'objectType' : 'Person',
|
||||
'objectType': 'Person',
|
||||
'displayName': 'Mr. ☕ Biff',
|
||||
'id': 'https://mas.to/mr-biff',
|
||||
'inbox': 'https://mas.to/inbox/biff',
|
||||
|
@ -1277,7 +1267,7 @@ class WebTest(TestCase):
|
|||
type='delete',
|
||||
object_ids=['https://user.com/post'],
|
||||
labels=['user', 'activity'],
|
||||
)
|
||||
)
|
||||
|
||||
def test_delete_no_object(self, mock_get, mock_post):
|
||||
mock_get.side_effect = [
|
||||
|
@ -1336,7 +1326,7 @@ class WebTest(TestCase):
|
|||
type='follow',
|
||||
object_ids=['https://mas.to/mrs-foo'],
|
||||
labels=['user', 'activity'],
|
||||
)
|
||||
)
|
||||
|
||||
def test_repost_blocklisted_error(self, mock_get, mock_post):
|
||||
"""Reposts of non-fediverse (ie blocklisted) sites aren't yet supported."""
|
||||
|
@ -1723,7 +1713,7 @@ class WebProtocolTest(TestCase):
|
|||
self.assert_equals({**REPOST_MF2, 'url': 'https://user.com/repost'}, obj.mf2)
|
||||
|
||||
def test_fetch_redirect(self, mock_get, __):
|
||||
mock_get.return_value =requests_response(
|
||||
mock_get.return_value = requests_response(
|
||||
REPOST_HTML, content_type=CONTENT_TYPE_HTML,
|
||||
redirected_url='http://new/url')
|
||||
obj = Object(id='https://orig/url')
|
||||
|
@ -1735,7 +1725,7 @@ class WebProtocolTest(TestCase):
|
|||
|
||||
def test_fetch_error(self, mock_get, __):
|
||||
mock_get.return_value = requests_response(REPOST_HTML, status=405)
|
||||
with self.assertRaises(BadGateway) as e:
|
||||
with self.assertRaises(BadGateway):
|
||||
Web.fetch(Object(id='https://foo'), gateway=True)
|
||||
|
||||
def test_fetch_run_authorship(self, mock_get, __):
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# coding=utf-8
|
||||
"""Unit tests for webfinger.py."""
|
||||
import copy
|
||||
import html
|
||||
from unittest.mock import patch
|
||||
import urllib.parse
|
||||
|
||||
|
@ -10,7 +9,6 @@ from oauth_dropins.webutil.testutil import requests_response
|
|||
# import first so that Fake is defined before URL routes are registered
|
||||
from .testutil import Fake, TestCase
|
||||
|
||||
import common
|
||||
from web import Web
|
||||
|
||||
from .test_web import ACTOR_HTML
|
||||
|
@ -115,7 +113,7 @@ class HostMetaTest(TestCase):
|
|||
got = self.client.get('/.well-known/host-meta')
|
||||
self.assertEqual(200, got.status_code)
|
||||
self.assertEqual('application/xrd+xml; charset=utf-8',
|
||||
got.headers['Content-Type'])
|
||||
got.headers['Content-Type'])
|
||||
body = got.get_data(as_text=True)
|
||||
self.assertTrue(body.startswith('<?xml'), body)
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
"""Unit tests for actor.py."""
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
import requests
|
||||
from unittest import skip
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from . import testutil
|
||||
|
||||
from models import User
|
||||
from .test_activitypub import ACTOR
|
||||
|
||||
|
||||
|
@ -30,7 +26,6 @@ class XrpcActorTest(testutil.TestCase):
|
|||
'handle': 'mas.to/users/swentel',
|
||||
'did': 'did:web:mas.to:users:swentel',
|
||||
'displayName': 'Mrs. ☕ Foo',
|
||||
'description': None,
|
||||
'description': "I'm a person",
|
||||
'avatar': 'https://user.com/me.jpg',
|
||||
'banner': 'http://user.com/header.png',
|
||||
|
@ -59,7 +54,7 @@ class XrpcActorTest(testutil.TestCase):
|
|||
|
||||
def test_search(self):
|
||||
resp = self.client.get('/xrpc/app.bsky.actor.searchActors',
|
||||
query_string={'term': 'foo'})
|
||||
query_string={'term': 'foo'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual({
|
||||
'actors': [],
|
||||
|
@ -67,7 +62,7 @@ class XrpcActorTest(testutil.TestCase):
|
|||
|
||||
def test_searchTypeahead(self):
|
||||
resp = self.client.get('/xrpc/app.bsky.actor.searchActorsTypeahead',
|
||||
query_string={'term': 'foo'})
|
||||
query_string={'term': 'foo'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual({
|
||||
'actors': [],
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
"""Unit tests for feed.py."""
|
||||
import copy
|
||||
from unittest import skip
|
||||
|
||||
from granary import as2, bluesky
|
||||
|
@ -7,22 +6,15 @@ from granary.tests.test_as1 import COMMENT, NOTE
|
|||
from granary.tests.test_bluesky import (
|
||||
POST_BSKY,
|
||||
POST_AS,
|
||||
POST_AUTHOR_AS,
|
||||
REPLY_BSKY,
|
||||
REPLY_AS,
|
||||
REPOST_BSKY,
|
||||
REPOST_AS,
|
||||
)
|
||||
from oauth_dropins.webutil import util
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
import requests
|
||||
from werkzeug.exceptions import BadGateway
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from . import testutil
|
||||
|
||||
import common
|
||||
from models import Object, User
|
||||
from models import Object
|
||||
from .test_activitypub import ACTOR
|
||||
|
||||
POST_THREAD_AS = {
|
||||
|
@ -90,7 +82,7 @@ class XrpcFeedTest(testutil.TestCase):
|
|||
Object(id='d', domains=['user.com'], labels=['feed'], as2=post_as2).put()
|
||||
# deleted
|
||||
Object(id='e', domains=['user.com'], labels=['user'], as2=post_as2,
|
||||
deleted=True).put()
|
||||
deleted=True).put()
|
||||
# other user's
|
||||
Object(id='f', domains=['bar.org'], labels=['user'], as2=post_as2).put()
|
||||
|
||||
|
@ -151,7 +143,7 @@ class XrpcFeedTest(testutil.TestCase):
|
|||
})).put()
|
||||
|
||||
got = self.client.get('/xrpc/app.bsky.feed.getRepostedBy',
|
||||
query_string={'uri': 'http://a/post'})
|
||||
query_string={'uri': 'http://a/post'})
|
||||
self.assertEqual({
|
||||
'uri': 'http://orig/post',
|
||||
'repostBy': [{
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
"""Unit tests for graph.py."""
|
||||
from granary import bluesky
|
||||
from oauth_dropins.webutil.testutil import requests_response
|
||||
import requests
|
||||
|
||||
# import first so that Fake is defined before URL routes are registered
|
||||
from . import testutil
|
||||
|
||||
from .test_activitypub import ACTOR, FOLLOW, FOLLOW_WITH_ACTOR, FOLLOW_WITH_OBJECT
|
||||
from models import Follower, User
|
||||
from .test_activitypub import FOLLOW, FOLLOW_WITH_ACTOR, FOLLOW_WITH_OBJECT
|
||||
from models import Follower
|
||||
from unittest import skip
|
||||
|
||||
SUBJECT = {
|
||||
|
@ -42,19 +38,19 @@ class XrpcGraphTest(testutil.TestCase):
|
|||
|
||||
def test_getFollowers_not_domain(self):
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
|
||||
query_string={'user': 'not a domain'})
|
||||
query_string={'user': 'not a domain'})
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
def test_getFollowers_no_user(self):
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
|
||||
query_string={'user': 'no.com'})
|
||||
query_string={'user': 'no.com'})
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
def test_getFollowers_empty(self):
|
||||
self.make_user('user.com')
|
||||
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
|
||||
query_string={'user': 'user.com'})
|
||||
query_string={'user': 'user.com'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual({
|
||||
'subject': SUBJECT,
|
||||
|
@ -83,7 +79,7 @@ class XrpcGraphTest(testutil.TestCase):
|
|||
last_follow=other_follow)
|
||||
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollowers',
|
||||
query_string={'user': 'user.com'})
|
||||
query_string={'user': 'user.com'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual({
|
||||
'subject': SUBJECT,
|
||||
|
@ -93,14 +89,14 @@ class XrpcGraphTest(testutil.TestCase):
|
|||
|
||||
def test_getFollows_not_domain(self):
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollows',
|
||||
query_string={'user': 'not a domain'})
|
||||
query_string={'user': 'not a domain'})
|
||||
self.assertEqual(400, resp.status_code)
|
||||
|
||||
def test_getFollows_empty(self):
|
||||
self.make_user('user.com')
|
||||
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollows',
|
||||
query_string={'user': 'user.com'})
|
||||
query_string={'user': 'user.com'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual({
|
||||
'subject': SUBJECT,
|
||||
|
@ -123,13 +119,13 @@ class XrpcGraphTest(testutil.TestCase):
|
|||
Follower.get_or_create('https://no/stored/follow', 'user.com')
|
||||
Follower.get_or_create('https://masto/user', 'user.com',
|
||||
last_follow=FOLLOW_WITH_OBJECT)
|
||||
Follower.get_or_create( 'http://other', 'user.com',
|
||||
Follower.get_or_create('http://other', 'user.com',
|
||||
last_follow=other_follow)
|
||||
Follower.get_or_create('http://nope', 'nope.com',
|
||||
last_follow=other_follow)
|
||||
|
||||
resp = self.client.get('/xrpc/app.bsky.graph.getFollows',
|
||||
query_string={'user': 'user.com'})
|
||||
query_string={'user': 'user.com'})
|
||||
self.assertEqual(200, resp.status_code)
|
||||
self.assertEqual({
|
||||
'subject': SUBJECT,
|
||||
|
|
4
web.py
4
web.py
|
@ -6,11 +6,9 @@ import re
|
|||
import urllib.parse
|
||||
from urllib.parse import urlencode, urljoin, urlparse
|
||||
|
||||
import feedparser
|
||||
from flask import g, redirect, render_template, request
|
||||
from flask.views import View
|
||||
from google.cloud import ndb
|
||||
from google.cloud.ndb import ComputedProperty, Key
|
||||
from google.cloud.ndb import ComputedProperty
|
||||
from granary import as1, as2, microformats2
|
||||
import mf2util
|
||||
from oauth_dropins.webutil import flask_util, util
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
https://webfinger.net/
|
||||
https://tools.ietf.org/html/rfc7033
|
||||
"""
|
||||
import datetime
|
||||
import logging
|
||||
import re
|
||||
import urllib.parse
|
||||
|
||||
from flask import g, render_template, request
|
||||
|
@ -16,7 +14,6 @@ from oauth_dropins.webutil.util import json_dumps, json_loads
|
|||
|
||||
import common
|
||||
from flask_app import app, cache
|
||||
from models import User
|
||||
from protocol import Protocol
|
||||
from web import Web
|
||||
|
||||
|
@ -56,7 +53,7 @@ class Webfinger(flask_util.XrdOrJrd):
|
|||
cls = Protocol.for_domain(id, fed=Web)
|
||||
if cls:
|
||||
id = user
|
||||
allow_indirect=True
|
||||
allow_indirect = True
|
||||
except ValueError:
|
||||
id = urllib.parse.urlparse(resource).netloc or resource
|
||||
|
||||
|
|
|
@ -4,12 +4,10 @@ import json
|
|||
import re
|
||||
|
||||
from flask import g
|
||||
from granary import microformats2, bluesky
|
||||
import mf2util
|
||||
from granary import bluesky
|
||||
from oauth_dropins.webutil import util
|
||||
|
||||
from flask_app import xrpc_server
|
||||
from models import User
|
||||
from web import Web
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -4,12 +4,11 @@ import logging
|
|||
import re
|
||||
|
||||
from flask import g
|
||||
from granary import bluesky, microformats2
|
||||
import mf2util
|
||||
from granary import bluesky
|
||||
from oauth_dropins.webutil import util
|
||||
|
||||
from flask_app import xrpc_server
|
||||
from models import Object, PAGE_SIZE, User
|
||||
from models import Object, PAGE_SIZE
|
||||
from web import Web
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
|
@ -6,8 +6,7 @@ from granary import bluesky
|
|||
from oauth_dropins.webutil import util
|
||||
|
||||
from flask_app import xrpc_server
|
||||
import common
|
||||
from models import Follower, User
|
||||
from models import Follower
|
||||
from web import Web
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
|
Ładowanie…
Reference in New Issue