more error handling for signup UI flow when user's web site fetch fails

fixes #400
pull/420/head
Ryan Barrett 2023-02-08 20:22:16 -08:00
rodzic a140a60a7f
commit d5eac953d0
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 6BE31FDF4776E9D4
6 zmienionych plików z 124 dodań i 81 usunięć

Wyświetl plik

@ -49,14 +49,16 @@ def check_web_site():
url = request.values['url']
domain = util.domain_from_link(url, minimize=False)
if not domain:
error(f'No domain found in {url}')
flash(f'No domain found in {url}')
return render_template('enter_web_site.html')
user = User.get_or_create(domain)
try:
user = user.verify()
except BaseException as e:
if util.is_connection_failure(e):
flash(f"Couldn't connect to {url}")
code, body = util.interpret_http_exception(e)
if code:
flash(f"Couldn't connect to {url}: {e}")
return render_template('enter_web_site.html')
raise

Wyświetl plik

@ -398,13 +398,12 @@ class ActivityPubTest(testutil.TestCase):
mock_get.return_value = self.as2_resp(ACTOR) # source actor
mock_post.return_value = requests_response()
with self.client:
got = self.client.post(path, json=NOTE)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
expected_as2 = common.redirect_unwrap({
**NOTE,
'actor': ACTOR,
})
got = self.client.post(path, json=NOTE)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
expected_as2 = common.redirect_unwrap({
**NOTE,
'actor': ACTOR,
})
self.assert_object('http://th.is/note/as2',
source_protocol='activitypub',
@ -446,10 +445,9 @@ class ActivityPubTest(testutil.TestCase):
not_public = copy.deepcopy(NOTE)
del not_public['object']['to']
with self.client:
got = self.client.post('/foo.com/inbox', json=not_public)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
self.assertEqual(0, Object.query().count())
got = self.client.post('/foo.com/inbox', json=not_public)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
self.assertEqual(0, Object.query().count())
def test_inbox_mention_object(self, *mocks):
self._test_inbox_mention(
@ -478,30 +476,29 @@ class ActivityPubTest(testutil.TestCase):
'<html><head><link rel="webmention" href="/webmention"></html>')
mock_post.return_value = requests_response()
with self.client:
got = self.client.post('/foo.com/inbox', json=mention)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
self.assert_req(mock_get, 'http://tar.get/')
self.assert_req(
mock_post,
'http://tar.get/webmention',
headers={'Accept': '*/*'},
allow_redirects=False,
data={
'source': 'http://localhost/render?id=http%3A%2F%2Fth.is%2Fmention',
'target': 'http://tar.get/',
},
)
got = self.client.post('/foo.com/inbox', json=mention)
self.assertEqual(200, got.status_code, got.get_data(as_text=True))
self.assert_req(mock_get, 'http://tar.get/')
self.assert_req(
mock_post,
'http://tar.get/webmention',
headers={'Accept': '*/*'},
allow_redirects=False,
data={
'source': 'http://localhost/render?id=http%3A%2F%2Fth.is%2Fmention',
'target': 'http://tar.get/',
},
)
expected_as2 = common.redirect_unwrap(mention)
self.assert_object('http://th.is/mention',
domains=['tar.get'],
source_protocol='activitypub',
status='complete',
as2=expected_as2,
as1=as2.to_as1(expected_as2),
delivered=['http://tar.get/'],
**expected_props)
expected_as2 = common.redirect_unwrap(mention)
self.assert_object('http://th.is/mention',
domains=['tar.get'],
source_protocol='activitypub',
status='complete',
as2=expected_as2,
as1=as2.to_as1(expected_as2),
delivered=['http://tar.get/'],
**expected_props)
def test_inbox_like(self, mock_head, mock_get, mock_post):
mock_head.return_value = requests_response(url='http://or.ig/post')

Wyświetl plik

@ -165,12 +165,11 @@ class FollowTest(testutil.TestCase):
'me': 'https://alice.com',
'state': input,
})
with self.client:
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/alice.com/following',resp.headers['Location'])
self.assertEqual([f'Followed <a href="https://bar/url">{input}</a>.'],
get_flashed_messages())
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/alice.com/following',resp.headers['Location'])
self.assertEqual([f'Followed <a href="https://bar/url">{input}</a>.'],
get_flashed_messages())
mock_get.assert_has_calls((
self.as2_req('https://bar/actor'),
@ -206,9 +205,8 @@ class FollowTest(testutil.TestCase):
'me': 'https://alice.com',
'state': '@foo@bar',
})
with self.client:
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(400, resp.status_code)
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(400, resp.status_code)
def test_callback_user_use_instead(self, mock_get, mock_post):
user = User.get_or_create('www.alice.com')
@ -228,10 +226,9 @@ class FollowTest(testutil.TestCase):
'me': 'https://alice.com',
'state': 'https://bar/actor',
})
with self.client:
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/www.alice.com/following', resp.headers['Location'])
resp = self.client.get(f'/follow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/www.alice.com/following', resp.headers['Location'])
id = 'http://localhost/user/www.alice.com/following#2022-01-02T03:04:05-https://bar/actor'
expected_follow = {
@ -312,12 +309,11 @@ class UnfollowTest(testutil.TestCase):
'me': 'https://alice.com',
'state': self.follower.id(),
})
with self.client:
resp = self.client.get(f'/unfollow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/alice.com/following', resp.headers['Location'])
self.assertEqual([f'Unfollowed <a href="https://bar/url">bar/url</a>.'],
get_flashed_messages())
resp = self.client.get(f'/unfollow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/alice.com/following', resp.headers['Location'])
self.assertEqual([f'Unfollowed <a href="https://bar/url">bar/url</a>.'],
get_flashed_messages())
inbox_args, inbox_kwargs = mock_post.call_args_list[1]
self.assertEqual(('http://bar/inbox',), inbox_args)
@ -360,10 +356,9 @@ class UnfollowTest(testutil.TestCase):
'me': 'https://alice.com',
'state': self.follower.id(),
})
with self.client:
resp = self.client.get(f'/unfollow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/www.alice.com/following', resp.headers['Location'])
resp = self.client.get(f'/unfollow/callback?code=my_code&state={state}')
self.assertEqual(302, resp.status_code)
self.assertEqual('/user/www.alice.com/following', resp.headers['Location'])
id = 'http://localhost/user/www.alice.com/following#undo-2022-01-02T03:04:05-https://bar/id'
expected_undo = {

Wyświetl plik

@ -1,5 +1,9 @@
"""Unit tests for pages.py."""
from flask import get_flashed_messages
from unittest.mock import patch
from oauth_dropins.webutil import util
from oauth_dropins.webutil.testutil import requests_response
from oauth_dropins.webutil.util import json_dumps, json_loads
from granary import as2, atom, microformats2, rss
from granary.tests.test_bluesky import REPLY_BSKY
@ -9,41 +13,26 @@ from granary.tests.test_as1 import (
FOLLOW_WITH_ACTOR,
FOLLOW_WITH_OBJECT,
LIKE,
MENTION,
NOTE,
)
import common
from models import Object, Follower, User
from . import testutil
from .test_webmention import ACTOR_MF2
from .test_webmention import ACTOR_AS2, ACTOR_HTML, ACTOR_MF2
def contents(activities):
return [(a.get('object') or a)['content'] for a in activities]
class PagesTest(testutil.TestCase):
EXPECTED_AS1 = [COMMENT, NOTE]
EXPECTED = contents(EXPECTED_AS1)
EXPECTED = contents([COMMENT, NOTE])
def setUp(self):
super().setUp()
self.user = User.get_or_create('foo.com')
@staticmethod
def add_objects():
# post
Object(id='a', domains=['foo.com'], labels=['feed', 'notification'],
as1=json_dumps(NOTE)).put()
# different domain
Object(id='b', domains=['bar.org'], labels=['feed', 'notification'],
as1=json_dumps(MENTION)).put()
# reply
Object(id='d', domains=['foo.com'], labels=['feed', 'notification'],
as1=json_dumps(COMMENT)).put()
# not feed/notif
Object(id='e', domains=['foo.com'], as1=json_dumps(NOTE)).put()
def test_user(self):
got = self.client.get('/user/foo.com')
self.assert_equals(200, got.status_code)
@ -66,6 +55,44 @@ class PagesTest(testutil.TestCase):
self.assert_equals(301, got.status_code)
self.assert_equals('/user/foo.com', got.headers['Location'])
@patch('requests.get')
def test_check_web_site(self, mock_get):
redir = 'http://localhost/.well-known/webfinger?resource=acct:orig@orig'
mock_get.side_effect = (
requests_response('', status=302, redirected_url=redir),
requests_response(ACTOR_HTML, url='https://orig/',
content_type=common.CONTENT_TYPE_HTML),
)
got = self.client.post('/web-site', data={'url': 'https://orig/'})
self.assert_equals(302, got.status_code)
self.assert_equals('/user/orig', got.headers['Location'])
user = User.get_by_id('orig')
self.assertTrue(user.has_hcard)
actor_as2 = json_loads(user.actor_as2)
self.assertEqual('Person', actor_as2['type'])
self.assertEqual('http://localhost/orig', actor_as2['id'])
def test_check_web_site_bad_url(self):
got = self.client.post('/web-site', data={'url': '!!!'})
self.assert_equals(200, got.status_code)
self.assertEqual(['No domain found in !!!'], get_flashed_messages())
self.assertEqual(1, User.query().count())
@patch('requests.get')
def test_check_web_site_fetch_fails(self, mock_get):
redir = 'http://localhost/.well-known/webfinger?resource=acct:orig@orig'
mock_get.side_effect = (
requests_response('', status=302, redirected_url=redir),
requests_response('', status=503),
)
got = self.client.post('/web-site', data={'url': 'https://orig/'})
self.assert_equals(200, got.status_code)
self.assertTrue(get_flashed_messages()[0].startswith(
"Couldn't connect to https://orig/: "))
def test_followers(self):
User.get_or_create('bar.com')
Follower.get_or_create('bar.com', 'https://no/stored/follow')

Wyświetl plik

@ -4,6 +4,7 @@ from unittest import skip
from unittest.mock import patch
from granary import bluesky
from granary.tests.test_as1 import COMMENT, NOTE
from granary.tests.test_bluesky import (
POST_BSKY,
POST_HTML,
@ -17,7 +18,6 @@ from oauth_dropins.webutil.testutil import requests_response
import requests
from werkzeug.exceptions import BadGateway
from .test_pages import PagesTest
from . import testutil
AUTHOR_HTML = """
@ -169,11 +169,11 @@ class XrpcFeedTest(testutil.TestCase):
}, got.json)
def test_getTimeline(self, mock_get):
PagesTest.add_objects()
self.add_objects()
got = self.client.get('/xrpc/app.bsky.feed.getTimeline')
self.assertEqual({
'feed': [bluesky.from_as1(a) for a in PagesTest.EXPECTED_AS1]
'feed': [bluesky.from_as1(COMMENT), bluesky.from_as1(NOTE)],
}, got.json)
def test_getVotes(self, mock_get):

Wyświetl plik

@ -6,6 +6,11 @@ import unittest
from unittest.mock import ANY, call
from granary import as2
from granary.tests.test_as1 import (
COMMENT,
MENTION,
NOTE,
)
from oauth_dropins.webutil import testutil, util
from oauth_dropins.webutil.appengine_config import ndb_client
from oauth_dropins.webutil.testutil import requests_response
@ -24,7 +29,9 @@ class TestCase(unittest.TestCase, testutil.Asserts):
super().setUp()
app.testing = True
cache.clear()
self.client = app.test_client()
self.client.__enter__()
# clear datastore
requests.post(f'http://{ndb_client.host}/reset')
@ -35,8 +42,23 @@ class TestCase(unittest.TestCase, testutil.Asserts):
def tearDown(self):
self.ndb_context.__exit__(None, None, None)
self.client.__exit__(None, None, None)
super().tearDown()
@staticmethod
def add_objects():
# post
Object(id='a', domains=['foo.com'], labels=['feed', 'notification'],
as1=json_dumps(NOTE)).put()
# different domain
Object(id='b', domains=['bar.org'], labels=['feed', 'notification'],
as1=json_dumps(MENTION)).put()
# reply
Object(id='d', domains=['foo.com'], labels=['feed', 'notification'],
as1=json_dumps(COMMENT)).put()
# not feed/notif
Object(id='e', domains=['foo.com'], as1=json_dumps(NOTE)).put()
def req(self, url, **kwargs):
"""Returns a mock requests call."""
kwargs.setdefault('headers', {}).update({