2022-11-17 15:38:52 +00:00
|
|
|
"""Unit tests for pages.py."""
|
2024-03-13 20:53:30 +00:00
|
|
|
from unittest import skip
|
2023-09-28 21:42:18 +00:00
|
|
|
from unittest.mock import patch
|
|
|
|
|
|
|
|
import arroba.server
|
2024-11-25 05:03:51 +00:00
|
|
|
import copy
|
2023-09-28 21:42:18 +00:00
|
|
|
from flask import get_flashed_messages
|
2023-07-20 05:39:22 +00:00
|
|
|
from google.cloud import ndb
|
2023-09-28 21:42:18 +00:00
|
|
|
from google.cloud.tasks_v2.types import Task
|
2023-06-20 18:22:54 +00:00
|
|
|
from granary import atom, microformats2, rss
|
2023-02-09 16:23:31 +00:00
|
|
|
from oauth_dropins.webutil import util
|
2023-09-28 21:42:18 +00:00
|
|
|
from oauth_dropins.webutil.appengine_config import tasks_client
|
|
|
|
from oauth_dropins.webutil.testutil import requests_response
|
2024-05-06 02:22:02 +00:00
|
|
|
from requests import ConnectionError
|
2023-01-28 23:07:05 +00:00
|
|
|
|
2023-05-30 23:36:18 +00:00
|
|
|
# import first so that Fake is defined before URL routes are registered
|
2025-01-10 19:36:22 +00:00
|
|
|
from .testutil import (
|
|
|
|
Fake,
|
|
|
|
ExplicitFake,
|
|
|
|
OtherFake,
|
|
|
|
TestCase,
|
|
|
|
ACTOR,
|
|
|
|
COMMENT,
|
|
|
|
MENTION,
|
|
|
|
NOTE,
|
|
|
|
)
|
2023-05-30 23:36:18 +00:00
|
|
|
|
2023-06-02 05:00:47 +00:00
|
|
|
from activitypub import ActivityPub
|
2023-11-16 03:08:06 +00:00
|
|
|
from atproto import ATProto
|
2023-10-31 19:49:15 +00:00
|
|
|
import common
|
2023-09-28 21:42:18 +00:00
|
|
|
from models import Object, Follower, Target
|
2023-07-20 05:39:22 +00:00
|
|
|
from web import Web
|
2023-05-26 23:07:36 +00:00
|
|
|
|
2023-10-03 23:52:21 +00:00
|
|
|
from granary.tests.test_bluesky import ACTOR_AS, ACTOR_PROFILE_BSKY
|
2025-01-10 20:38:46 +00:00
|
|
|
from .test_atproto import DID_DOC
|
2024-09-22 19:57:09 +00:00
|
|
|
from .test_web import (
|
2024-10-05 03:29:36 +00:00
|
|
|
ACTOR_AS1_UNWRAPPED_URLS,
|
|
|
|
ACTOR_AS2,
|
|
|
|
ACTOR_HTML,
|
|
|
|
ACTOR_HTML_RESP,
|
|
|
|
ACTOR_MF2_REL_URLS,
|
|
|
|
REPOST_AS2,
|
2024-09-22 19:57:09 +00:00
|
|
|
)
|
2023-02-09 04:22:16 +00:00
|
|
|
|
2023-06-02 05:00:47 +00:00
|
|
|
ACTOR_WITH_PREFERRED_USERNAME = {
|
2024-06-23 15:38:00 +00:00
|
|
|
'objectType': 'person',
|
2024-05-13 01:45:51 +00:00
|
|
|
'displayName': 'Me',
|
2024-06-23 15:38:00 +00:00
|
|
|
'username': 'me',
|
2024-01-13 03:52:49 +00:00
|
|
|
'url': 'https://plus.google.com/bob',
|
2024-05-13 01:45:51 +00:00
|
|
|
'image': 'http://pic',
|
2023-06-02 05:00:47 +00:00
|
|
|
}
|
2022-11-17 15:38:52 +00:00
|
|
|
|
2023-06-20 18:22:54 +00:00
|
|
|
|
2022-11-17 15:38:52 +00:00
|
|
|
def contents(activities):
|
2024-04-29 19:26:54 +00:00
|
|
|
return [
|
|
|
|
' '.join(util.parse_html((a.get('object') or a)['content']).get_text().split())
|
|
|
|
for a in activities]
|
2022-11-17 15:38:52 +00:00
|
|
|
|
|
|
|
|
2023-05-26 23:07:36 +00:00
|
|
|
class PagesTest(TestCase):
|
2023-10-10 22:43:29 +00:00
|
|
|
EXPECTED = contents([COMMENT, MENTION, NOTE])
|
2023-10-12 17:37:44 +00:00
|
|
|
EXPECTED_SNIPPETS = [
|
|
|
|
'Dr. Eve replied a comment',
|
|
|
|
'tag:fake.com:44... posted a mention',
|
2024-04-29 19:26:54 +00:00
|
|
|
'🌐 user.com posted my note',
|
2023-10-12 17:37:44 +00:00
|
|
|
]
|
2022-11-17 15:38:52 +00:00
|
|
|
|
2022-11-22 23:21:53 +00:00
|
|
|
def setUp(self):
|
|
|
|
super().setUp()
|
2023-11-30 05:06:55 +00:00
|
|
|
self.user = self.make_user('user.com', cls=Web, has_redirects=True)
|
2022-11-22 23:21:53 +00:00
|
|
|
|
2022-12-02 19:08:24 +00:00
|
|
|
def test_user(self):
|
2023-09-26 23:43:48 +00:00
|
|
|
got = self.client.get('/web/user.com', base_url='https://fed.brid.gy/')
|
2022-12-02 19:08:24 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-05-30 23:36:18 +00:00
|
|
|
def test_user_fake(self):
|
2023-10-10 21:55:27 +00:00
|
|
|
self.make_user('fake:foo', cls=Fake)
|
2023-11-24 06:06:08 +00:00
|
|
|
got = self.client.get('/fake/fake:foo')
|
2023-05-30 23:36:18 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2025-01-10 20:38:46 +00:00
|
|
|
def test_user_page_handle_activitypub(self):
|
2024-01-13 03:52:49 +00:00
|
|
|
user = self.make_user('http://fo/o', cls=ActivityPub,
|
2025-01-10 19:36:22 +00:00
|
|
|
enabled_protocols=['fake'],
|
2024-06-23 15:38:00 +00:00
|
|
|
obj_as1=ACTOR_WITH_PREFERRED_USERNAME)
|
|
|
|
self.assertEqual('@me@fo', user.handle_as(ActivityPub))
|
2023-06-02 05:00:47 +00:00
|
|
|
|
2024-06-23 15:38:00 +00:00
|
|
|
got = self.client.get('/ap/@me@fo')
|
2023-06-02 05:00:47 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-06-23 19:22:37 +00:00
|
|
|
# TODO: can't handle slashes in id segment of path. is that ok?
|
|
|
|
# got = self.client.get('/ap/http%3A//foo')
|
|
|
|
# self.assert_equals(302, got.status_code)
|
|
|
|
# self.assert_equals('/ap/@me@plus.google.com', got.headers['Location'])
|
2023-06-02 05:00:47 +00:00
|
|
|
|
2025-01-10 20:38:46 +00:00
|
|
|
def test_user_page_handle_atproto(self):
|
|
|
|
self.store_object(id='did:plc:user', raw={
|
|
|
|
**DID_DOC,
|
|
|
|
'alsoKnownAs': ['at://han.dull'],
|
|
|
|
})
|
|
|
|
user = self.make_user('did:plc:user', cls=ATProto, enabled_protocols=['fake'])
|
|
|
|
|
|
|
|
got = self.client.get('/bsky/@han.dull')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
got = self.client.get('/bsky/han.dull')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-06-07 18:51:31 +00:00
|
|
|
def test_user_web_custom_username_doesnt_redirect(self):
|
|
|
|
"""https://github.com/snarfed/bridgy-fed/issues/534"""
|
2023-06-27 03:22:06 +00:00
|
|
|
self.user.obj = Object(id='a', as2={
|
2023-06-07 18:51:31 +00:00
|
|
|
**ACTOR_AS2,
|
|
|
|
'url': 'acct:baz@user.com',
|
2023-06-27 03:22:06 +00:00
|
|
|
})
|
|
|
|
self.user.obj.put()
|
2023-06-07 18:51:31 +00:00
|
|
|
self.user.put()
|
|
|
|
self.assertEqual('baz', self.user.username())
|
|
|
|
|
|
|
|
got = self.client.get('/web/@baz@user.com')
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
|
|
|
got = self.client.get('/web/baz')
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
|
|
|
got = self.client.get('/web/user.com')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assertIn('@baz@user.com', got.get_data(as_text=True))
|
|
|
|
|
2024-08-09 03:16:02 +00:00
|
|
|
def test_user_www_domain_special_case(self):
|
|
|
|
"""https://github.com/snarfed/bridgy-fed/issues/1244"""
|
|
|
|
www = self.make_user('www.jvt.me', cls=Web)
|
|
|
|
|
|
|
|
got = self.client.get('/web/www.jvt.me')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-01-28 23:07:05 +00:00
|
|
|
def test_user_objects(self):
|
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com')
|
2023-01-24 03:20:31 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2022-12-02 19:08:24 +00:00
|
|
|
def test_user_not_found(self):
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/bar.com')
|
2022-11-22 23:21:53 +00:00
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2023-10-13 19:36:31 +00:00
|
|
|
def test_user_opted_out(self):
|
|
|
|
self.user.obj.our_as1 = {'summary': '#nobridge'}
|
|
|
|
self.user.obj.put()
|
|
|
|
got = self.client.get('/web/user.com')
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2024-06-23 15:03:22 +00:00
|
|
|
def test_user_handle_opted_out(self):
|
|
|
|
user = self.make_user('fake:user', cls=Fake, manual_opt_out=True)
|
|
|
|
got = self.client.get('/fa/fake:handle:user')
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2025-01-10 19:36:22 +00:00
|
|
|
def test_user_default_serve_false_no_enabled_protocols(self):
|
|
|
|
self.make_user('other:foo', cls=OtherFake)
|
|
|
|
got = self.client.get('/other/other:foo')
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
|
|
|
def test_user_default_serve_false_enabled_protocols(self):
|
|
|
|
self.make_user('other:foo', cls=OtherFake, enabled_protocols=['fake'])
|
|
|
|
got = self.client.get('/other/other:foo')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2022-12-02 19:08:24 +00:00
|
|
|
def test_user_use_instead(self):
|
2023-11-15 22:23:08 +00:00
|
|
|
self.make_user('bar.com', cls=Web, use_instead=self.user.key)
|
2022-12-02 19:08:24 +00:00
|
|
|
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/bar.com')
|
2023-06-07 18:51:31 +00:00
|
|
|
self.assert_equals(302, got.status_code)
|
2023-05-30 21:08:13 +00:00
|
|
|
self.assert_equals('/web/user.com', got.headers['Location'])
|
2022-12-02 19:08:24 +00:00
|
|
|
|
2023-06-07 18:51:31 +00:00
|
|
|
got = self.client.get('/web/user.com')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-03-03 18:07:54 +00:00
|
|
|
def test_user_object_bare_string_id(self):
|
2023-06-15 22:09:03 +00:00
|
|
|
Object(id='a', users=[self.user.key], labels=['notification'],
|
|
|
|
as2=REPOST_AS2).put()
|
2023-03-03 18:07:54 +00:00
|
|
|
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com')
|
2023-03-03 18:07:54 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-03-24 05:04:09 +00:00
|
|
|
def test_user_object_url_object(self):
|
2023-05-23 06:09:36 +00:00
|
|
|
with self.request_context:
|
2023-06-09 19:56:45 +00:00
|
|
|
Object(id='a', users=[self.user.key], labels=['notification'], our_as1={
|
2023-03-24 05:04:09 +00:00
|
|
|
**REPOST_AS2,
|
|
|
|
'object': {
|
|
|
|
'id': 'https://mas.to/toot/id',
|
|
|
|
'url': {'value': 'http://foo', 'displayName': 'bar'},
|
|
|
|
},
|
|
|
|
}).put()
|
|
|
|
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com')
|
2023-03-24 05:04:09 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-05-24 13:49:54 +00:00
|
|
|
def test_user_before(self):
|
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get(f'/web/user.com?before={util.now().isoformat()}')
|
2023-05-24 13:49:54 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
def test_user_after(self):
|
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get(f'/web/user.com?after={util.now().isoformat()}')
|
2023-05-24 13:49:54 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
def test_user_before_bad(self):
|
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com?before=nope')
|
2023-05-24 13:49:54 +00:00
|
|
|
self.assert_equals(400, got.status_code)
|
|
|
|
|
|
|
|
def test_user_before_and_after(self):
|
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com?before=2024-01-01+01:01:01&after=2023-01-01+01:01:01')
|
2023-05-24 13:49:54 +00:00
|
|
|
self.assert_equals(400, got.status_code)
|
|
|
|
|
2025-01-12 16:32:18 +00:00
|
|
|
def test_user_protocol_bot_user(self):
|
|
|
|
bot = self.make_user(id='fa.brid.gy', cls=Web)
|
|
|
|
got = self.client.get(f'/web/fa.brid.gy')
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2024-04-23 03:21:56 +00:00
|
|
|
def test_update_profile(self):
|
2025-01-20 21:42:02 +00:00
|
|
|
user = self.make_user('fake:user', cls=Fake)
|
2025-01-28 00:13:59 +00:00
|
|
|
user.obj.copies = [Target(protocol='other', uri='other:profile:fake:user')]
|
|
|
|
user.obj.put()
|
|
|
|
|
|
|
|
bob = self.make_user('other:bob', cls=OtherFake)
|
|
|
|
Follower.get_or_create(to=user, from_=bob)
|
2024-04-23 03:21:56 +00:00
|
|
|
|
|
|
|
actor = {
|
|
|
|
'objectType': 'person',
|
2025-01-20 21:42:02 +00:00
|
|
|
'id': 'fake:profile:user',
|
2024-04-23 03:21:56 +00:00
|
|
|
'displayName': 'Ms User',
|
|
|
|
}
|
2024-05-29 23:18:15 +00:00
|
|
|
Fake.fetchable = {'fake:profile:user': actor}
|
2024-04-23 03:21:56 +00:00
|
|
|
got = self.client.post('/fa/fake:user/update-profile')
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/fa/fake:handle:user', got.headers['Location'])
|
2024-05-29 23:46:16 +00:00
|
|
|
self.assertEqual(
|
|
|
|
['Updating profile from <a href="web:fake:user">fake:handle:user</a>...'],
|
|
|
|
get_flashed_messages())
|
2024-04-23 03:21:56 +00:00
|
|
|
|
2025-01-28 00:47:33 +00:00
|
|
|
self.assertEqual(['fake:profile:user'], Fake.fetched)
|
2024-04-25 20:45:27 +00:00
|
|
|
|
|
|
|
actor['updated'] = '2022-01-02T03:04:05+00:00'
|
2025-01-20 21:42:02 +00:00
|
|
|
self.assert_object('fake:profile:user', source_protocol='fake', our_as1=actor,
|
2025-01-28 00:13:59 +00:00
|
|
|
users=[user.key], copies=user.obj.copies)
|
|
|
|
|
|
|
|
update = {
|
|
|
|
'objectType': 'activity',
|
|
|
|
'verb': 'update',
|
|
|
|
'id': 'fake:profile:user#bridgy-fed-update-2022-01-02T03:04:05+00:00',
|
2025-01-28 00:47:33 +00:00
|
|
|
'actor': {**actor, 'id': 'fake:user'},
|
2025-01-28 00:13:59 +00:00
|
|
|
'object': actor,
|
|
|
|
}
|
|
|
|
self.assertEqual([('other:bob:target', update)], OtherFake.sent)
|
2024-04-23 03:21:56 +00:00
|
|
|
|
2024-05-06 02:22:02 +00:00
|
|
|
@patch.object(Fake, 'fetch', side_effect=ConnectionError('foo'))
|
|
|
|
def test_update_profile_load_fails(self, _):
|
|
|
|
self.make_user('fake:user', cls=Fake)
|
|
|
|
|
|
|
|
got = self.client.post('/fa/fake:user/update-profile')
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/fa/fake:handle:user', got.headers['Location'])
|
|
|
|
self.assertEqual(
|
2024-05-29 23:46:16 +00:00
|
|
|
['Couldn\'t update profile for <a href="web:fake:user">fake:handle:user</a>: foo'],
|
2024-05-06 02:22:02 +00:00
|
|
|
get_flashed_messages())
|
|
|
|
|
2024-05-25 17:55:48 +00:00
|
|
|
@patch.object(tasks_client, 'create_task', return_value=Task(name='my task'))
|
|
|
|
def test_update_profile_receive_task(self, mock_create_task):
|
|
|
|
common.RUN_TASKS_INLINE = False
|
|
|
|
|
|
|
|
user = self.make_user('fake:user', cls=Fake)
|
|
|
|
|
2024-05-29 23:18:15 +00:00
|
|
|
Fake.fetchable = {'fake:profile:user': {
|
2024-05-25 17:55:48 +00:00
|
|
|
'objectType': 'person',
|
|
|
|
'id': 'fake:user',
|
|
|
|
'displayName': 'Ms User',
|
|
|
|
}}
|
|
|
|
|
2024-05-28 21:54:31 +00:00
|
|
|
# use handle in URL to check that we use key id as authed_as below
|
|
|
|
got = self.client.post('/fa/fake:handle:user/update-profile')
|
2024-05-25 17:55:48 +00:00
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/fa/fake:handle:user', got.headers['Location'])
|
|
|
|
|
2024-05-29 23:18:15 +00:00
|
|
|
self.assert_equals(['fake:profile:user'], Fake.fetched)
|
2024-10-11 15:01:40 +00:00
|
|
|
self.assert_task(mock_create_task, 'receive', obj_id='fake:profile:user',
|
2024-05-25 17:55:48 +00:00
|
|
|
authed_as='fake:user')
|
|
|
|
|
2024-09-22 19:57:09 +00:00
|
|
|
@patch('requests.get', return_value=ACTOR_HTML_RESP)
|
|
|
|
def test_update_profile_web(self, mock_get):
|
2024-10-29 04:23:11 +00:00
|
|
|
self.user.obj.copies = [
|
|
|
|
Target(protocol='fake', uri='fa:profile:web:user.com'),
|
|
|
|
Target(protocol='other', uri='other:profile:web:user.com'),
|
|
|
|
]
|
|
|
|
self.user.enabled_protocols = ['other']
|
2024-10-08 22:02:56 +00:00
|
|
|
self.user.obj.put()
|
2024-09-22 19:57:09 +00:00
|
|
|
Follower.get_or_create(from_=self.make_user('fake:user', cls=Fake),
|
|
|
|
to=self.user)
|
|
|
|
|
|
|
|
got = self.client.post('/web/user.com/update-profile')
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/web/user.com', got.headers['Location'])
|
|
|
|
|
|
|
|
user = self.user.key.get()
|
|
|
|
self.assertIsNone(user.status)
|
2024-11-25 05:03:51 +00:00
|
|
|
expected_mf2 = copy.deepcopy(ACTOR_MF2_REL_URLS)
|
|
|
|
expected_mf2['rel-urls']['https://user.com/webmention'] = {
|
|
|
|
'rels': ['webmention'],
|
|
|
|
'text': '',
|
|
|
|
}
|
|
|
|
self.assertEqual(expected_mf2, user.obj.mf2)
|
2024-09-22 19:57:09 +00:00
|
|
|
|
2024-10-05 03:29:36 +00:00
|
|
|
actor_as1 = {
|
|
|
|
**ACTOR_AS1_UNWRAPPED_URLS,
|
|
|
|
'updated': '2022-01-02T03:04:05+00:00',
|
|
|
|
}
|
|
|
|
self.assertEqual([('fake:shared:target', {
|
|
|
|
'objectType': 'activity',
|
|
|
|
'verb': 'update',
|
|
|
|
'id': 'https://user.com/#bridgy-fed-update-2022-01-02T03:04:05+00:00',
|
2025-01-28 00:13:59 +00:00
|
|
|
'actor': {**actor_as1, 'id': 'user.com'},
|
2024-10-05 03:29:36 +00:00
|
|
|
'object': actor_as1,
|
|
|
|
})], Fake.sent)
|
2024-09-22 19:57:09 +00:00
|
|
|
|
2024-10-29 04:23:11 +00:00
|
|
|
self.assertEqual({'user.com': 'user.com'}, OtherFake.usernames)
|
|
|
|
|
2024-11-16 05:31:22 +00:00
|
|
|
@patch('requests.get', return_value=requests_response(
|
|
|
|
ACTOR_HTML, url='https://www.user.com/'))
|
|
|
|
def test_update_profile_web_www(self, mock_get):
|
|
|
|
self.user.obj.copies = [
|
|
|
|
Target(protocol='fake', uri='fa:profile:web:user.com'),
|
|
|
|
]
|
|
|
|
self.user.obj.put()
|
|
|
|
Follower.get_or_create(from_=self.make_user('fake:user', cls=Fake),
|
|
|
|
to=self.user)
|
|
|
|
|
|
|
|
got = self.client.post('/web/user.com/update-profile')
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/web/user.com', got.headers['Location'])
|
|
|
|
|
|
|
|
actor_as1 = {
|
|
|
|
**ACTOR_AS1_UNWRAPPED_URLS,
|
|
|
|
'updated': '2022-01-02T03:04:05+00:00',
|
|
|
|
}
|
|
|
|
self.assertEqual([('fake:shared:target', {
|
|
|
|
'objectType': 'activity',
|
|
|
|
'verb': 'update',
|
|
|
|
'id': 'https://user.com/#bridgy-fed-update-2022-01-02T03:04:05+00:00',
|
2025-01-28 00:13:59 +00:00
|
|
|
'actor': {**actor_as1, 'id': 'user.com'},
|
2024-11-16 05:31:22 +00:00
|
|
|
'object': actor_as1,
|
|
|
|
})], Fake.sent)
|
|
|
|
|
2024-09-22 19:57:09 +00:00
|
|
|
@patch('requests.get', return_value=requests_response(
|
|
|
|
ACTOR_HTML.replace('Ms. ☕ Baz', 'Ms. ☕ Baz #nobridge'),
|
|
|
|
url='https://user.com/'))
|
|
|
|
def test_update_profile_web_delete(self, mock_get):
|
2024-10-08 22:02:56 +00:00
|
|
|
self.user.obj.copies = [Target(protocol='fake', uri='fa:profile:web:user.com')]
|
|
|
|
self.user.obj.put()
|
2024-09-22 19:57:09 +00:00
|
|
|
Follower.get_or_create(from_=self.make_user('fake:user', cls=Fake),
|
|
|
|
to=self.user)
|
|
|
|
|
|
|
|
got = self.client.post('/web/user.com/update-profile')
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/web/user.com', got.headers['Location'])
|
|
|
|
|
|
|
|
user = self.user.key.get()
|
|
|
|
self.assertEqual('opt-out', user.status)
|
2024-10-05 03:29:36 +00:00
|
|
|
self.assertEqual([('fake:shared:target', {
|
|
|
|
'objectType': 'activity',
|
|
|
|
'verb': 'delete',
|
2025-01-18 20:12:25 +00:00
|
|
|
'id': 'https://user.com/#bridgy-fed-delete-user-all-2022-01-02T03:04:05+00:00',
|
2024-10-05 03:29:36 +00:00
|
|
|
'actor': 'user.com',
|
|
|
|
'object': 'user.com',
|
|
|
|
})], Fake.sent)
|
2024-09-22 19:57:09 +00:00
|
|
|
|
2023-01-19 23:25:56 +00:00
|
|
|
def test_followers(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
Follower.get_or_create(
|
|
|
|
to=self.user,
|
2024-05-13 01:45:51 +00:00
|
|
|
from_=self.make_user('http://un/used', cls=ActivityPub, obj_as1={
|
2023-06-12 22:50:47 +00:00
|
|
|
**ACTOR,
|
2024-01-13 03:52:49 +00:00
|
|
|
'id': 'http://un/used',
|
2023-06-12 22:50:47 +00:00
|
|
|
'url': 'http://stored/users/follow',
|
|
|
|
}))
|
2023-06-06 21:50:20 +00:00
|
|
|
Follower.get_or_create(
|
|
|
|
to=self.user,
|
2023-11-28 05:40:01 +00:00
|
|
|
from_=self.make_user('http://masto/user', cls=ActivityPub,
|
2024-06-23 15:38:00 +00:00
|
|
|
obj_as1=ACTOR_WITH_PREFERRED_USERNAME))
|
2023-06-06 21:50:20 +00:00
|
|
|
|
2024-02-11 22:35:10 +00:00
|
|
|
from models import PROTOCOLS
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get('/web/user.com/followers')
|
2023-01-19 23:25:56 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
body = got.get_data(as_text=True)
|
2023-06-12 22:50:47 +00:00
|
|
|
self.assertIn('@follow@stored', body)
|
2024-06-23 15:38:00 +00:00
|
|
|
self.assertIn('@me@masto', body)
|
2023-01-19 23:25:56 +00:00
|
|
|
|
2023-10-10 21:55:27 +00:00
|
|
|
def test_home_fake(self):
|
|
|
|
self.make_user('fake:foo', cls=Fake)
|
2023-11-24 06:06:08 +00:00
|
|
|
got = self.client.get('/fake/fake:foo/home')
|
2023-10-10 21:55:27 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
def test_home_objects(self):
|
|
|
|
self.add_objects()
|
|
|
|
got = self.client.get('/web/user.com/home')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-10-11 18:28:39 +00:00
|
|
|
def test_notifications_fake(self):
|
|
|
|
self.make_user('fake:foo', cls=Fake)
|
2023-11-24 06:06:08 +00:00
|
|
|
got = self.client.get('/fake/fake:foo/notifications')
|
2023-10-11 18:28:39 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
def test_notifications_objects(self):
|
|
|
|
self.add_objects()
|
|
|
|
got = self.client.get('/web/user.com/notifications')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
def test_notifications_rss(self):
|
|
|
|
self.add_objects()
|
|
|
|
got = self.client.get('/web/user.com/notifications?format=rss')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assert_equals(rss.CONTENT_TYPE, got.headers['Content-Type'])
|
2023-10-12 17:37:44 +00:00
|
|
|
self.assert_equals(self.EXPECTED_SNIPPETS,
|
|
|
|
contents(rss.to_activities(got.text)))
|
2023-10-11 18:28:39 +00:00
|
|
|
|
|
|
|
def test_notifications_atom(self):
|
|
|
|
self.add_objects()
|
|
|
|
got = self.client.get('/web/user.com/notifications?format=atom')
|
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assert_equals(atom.CONTENT_TYPE, got.headers['Content-Type'])
|
2023-10-12 17:37:44 +00:00
|
|
|
self.assert_equals(self.EXPECTED_SNIPPETS,
|
|
|
|
contents(atom.atom_to_activities(got.text)))
|
2023-10-11 18:28:39 +00:00
|
|
|
|
|
|
|
def test_notifications_html(self):
|
|
|
|
self.add_objects()
|
|
|
|
got = self.client.get('/web/user.com/notifications?format=html')
|
|
|
|
self.assert_equals(200, got.status_code)
|
2023-10-12 17:37:44 +00:00
|
|
|
self.assert_equals(self.EXPECTED_SNIPPETS,
|
2023-10-11 18:28:39 +00:00
|
|
|
contents(microformats2.html_to_activities(got.text)))
|
|
|
|
|
2023-05-30 23:36:18 +00:00
|
|
|
def test_followers_fake(self):
|
2023-10-10 21:55:27 +00:00
|
|
|
self.make_user('fake:foo', cls=Fake)
|
2023-11-24 06:06:08 +00:00
|
|
|
got = self.client.get('/fake/fake:foo/followers')
|
2023-05-30 23:36:18 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-11-28 05:01:02 +00:00
|
|
|
def test_followers_activitypub(self):
|
2024-06-23 15:38:00 +00:00
|
|
|
user = self.make_user('https://inst/user', cls=ActivityPub,
|
2025-01-10 19:36:22 +00:00
|
|
|
enabled_protocols=['fake'],
|
2024-06-23 15:38:00 +00:00
|
|
|
obj_as1=ACTOR_WITH_PREFERRED_USERNAME)
|
2023-11-28 05:01:02 +00:00
|
|
|
|
2024-06-23 15:38:00 +00:00
|
|
|
got = self.client.get('/ap/@me@inst/followers')
|
2023-11-28 05:01:02 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assert_equals('text/html', got.headers['Content-Type'].split(';')[0])
|
|
|
|
|
2023-01-19 23:25:56 +00:00
|
|
|
def test_followers_empty(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get('/web/user.com/followers')
|
2023-01-19 23:25:56 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assertNotIn('class="follower', got.get_data(as_text=True))
|
|
|
|
|
2022-11-22 23:21:53 +00:00
|
|
|
def test_followers_user_not_found(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get('/web/nope.com/followers')
|
2022-11-22 23:21:53 +00:00
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2023-01-19 23:25:56 +00:00
|
|
|
def test_following(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
Follower.get_or_create(
|
|
|
|
from_=self.user,
|
2024-05-13 01:45:51 +00:00
|
|
|
to=self.make_user('http://un/used', cls=ActivityPub, obj_as1={
|
2023-06-12 22:50:47 +00:00
|
|
|
**ACTOR,
|
2024-01-13 03:52:49 +00:00
|
|
|
'id': 'http://un/used',
|
2023-06-12 22:50:47 +00:00
|
|
|
'url': 'http://stored/users/follow',
|
|
|
|
}))
|
2023-06-06 21:50:20 +00:00
|
|
|
Follower.get_or_create(
|
|
|
|
from_=self.user,
|
2023-11-28 05:40:01 +00:00
|
|
|
to=self.make_user('http://masto/user', cls=ActivityPub,
|
2024-06-23 15:38:00 +00:00
|
|
|
obj_as1=ACTOR_WITH_PREFERRED_USERNAME))
|
2023-06-06 21:50:20 +00:00
|
|
|
|
|
|
|
got = self.client.get('/web/user.com/following')
|
2023-01-19 23:25:56 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
body = got.get_data(as_text=True)
|
2023-06-12 22:50:47 +00:00
|
|
|
self.assertIn('@follow@stored', body)
|
2024-06-23 15:38:00 +00:00
|
|
|
self.assertIn('@me@masto', body)
|
2023-01-19 23:25:56 +00:00
|
|
|
|
|
|
|
def test_following_empty(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get('/web/user.com/following')
|
2023-01-19 23:25:56 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assertNotIn('class="follower', got.get_data(as_text=True))
|
|
|
|
|
2023-05-30 23:36:18 +00:00
|
|
|
def test_following_fake(self):
|
2023-10-10 21:55:27 +00:00
|
|
|
self.make_user('fake:foo', cls=Fake)
|
2023-11-24 06:06:08 +00:00
|
|
|
got = self.client.get('/fake/fake:foo/following')
|
2023-05-30 23:36:18 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2022-11-22 23:21:53 +00:00
|
|
|
def test_following_user_not_found(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get('/web/nope.com/following')
|
2022-11-22 23:21:53 +00:00
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2023-02-09 16:23:31 +00:00
|
|
|
def test_following_before_empty(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get(f'/web/user.com/following?before={util.now().isoformat()}')
|
2023-02-09 16:23:31 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
|
|
|
def test_following_after_empty(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get(f'/web/user.com/following?after={util.now().isoformat()}')
|
2023-02-09 16:23:31 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2022-11-22 23:21:53 +00:00
|
|
|
def test_feed_user_not_found(self):
|
2023-06-06 21:50:20 +00:00
|
|
|
got = self.client.get('/web/nope.com/feed')
|
2022-11-22 23:21:53 +00:00
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
|
2023-05-30 23:36:18 +00:00
|
|
|
def test_feed_fake(self):
|
2023-10-10 21:55:27 +00:00
|
|
|
self.make_user('fake:foo', cls=Fake)
|
2023-11-24 06:06:08 +00:00
|
|
|
got = self.client.get('/fake/fake:foo/feed')
|
2023-05-30 23:36:18 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
|
2023-05-30 21:08:13 +00:00
|
|
|
def test_feed_html_empty(self):
|
|
|
|
got = self.client.get('/web/user.com/feed')
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
|
|
|
self.assert_equals([], microformats2.html_to_activities(got.text))
|
|
|
|
|
|
|
|
def test_feed_html(self):
|
2023-01-28 23:07:05 +00:00
|
|
|
self.add_objects()
|
2023-07-20 05:39:22 +00:00
|
|
|
|
2023-07-20 17:24:58 +00:00
|
|
|
# repost with object (original post) in separate Object
|
|
|
|
repost = {
|
|
|
|
'objectType': 'activity',
|
|
|
|
'verb': 'share',
|
|
|
|
'object': 'fake:orig',
|
|
|
|
}
|
|
|
|
orig = {
|
|
|
|
'objectType': 'note',
|
|
|
|
'content': 'biff',
|
|
|
|
}
|
2023-07-28 22:49:29 +00:00
|
|
|
self.store_object(id='fake:repost', feed=[self.user.key], our_as1=repost)
|
2023-07-20 17:24:58 +00:00
|
|
|
self.store_object(id='fake:orig', our_as1=orig)
|
|
|
|
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com/feed')
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
2023-07-28 22:49:29 +00:00
|
|
|
self.assert_equals(['biff'] + self.EXPECTED,
|
2022-11-17 15:38:52 +00:00
|
|
|
contents(microformats2.html_to_activities(got.text)))
|
2023-07-28 22:49:29 +00:00
|
|
|
|
|
|
|
# NOTE's and MENTION's authors; check for two instances
|
|
|
|
bob = '<a class="p-name u-url" href="https://plus.google.com/bob">Bob</a>'
|
|
|
|
assert got.text.index(bob) != got.text.rindex(bob)
|
2022-11-17 15:38:52 +00:00
|
|
|
|
|
|
|
def test_feed_atom_empty(self):
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com/feed?format=atom')
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
2023-10-11 18:28:39 +00:00
|
|
|
self.assert_equals(atom.CONTENT_TYPE, got.headers['Content-Type'])
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals([], atom.atom_to_activities(got.text))
|
|
|
|
|
2023-10-23 20:10:27 +00:00
|
|
|
def test_feed_atom_empty_g_user_without_obj(self):
|
|
|
|
self.user.obj_key = None
|
|
|
|
self.user.put()
|
|
|
|
self.test_feed_atom_empty()
|
|
|
|
|
2022-11-17 15:38:52 +00:00
|
|
|
def test_feed_atom(self):
|
2023-01-28 23:07:05 +00:00
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com/feed?format=atom')
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
2023-10-11 18:28:39 +00:00
|
|
|
self.assert_equals(atom.CONTENT_TYPE, got.headers['Content-Type'])
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(self.EXPECTED, contents(atom.atom_to_activities(got.text)))
|
|
|
|
|
2023-07-28 22:49:29 +00:00
|
|
|
# NOTE's and MENTION's authors; check for two instances
|
|
|
|
bob = """
|
|
|
|
<uri>https://plus.google.com/bob</uri>
|
|
|
|
|
|
|
|
<name>Bob</name>
|
|
|
|
"""
|
|
|
|
assert got.text.index(bob) != got.text.rindex(bob)
|
|
|
|
# COMMENT's author
|
|
|
|
self.assertIn('Dr. Eve', got.text)
|
|
|
|
|
2022-11-17 15:38:52 +00:00
|
|
|
def test_feed_rss_empty(self):
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com/feed?format=rss')
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
2023-10-11 18:28:39 +00:00
|
|
|
self.assert_equals(rss.CONTENT_TYPE, got.headers['Content-Type'])
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals([], rss.to_activities(got.text))
|
|
|
|
|
|
|
|
def test_feed_rss(self):
|
2023-01-28 23:07:05 +00:00
|
|
|
self.add_objects()
|
2023-05-30 21:08:13 +00:00
|
|
|
got = self.client.get('/web/user.com/feed?format=rss')
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(200, got.status_code)
|
2023-10-11 18:28:39 +00:00
|
|
|
self.assert_equals(rss.CONTENT_TYPE, got.headers['Content-Type'])
|
2022-11-17 15:38:52 +00:00
|
|
|
self.assert_equals(self.EXPECTED, contents(rss.to_activities(got.text)))
|
2023-05-26 23:36:45 +00:00
|
|
|
|
2023-07-28 22:49:29 +00:00
|
|
|
# NOTE's and MENTION's authors; check for two instances
|
2024-12-23 20:54:13 +00:00
|
|
|
bob = '<author>_@_._ (Bob)</author>'
|
|
|
|
self.assertIn(bob, got.text)
|
|
|
|
self.assertNotEqual(got.text.index(bob), got.text.rindex(bob), got.text)
|
2023-07-28 22:49:29 +00:00
|
|
|
# COMMENT's author
|
2024-12-23 20:54:13 +00:00
|
|
|
self.assertIn('<author>_@_._ (Dr. Eve)</author>', got.text, got.text)
|
2023-07-28 22:49:29 +00:00
|
|
|
|
2023-05-26 23:36:45 +00:00
|
|
|
def test_nodeinfo(self):
|
|
|
|
# just check that it doesn't crash
|
|
|
|
self.client.get('/nodeinfo.json')
|
2023-10-16 21:02:17 +00:00
|
|
|
|
2023-12-31 16:42:26 +00:00
|
|
|
def test_instance_info(self):
|
|
|
|
# just check that it doesn't crash
|
|
|
|
self.client.get('/api/v1/instance')
|
|
|
|
|
2023-10-16 21:02:17 +00:00
|
|
|
def test_canonicalize_domain(self):
|
|
|
|
got = self.client.get('/', base_url='https://ap.brid.gy/')
|
|
|
|
self.assert_equals(301, got.status_code)
|
|
|
|
self.assert_equals('https://fed.brid.gy/', got.headers['Location'])
|
2025-01-10 20:38:46 +00:00
|
|
|
|
|
|
|
def test_find_user_page_web_domain(self):
|
|
|
|
got = self.client.post('/user-page', data={'id': 'user.com'})
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/web/user.com', got.headers['Location'])
|
|
|
|
|
|
|
|
def test_find_user_page_fake_id(self):
|
|
|
|
self.make_user('fake:foo', cls=Fake)
|
|
|
|
got = self.client.post('/user-page', data={'id': 'fake:foo'})
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/fa/fake:handle:foo', got.headers['Location'])
|
|
|
|
|
|
|
|
def test_find_user_page_fake_handle(self):
|
|
|
|
self.make_user('fake:foo', cls=Fake)
|
|
|
|
got = self.client.post('/user-page', data={'id': 'fake:handle:foo'})
|
|
|
|
self.assert_equals(302, got.status_code)
|
|
|
|
self.assert_equals('/fa/fake:handle:foo', got.headers['Location'])
|
|
|
|
|
|
|
|
def test_find_user_page_unknown_protocol(self):
|
|
|
|
self.make_user('fake:foo', cls=Fake)
|
|
|
|
got = self.client.post('/user-page', data={'id': 'un:kn:own'})
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
self.assertEqual(["Couldn't determine network for un:kn:own."],
|
|
|
|
get_flashed_messages())
|
|
|
|
|
|
|
|
def test_find_user_page_fake_not_found(self):
|
|
|
|
got = self.client.post('/user-page', data={'id': 'fake:foo'})
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
self.assertEqual(["User fake:foo on fake-phrase isn't signed up."],
|
|
|
|
get_flashed_messages())
|
|
|
|
|
|
|
|
def test_find_user_page_other_not_enabled(self):
|
|
|
|
self.make_user('other:foo', cls=OtherFake)
|
|
|
|
got = self.client.post('/user-page', data={'id': 'other:foo'})
|
|
|
|
self.assert_equals(404, got.status_code)
|
|
|
|
self.assertEqual(["User other:foo on other-phrase isn't signed up."],
|
|
|
|
get_flashed_messages())
|