bridgy-fed/tests/test_convert.py

356 wiersze
14 KiB
Python
Czysty Zwykły widok Historia

"""Unit tests for convert.py.
"""
2023-05-24 23:00:41 +00:00
import copy
from unittest.mock import patch
2023-05-24 23:00:41 +00:00
from granary import as2
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 json_loads, parse_mf2
# import first so that Fake is defined before URL routes are registered
from . import testutil
from .testutil import Fake, OtherFake
from activitypub import ActivityPub
from common import CONTENT_TYPE_HTML
from web import Web
COMMENT_AS2 = {
**as2.from_as1(COMMENT),
'type': 'Note',
'id': 'https://web.brid.gy/r/https://fake.com/123456',
'url': 'https://web.brid.gy/r/https://fake.com/123456',
'name': 'A ☕ reply',
'contentMap': {'en': COMMENT['content']},
'inReplyTo': 'https://web.brid.gy/r/https://fake.com/123',
}
HTML = """\
2023-05-24 23:00:41 +00:00
<!DOCTYPE html>
<html>
<head><meta charset="utf-8">
<meta http-equiv="refresh" content="0;url=https://fake.com/123456"></head>
<body class="">
<article class="h-entry">
<span class="p-uid">tag:fake.com:123456</span>
2023-05-24 23:00:41 +00:00
<time class="dt-published" datetime="2012-12-05T00:58:26+00:00">2012-12-05T00:58:26+00:00</time>
<a class="u-url" href="https://fake.com/123456">fake.com/123456</a>
<div class="e-content p-name">
A reply
</div>
<a class="u-in-reply-to" href="https://fake.com/123"></a>
<a class="u-in-reply-to" href="tag:fake.com:123"></a>
2023-05-24 23:00:41 +00:00
</article>
</body>
</html>
"""
HTML_NO_ID = HTML.replace('<span class="p-uid">tag:fake.com:123456</span>', '')
AUTHOR_HTML = """\
2023-05-24 23:00:41 +00:00
<!DOCTYPE html>
<html>
<head><meta charset="utf-8">
<meta http-equiv="refresh" content="0;url=https://fake.com/123456"></head>
<body class="">
<article class="h-entry">
<span class="p-uid">tag:fake.com:123456</span>
<time class="dt-published" datetime="2012-12-05T00:58:26+00:00">2012-12-05T00:58:26+00:00</time>
<span class="p-author h-card">
<data class="p-uid" value="tag:fake.com:444"></data>
<a class="p-name u-url" href="https://plus.google.com/bob">Bob</a>
<img class="u-photo" src="https://bob/picture" alt="" />
</span>
<a class="u-url" href="https://fake.com/123456">fake.com/123456</a>
<div class="e-content p-name">
A reply
</div>
<a class="u-in-reply-to" href="https://fake.com/123"></a>
<a class="u-in-reply-to" href="tag:fake.com:123"></a>
2023-05-24 23:00:41 +00:00
</article>
</body>
</html>
"""
class ConvertTest(testutil.TestCase):
2023-05-24 23:00:41 +00:00
def test_unknown_source(self):
resp = self.client.get('/convert/web/http://foo',
base_url='https://nope.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(404, resp.status_code)
def test_unknown_dest(self):
resp = self.client.get('/convert/nope/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(404, resp.status_code)
def test_missing_url(self):
resp = self.client.get('/convert/web/',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(404, resp.status_code)
def test_fake_to_other(self):
self.store_object(id='fake:post', our_as1={'foo': 'bar'})
resp = self.client.get('/convert/other/fake:post',
base_url='https://fa.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assertEqual(OtherFake.CONTENT_TYPE, resp.content_type)
self.assertEqual({
'id': 'other:o:fa:fake:post',
'foo': 'bar',
}, json_loads(resp.get_data()))
def test_fake_to_activitypub(self):
self.make_user('fake:alice', cls=Fake)
self.store_object(id='fake:post', our_as1={
'actor': 'fake:alice',
'foo': 'bar',
})
resp = self.client.get('/convert/ap/fake:post',
base_url='https://fa.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assertEqual(ActivityPub.CONTENT_TYPE, resp.content_type)
self.assertEqual({
'@context': 'https://www.w3.org/ns/activitystreams',
'id': 'https://fa.brid.gy/convert/ap/fake:post',
'actor': 'https://fa.brid.gy/ap/fake:alice',
'foo': 'bar',
'to': ['https://www.w3.org/ns/activitystreams#Public'],
}, json_loads(resp.get_data()))
2023-05-24 23:00:41 +00:00
def test_activitypub_to_web_object(self):
url = 'https://user.com/bar?baz=baj&biff'
Object(id=url, our_as1=COMMENT).put()
resp = self.client.get('/convert/web/https://user.com/bar?baz=baj&biff',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(200, resp.status_code)
self.assert_multiline_equals(HTML, resp.get_data(as_text=True),
2023-05-24 23:00:41 +00:00
ignore_blanks=True)
2023-05-24 23:00:41 +00:00
def test_activitypub_to_web_object_empty(self):
Object(id='http://foo').put()
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(404, resp.status_code)
Revert "cache outbound HTTP request responses, locally to each inbound request" This reverts commit 30debfc8faf730190bd51a3aef49df6c6bfbd50a. seemed promising, but broke in production. Saw a lot of `IncompleteRead`s on both GETs and POSTs. Rolled back for now. ``` ('Connection broken: IncompleteRead(9172 bytes read, -4586 more expected)', IncompleteRead(9172 bytes read, -4586 more expected)) ... File "oauth_dropins/webutil/util.py", line 1673, in call resp = getattr((session or requests), fn)(url, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 102, in get return self.request('GET', url, params=params, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 158, in request return super().request(method, url, *args, headers=headers, **kwargs) # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 205, in send response = self._send_and_cache(request, actions, cached_response, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 233, in _send_and_cache self.cache.save_response(response, actions.cache_key, actions.expires) File "requests_cache/backends/base.py", line 89, in save_response cached_response = CachedResponse.from_response(response, expires=expires) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/response.py", line 102, in from_response obj.raw = CachedHTTPResponse.from_response(response) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/raw_response.py", line 69, in from_response _ = response.content # This property reads, decodes, and stores response content ^^^^^^^^^^^^^^^^ File "requests/models.py", line 899, in content self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/models.py", line 818, in generate raise ChunkedEncodingError(e) ```
2024-03-08 21:24:28 +00:00
@patch('requests.get')
2023-05-24 23:00:41 +00:00
def test_activitypub_to_web_fetch(self, mock_get):
mock_get.return_value = self.as2_resp(as2.from_as1(COMMENT))
url = 'https://user.com/bar?baz=baj&biff'
resp = self.client.get(f'/convert/web/{url}',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(200, resp.status_code)
self.assertEqual(CONTENT_TYPE_HTML, resp.content_type)
self.assert_multiline_equals(HTML, resp.get_data(as_text=True),
2023-05-24 23:00:41 +00:00
ignore_blanks=True)
2023-05-24 23:00:41 +00:00
mock_get.assert_has_calls((self.as2_req(url),))
Revert "cache outbound HTTP request responses, locally to each inbound request" This reverts commit 30debfc8faf730190bd51a3aef49df6c6bfbd50a. seemed promising, but broke in production. Saw a lot of `IncompleteRead`s on both GETs and POSTs. Rolled back for now. ``` ('Connection broken: IncompleteRead(9172 bytes read, -4586 more expected)', IncompleteRead(9172 bytes read, -4586 more expected)) ... File "oauth_dropins/webutil/util.py", line 1673, in call resp = getattr((session or requests), fn)(url, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 102, in get return self.request('GET', url, params=params, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 158, in request return super().request(method, url, *args, headers=headers, **kwargs) # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 205, in send response = self._send_and_cache(request, actions, cached_response, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 233, in _send_and_cache self.cache.save_response(response, actions.cache_key, actions.expires) File "requests_cache/backends/base.py", line 89, in save_response cached_response = CachedResponse.from_response(response, expires=expires) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/response.py", line 102, in from_response obj.raw = CachedHTTPResponse.from_response(response) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/raw_response.py", line 69, in from_response _ = response.content # This property reads, decodes, and stores response content ^^^^^^^^^^^^^^^^ File "requests/models.py", line 899, in content self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/models.py", line 818, in generate raise ChunkedEncodingError(e) ```
2024-03-08 21:24:28 +00:00
@patch('requests.get')
def test_activitypub_to_web_fetch_fails(self, mock_get):
mock_get.side_effect = [requests_response('', status=405)]
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(502, resp.status_code)
mock_get.assert_has_calls((self.as2_req('http://foo'),))
2023-05-24 23:00:41 +00:00
def test_activitypub_to_web_with_author(self):
Object(id='http://fo/o', our_as1={**COMMENT, 'author': 'http://ba/r'},
source_protocol='activitypub').put()
Object(id='http://ba/r', our_as1=ACTOR,
source_protocol='activitypub').put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://fo/o',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(200, resp.status_code)
self.assert_multiline_equals(AUTHOR_HTML, resp.get_data(as_text=True),
2023-05-24 23:00:41 +00:00
ignore_blanks=True)
def test_activitypub_to_web_no_url(self):
comment = copy.deepcopy(COMMENT)
del comment['url']
Object(id='http://foo', our_as1=comment).put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(200, resp.status_code)
expected = HTML.replace(
2023-05-24 23:00:41 +00:00
'\n<meta http-equiv="refresh" content="0;url=https://fake.com/123456">', ''
).replace('<a class="u-url" href="https://fake.com/123456">fake.com/123456</a>', '')
self.assert_multiline_equals(expected, resp.get_data(as_text=True),
ignore_blanks=True)
def test_activitypub_to_web_deleted_object(self):
Object(id='http://foo', as2={'content': 'foo'}, deleted=True).put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(410, resp.status_code)
def test_activitypub_to_web_delete_activity(self):
Object(id='http://foo', our_as1=DELETE_OF_ID).put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(410, resp.status_code)
def test_activitypub_to_web_update_inner_obj_exists_redirect(self):
# UPDATE's object field is a full object
Object(id='http://foo', our_as1=UPDATE).put()
Object(id=UPDATE['object']['id'], as2={'content': 'foo'}).put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(301, resp.status_code)
self.assertEqual(f'/convert/web/tag:fake.com:123456',
2023-05-24 23:00:41 +00:00
resp.headers['Location'])
def test_activitypub_to_web_delete_inner_obj_exists_redirect(self):
# DELETE_OF_ID's object field is a bare string id
Object(id='http://foo', our_as1=DELETE_OF_ID).put()
Object(id=DELETE_OF_ID['object'], as2={'content': 'foo'}).put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(301, resp.status_code)
self.assertEqual(f'/convert/web/tag:fake.com:123456',
2023-05-24 23:00:41 +00:00
resp.headers['Location'])
def test_activitypub_to_web_update_no_inner_obj_serve_as_is(self):
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 ':'
2023-06-20 18:22:54 +00:00
# Update's object field is a full object
Object(id='http://foo', our_as1=UPDATE).put()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(200, resp.status_code)
self.assert_multiline_in("""\
<div class="e-content p-name">
A reply
</div>
<a class="u-in-reply-to" href="https://fake.com/123"></a>
""", resp.get_data(as_text=True), ignore_blanks=True)
def test_activitypub_to_web_update_inner_obj_too_minimal_serve_as_is(self):
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 ':'
2023-06-20 18:22:54 +00:00
# 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()
2023-05-24 23:00:41 +00:00
resp = self.client.get('/convert/web/http://foo',
base_url='https://ap.brid.gy/')
2023-05-24 23:00:41 +00:00
self.assertEqual(200, resp.status_code)
self.assert_multiline_in("""\
<div class="e-content p-name">
A reply
</div>
<a class="u-in-reply-to" href="https://fake.com/123"></a>
""", resp.get_data(as_text=True), ignore_blanks=True)
def test_render_endpoint_redirect(self):
resp = self.client.get('/render?id=http://foo%3Fbar')
self.assertEqual(301, resp.status_code)
self.assertEqual(f'https://ap.brid.gy/convert/web/http://foo?bar',
resp.headers['Location'])
def test_convert_source_path_redirect(self):
resp = self.client.get('/convert/activitypub/web/https:/foo%3Fbar%23baz',
base_url='https://fed.brid.gy/')
self.assertEqual(301, resp.status_code)
self.assertEqual(f'https://ap.brid.gy/convert/web/https:/foo%3Fbar%23baz',
resp.headers['Location'])
# the Flask/Werkeug test client strips the #baz here. but ideally we
# should be testing it since somehow request.full_path URL-decodes in
# prod but not here. ugh.
# resp = self.client.get('/convert/activitypub/web/https:/foo?bar#baz')
# self.assertEqual(301, resp.status_code)
# self.assertEqual(f'https://ap.brid.gy/convert/web/https:/foo%3Fbar%23baz',
# resp.headers['Location'])
Revert "cache outbound HTTP request responses, locally to each inbound request" This reverts commit 30debfc8faf730190bd51a3aef49df6c6bfbd50a. seemed promising, but broke in production. Saw a lot of `IncompleteRead`s on both GETs and POSTs. Rolled back for now. ``` ('Connection broken: IncompleteRead(9172 bytes read, -4586 more expected)', IncompleteRead(9172 bytes read, -4586 more expected)) ... File "oauth_dropins/webutil/util.py", line 1673, in call resp = getattr((session or requests), fn)(url, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 102, in get return self.request('GET', url, params=params, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 158, in request return super().request(method, url, *args, headers=headers, **kwargs) # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 205, in send response = self._send_and_cache(request, actions, cached_response, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 233, in _send_and_cache self.cache.save_response(response, actions.cache_key, actions.expires) File "requests_cache/backends/base.py", line 89, in save_response cached_response = CachedResponse.from_response(response, expires=expires) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/response.py", line 102, in from_response obj.raw = CachedHTTPResponse.from_response(response) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/raw_response.py", line 69, in from_response _ = response.content # This property reads, decodes, and stores response content ^^^^^^^^^^^^^^^^ File "requests/models.py", line 899, in content self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/models.py", line 818, in generate raise ChunkedEncodingError(e) ```
2024-03-08 21:24:28 +00:00
@patch('requests.get')
2023-11-03 22:11:21 +00:00
def test_web_to_activitypub_object(self, mock_get):
mock_get.return_value = requests_response(HTML_NO_ID)
2023-11-03 22:11:21 +00:00
self.make_user('user.com', cls=Web)
2023-11-03 22:11:21 +00:00
url = 'https://user.com/bar?baz=baj&biff'
Object(id=url, mf2=parse_mf2(HTML_NO_ID)['items'][0]).put()
2023-11-03 22:11:21 +00:00
resp = self.client.get(f'/convert/ap/{url}', base_url='https://web.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assert_equals(COMMENT_AS2, resp.json, ignore=['to'])
Revert "cache outbound HTTP request responses, locally to each inbound request" This reverts commit 30debfc8faf730190bd51a3aef49df6c6bfbd50a. seemed promising, but broke in production. Saw a lot of `IncompleteRead`s on both GETs and POSTs. Rolled back for now. ``` ('Connection broken: IncompleteRead(9172 bytes read, -4586 more expected)', IncompleteRead(9172 bytes read, -4586 more expected)) ... File "oauth_dropins/webutil/util.py", line 1673, in call resp = getattr((session or requests), fn)(url, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 102, in get return self.request('GET', url, params=params, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 158, in request return super().request(method, url, *args, headers=headers, **kwargs) # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 205, in send response = self._send_and_cache(request, actions, cached_response, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 233, in _send_and_cache self.cache.save_response(response, actions.cache_key, actions.expires) File "requests_cache/backends/base.py", line 89, in save_response cached_response = CachedResponse.from_response(response, expires=expires) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/response.py", line 102, in from_response obj.raw = CachedHTTPResponse.from_response(response) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/raw_response.py", line 69, in from_response _ = response.content # This property reads, decodes, and stores response content ^^^^^^^^^^^^^^^^ File "requests/models.py", line 899, in content self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/models.py", line 818, in generate raise ChunkedEncodingError(e) ```
2024-03-08 21:24:28 +00:00
@patch('requests.get')
def test_web_to_activitypub_fetch(self, mock_get):
mock_get.return_value = requests_response(HTML) # protocol inference
url = 'https://user.com/bar?baz=baj&biff'
self.make_user('user.com', cls=Web)
Object(id=url, mf2=parse_mf2(HTML_NO_ID)['items'][0]).put()
resp = self.client.get(f'/convert/ap/{url}', base_url='https://web.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assert_equals(COMMENT_AS2, resp.json, ignore=['to'])
Revert "cache outbound HTTP request responses, locally to each inbound request" This reverts commit 30debfc8faf730190bd51a3aef49df6c6bfbd50a. seemed promising, but broke in production. Saw a lot of `IncompleteRead`s on both GETs and POSTs. Rolled back for now. ``` ('Connection broken: IncompleteRead(9172 bytes read, -4586 more expected)', IncompleteRead(9172 bytes read, -4586 more expected)) ... File "oauth_dropins/webutil/util.py", line 1673, in call resp = getattr((session or requests), fn)(url, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 102, in get return self.request('GET', url, params=params, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 158, in request return super().request(method, url, *args, headers=headers, **kwargs) # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 205, in send response = self._send_and_cache(request, actions, cached_response, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 233, in _send_and_cache self.cache.save_response(response, actions.cache_key, actions.expires) File "requests_cache/backends/base.py", line 89, in save_response cached_response = CachedResponse.from_response(response, expires=expires) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/response.py", line 102, in from_response obj.raw = CachedHTTPResponse.from_response(response) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/raw_response.py", line 69, in from_response _ = response.content # This property reads, decodes, and stores response content ^^^^^^^^^^^^^^^^ File "requests/models.py", line 899, in content self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/models.py", line 818, in generate raise ChunkedEncodingError(e) ```
2024-03-08 21:24:28 +00:00
@patch('requests.get')
def test_web_to_activitypub_no_user(self, mock_get):
hcard = requests_response("""
<html>
<body class="h-card">
<a rel="me" class="u-url" href="/">
</a>
</body>
</html>""", url='https://nope.com/')
mock_get.side_effect = [
# post protocol inference
requests_response('<html><body class="h-entry"></body></html>'),
hcard,
hcard,
hcard,
]
resp = self.client.get(f'/convert/ap/https://nope.com/post',
base_url='https://web.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assert_equals({
'type': 'Note',
'id': 'https://web.brid.gy/r/https://nope.com/post',
'attributedTo': 'https://web.brid.gy/nope.com',
}, resp.json, ignore=['to'])
Revert "cache outbound HTTP request responses, locally to each inbound request" This reverts commit 30debfc8faf730190bd51a3aef49df6c6bfbd50a. seemed promising, but broke in production. Saw a lot of `IncompleteRead`s on both GETs and POSTs. Rolled back for now. ``` ('Connection broken: IncompleteRead(9172 bytes read, -4586 more expected)', IncompleteRead(9172 bytes read, -4586 more expected)) ... File "oauth_dropins/webutil/util.py", line 1673, in call resp = getattr((session or requests), fn)(url, *args, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 102, in get return self.request('GET', url, params=params, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 158, in request return super().request(method, url, *args, headers=headers, **kwargs) # type: ignore ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/sessions.py", line 589, in request resp = self.send(prep, **send_kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 205, in send response = self._send_and_cache(request, actions, cached_response, **kwargs) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/session.py", line 233, in _send_and_cache self.cache.save_response(response, actions.cache_key, actions.expires) File "requests_cache/backends/base.py", line 89, in save_response cached_response = CachedResponse.from_response(response, expires=expires) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/response.py", line 102, in from_response obj.raw = CachedHTTPResponse.from_response(response) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests_cache/models/raw_response.py", line 69, in from_response _ = response.content # This property reads, decodes, and stores response content ^^^^^^^^^^^^^^^^ File "requests/models.py", line 899, in content self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b"" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "requests/models.py", line 818, in generate raise ChunkedEncodingError(e) ```
2024-03-08 21:24:28 +00:00
@patch('requests.get')
2023-11-03 22:11:21 +00:00
def test_web_to_activitypub_url_decode(self, mock_get):
"""https://github.com/snarfed/bridgy-fed/issues/581"""
mock_get.return_value = requests_response(HTML_NO_ID)
2023-11-03 22:11:21 +00:00
self.make_user('user.com', cls=Web)
self.store_object(id='http://user.com/a#b',
mf2=parse_mf2(HTML_NO_ID)['items'][0])
resp = self.client.get(f'/convert/ap/http://user.com/a%23b',
base_url='https://web.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assert_equals(COMMENT_AS2, resp.json, ignore=['to'])
def test_fed_subdomain(self):
url = 'https://user.com/post'
Object(id=url, our_as1=COMMENT).put()
resp = self.client.get('/convert/web/https://user.com/post',
base_url='https://fed.brid.gy/')
self.assertEqual(200, resp.status_code)
self.assert_multiline_equals(HTML, resp.get_data(as_text=True),
ignore_blanks=True)
def test_missing_protocols(self):
resp = self.client.get('/convert/https:/user.com/post')
self.assertEqual(404, resp.status_code)
resp = self.client.get('/convert/https:/user.com/post',
base_url='https://fed.brid.gy/')
self.assertEqual(404, resp.status_code)