2017-08-19 15:36:55 +00:00
|
|
|
# coding=utf-8
|
|
|
|
"""Unit tests for models.py."""
|
2023-05-06 21:37:23 +00:00
|
|
|
from arroba.mst import dag_cbor_cid
|
2023-04-28 19:02:26 +00:00
|
|
|
from Crypto.PublicKey import ECC
|
2023-06-20 18:22:54 +00:00
|
|
|
from flask import g
|
2023-07-16 21:06:03 +00:00
|
|
|
from google.cloud import ndb
|
2023-04-28 19:02:26 +00:00
|
|
|
from granary.tests.test_bluesky import ACTOR_PROFILE_BSKY
|
2023-06-20 18:22:54 +00:00
|
|
|
from oauth_dropins.webutil.testutil import NOW
|
2022-11-19 02:46:27 +00:00
|
|
|
|
2023-05-30 23:36:18 +00:00
|
|
|
# import first so that Fake is defined before URL routes are registered
|
|
|
|
from .testutil import Fake, TestCase
|
|
|
|
|
2023-08-31 18:49:10 +00:00
|
|
|
from atproto import ATProto
|
2023-08-31 20:49:45 +00:00
|
|
|
from cryptography.hazmat.primitives.asymmetric import ec
|
|
|
|
from cryptography.hazmat.primitives import serialization
|
2023-07-26 15:22:34 +00:00
|
|
|
from models import Follower, Object, OBJECT_EXPIRE_AGE, Target, User
|
2023-04-03 03:36:23 +00:00
|
|
|
import protocol
|
2023-06-22 19:30:25 +00:00
|
|
|
from protocol import Protocol
|
2023-06-14 03:58:28 +00:00
|
|
|
from web import Web
|
2017-08-19 15:36:55 +00:00
|
|
|
|
2023-01-19 06:20:15 +00:00
|
|
|
from .test_activitypub import ACTOR
|
2017-08-19 15:36:55 +00:00
|
|
|
|
2023-05-26 23:07:36 +00:00
|
|
|
|
|
|
|
class UserTest(TestCase):
|
2017-08-19 15:36:55 +00:00
|
|
|
|
2017-09-19 14:13:35 +00:00
|
|
|
def setUp(self):
|
2023-03-20 21:28:14 +00:00
|
|
|
super().setUp()
|
|
|
|
g.user = self.make_user('y.z')
|
2017-09-19 14:13:35 +00:00
|
|
|
|
2022-11-20 17:38:46 +00:00
|
|
|
def test_get_or_create(self):
|
2023-05-26 23:07:36 +00:00
|
|
|
user = Fake.get_or_create('a.b')
|
2023-04-25 21:04:29 +00:00
|
|
|
|
2023-05-30 02:37:35 +00:00
|
|
|
assert not user.direct
|
2023-04-25 21:04:29 +00:00
|
|
|
assert user.mod
|
|
|
|
assert user.public_exponent
|
|
|
|
assert user.private_exponent
|
|
|
|
|
|
|
|
# check that we can load the keys
|
|
|
|
assert user.public_pem()
|
|
|
|
assert user.private_pem()
|
|
|
|
|
2023-05-30 02:37:35 +00:00
|
|
|
# direct should get set even if the user exists
|
|
|
|
same = Fake.get_or_create('a.b', direct=True)
|
|
|
|
user.direct = True
|
|
|
|
self.assert_entities_equal(same, user, ignore=['updated'])
|
2017-08-19 16:15:29 +00:00
|
|
|
|
2023-09-01 19:07:21 +00:00
|
|
|
def test_validate_atproto_did(self):
|
|
|
|
user = Fake()
|
|
|
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
user.atproto_did = 'did:foo:bar'
|
|
|
|
|
|
|
|
user.atproto_did = 'did:plc:123'
|
|
|
|
user.atproto_did = None
|
|
|
|
|
2023-09-19 23:48:16 +00:00
|
|
|
def test_get_for_copy(self):
|
|
|
|
self.assertIsNone(User.get_for_copy('did:plc:foo'))
|
2023-09-18 18:41:01 +00:00
|
|
|
|
2023-09-19 23:48:16 +00:00
|
|
|
target = Target(uri='did:plc:foo', protocol='atproto')
|
|
|
|
fake_user = self.make_user('fake:user', cls=Fake, copies=[target])
|
|
|
|
self.assertEqual(fake_user, User.get_for_copy('did:plc:foo'))
|
2023-09-18 05:38:33 +00:00
|
|
|
|
2022-12-02 18:48:16 +00:00
|
|
|
def test_get_or_create_use_instead(self):
|
2023-05-26 23:07:36 +00:00
|
|
|
user = Fake.get_or_create('a.b')
|
2023-03-20 21:28:14 +00:00
|
|
|
user.use_instead = g.user.key
|
2022-12-02 18:48:16 +00:00
|
|
|
user.put()
|
|
|
|
|
2023-05-26 23:07:36 +00:00
|
|
|
self.assertEqual('y.z', Fake.get_or_create('a.b').key.id())
|
2022-12-02 18:48:16 +00:00
|
|
|
|
2017-08-19 16:15:29 +00:00
|
|
|
def test_href(self):
|
2023-03-20 21:28:14 +00:00
|
|
|
href = g.user.href()
|
2017-08-19 16:15:29 +00:00
|
|
|
self.assertTrue(href.startswith('data:application/magic-public-key,RSA.'), href)
|
2023-03-20 21:28:14 +00:00
|
|
|
self.assertIn(g.user.mod, href)
|
|
|
|
self.assertIn(g.user.public_exponent, href)
|
2017-09-19 14:13:35 +00:00
|
|
|
|
|
|
|
def test_public_pem(self):
|
2023-03-20 21:28:14 +00:00
|
|
|
pem = g.user.public_pem()
|
2019-12-26 06:20:57 +00:00
|
|
|
self.assertTrue(pem.decode().startswith('-----BEGIN PUBLIC KEY-----\n'), pem)
|
|
|
|
self.assertTrue(pem.decode().endswith('-----END PUBLIC KEY-----'), pem)
|
2017-09-19 14:13:35 +00:00
|
|
|
|
2019-12-26 06:20:57 +00:00
|
|
|
def test_private_pem(self):
|
2023-03-20 21:28:14 +00:00
|
|
|
pem = g.user.private_pem()
|
2019-12-26 06:20:57 +00:00
|
|
|
self.assertTrue(pem.decode().startswith('-----BEGIN RSA PRIVATE KEY-----\n'), pem)
|
|
|
|
self.assertTrue(pem.decode().endswith('-----END RSA PRIVATE KEY-----'), pem)
|
2017-10-11 05:42:19 +00:00
|
|
|
|
2023-05-30 21:08:13 +00:00
|
|
|
def test_user_page_path(self):
|
|
|
|
self.assertEqual('/web/y.z', g.user.user_page_path())
|
|
|
|
self.assertEqual('/web/y.z/followers', g.user.user_page_path('followers'))
|
2023-06-11 15:14:17 +00:00
|
|
|
self.assertEqual('/fa/foo', self.make_user('foo', cls=Fake).user_page_path())
|
2023-05-30 21:08:13 +00:00
|
|
|
|
|
|
|
def test_user_page_link(self):
|
|
|
|
self.assertEqual('<a class="h-card u-author" href="/web/y.z"><img src="" class="profile"> y.z</a>', g.user.user_page_link())
|
2023-06-16 04:22:20 +00:00
|
|
|
g.user.obj = Object(id='a', as2=ACTOR)
|
2023-05-30 21:08:13 +00:00
|
|
|
self.assertEqual('<a class="h-card u-author" href="/web/y.z"><img src="https://user.com/me.jpg" class="profile"> Mrs. ☕ Foo</a>', g.user.user_page_link())
|
|
|
|
|
2023-06-01 01:34:33 +00:00
|
|
|
def test_is_web_url(self):
|
|
|
|
for url in 'y.z', '//y.z', 'http://y.z', 'https://y.z':
|
|
|
|
self.assertTrue(g.user.is_web_url(url), url)
|
|
|
|
|
|
|
|
for url in (None, '', 'user', 'com', 'com.user', 'ftp://y.z',
|
|
|
|
'https://user', '://y.z'):
|
|
|
|
self.assertFalse(g.user.is_web_url(url), url)
|
|
|
|
|
2023-06-02 04:37:58 +00:00
|
|
|
def test_name(self):
|
|
|
|
self.assertEqual('y.z', g.user.name())
|
|
|
|
|
2023-06-16 04:22:20 +00:00
|
|
|
g.user.obj = Object(id='a', as2={'id': 'abc'})
|
2023-06-02 04:37:58 +00:00
|
|
|
self.assertEqual('y.z', g.user.name())
|
|
|
|
|
2023-06-16 04:22:20 +00:00
|
|
|
g.user.obj = Object(id='a', as2={'name': 'alice'})
|
2023-06-02 04:37:58 +00:00
|
|
|
self.assertEqual('alice', g.user.name())
|
|
|
|
|
2023-06-04 04:48:15 +00:00
|
|
|
def test_readable_id(self):
|
|
|
|
self.assertIsNone(g.user.readable_id)
|
2023-06-02 04:37:58 +00:00
|
|
|
|
2023-06-16 21:09:28 +00:00
|
|
|
def test_as2(self):
|
|
|
|
self.assertEqual({}, g.user.as2())
|
|
|
|
|
|
|
|
obj = Object(id='foo')
|
|
|
|
g.user.obj_key = obj.key # doesn't exist
|
|
|
|
self.assertEqual({}, g.user.as2())
|
|
|
|
|
|
|
|
del g.user._obj
|
|
|
|
obj.as2 = {'foo': 'bar'}
|
|
|
|
obj.put()
|
|
|
|
self.assertEqual({'foo': 'bar'}, g.user.as2())
|
|
|
|
|
2023-08-31 18:49:10 +00:00
|
|
|
def test_ap_actor(self):
|
|
|
|
user = self.make_user('did:plc:abc', cls=ATProto)
|
|
|
|
self.assertEqual('http://localhost/ap/atproto/did:plc:abc',
|
|
|
|
user.ap_actor())
|
|
|
|
self.assertEqual('http://localhost/ap/atproto/did:plc:abc/foo',
|
|
|
|
user.ap_actor(rest='foo'))
|
|
|
|
|
2023-06-29 20:45:55 +00:00
|
|
|
def test_load_multi(self):
|
|
|
|
# obj_key is None
|
|
|
|
alice = Fake(id='alice.com')
|
|
|
|
alice.put()
|
|
|
|
|
|
|
|
# obj_key points to nonexistent entity
|
|
|
|
bob = Fake(id='bob.com', obj_key=Object(id='bob').key)
|
|
|
|
bob.put()
|
|
|
|
|
|
|
|
user = g.user.key.get()
|
|
|
|
self.assertFalse(hasattr(user, '_obj'))
|
|
|
|
self.assertFalse(hasattr(alice, '_obj'))
|
|
|
|
self.assertFalse(hasattr(bob, '_obj'))
|
|
|
|
|
|
|
|
User.load_multi([user, alice, bob])
|
|
|
|
self.assertIsNotNone(user._obj)
|
|
|
|
self.assertIsNone(alice._obj)
|
|
|
|
self.assertIsNone(bob._obj)
|
|
|
|
|
2023-05-26 23:07:36 +00:00
|
|
|
|
|
|
|
class ObjectTest(TestCase):
|
2023-03-20 21:28:14 +00:00
|
|
|
def setUp(self):
|
|
|
|
super().setUp()
|
|
|
|
g.user = None
|
|
|
|
|
2023-06-28 21:41:48 +00:00
|
|
|
def test_target_hashable(self):
|
|
|
|
target = Target(protocol='ui', uri='http://foo')
|
|
|
|
|
|
|
|
# just check that these don't crash
|
|
|
|
assert isinstance(id(target), int)
|
|
|
|
|
2023-06-22 21:27:02 +00:00
|
|
|
def test_ndb_in_memory_cache_off(self):
|
|
|
|
"""It has a weird bug that we want to avoid.
|
|
|
|
|
|
|
|
https://github.com/googleapis/python-ndb/issues/888
|
|
|
|
"""
|
|
|
|
from google.cloud.ndb import Model, StringProperty
|
|
|
|
class Foo(Model):
|
|
|
|
a = StringProperty()
|
|
|
|
|
|
|
|
f = Foo(id='x', a='asdf')
|
|
|
|
f.put()
|
|
|
|
# print(id(f))
|
|
|
|
|
|
|
|
f.a = 'qwert'
|
|
|
|
|
|
|
|
got = Foo.get_by_id('x')
|
|
|
|
# print(got)
|
|
|
|
# print(id(got))
|
|
|
|
self.assertEqual('asdf', got.a)
|
|
|
|
|
2023-07-01 17:02:01 +00:00
|
|
|
def test_get_or_create(self):
|
|
|
|
def check(obj1, obj2):
|
|
|
|
self.assert_entities_equal(obj1, obj2, ignore=['expire', 'updated'])
|
|
|
|
|
|
|
|
self.assertEqual(0, Object.query().count())
|
|
|
|
|
2023-07-16 21:06:03 +00:00
|
|
|
user = ndb.Key(Web, 'user.com')
|
2023-07-02 01:45:18 +00:00
|
|
|
obj = Object.get_or_create('foo', our_as1={'content': 'foo'},
|
2023-07-16 21:06:03 +00:00
|
|
|
source_protocol='ui', notify=[user])
|
2023-07-01 17:02:01 +00:00
|
|
|
check([obj], Object.query().fetch())
|
2023-07-02 01:45:18 +00:00
|
|
|
self.assertTrue(obj.new)
|
|
|
|
self.assertIsNone(obj.changed)
|
2023-07-01 17:02:01 +00:00
|
|
|
self.assertEqual('foo', obj.key.id())
|
2023-07-09 23:29:22 +00:00
|
|
|
self.assertEqual({'content': 'foo', 'id': 'foo'}, obj.as1)
|
2023-07-01 17:02:01 +00:00
|
|
|
self.assertEqual('ui', obj.source_protocol)
|
2023-07-16 21:06:03 +00:00
|
|
|
self.assertEqual([user], obj.notify)
|
2023-07-01 17:02:01 +00:00
|
|
|
|
|
|
|
obj2 = Object.get_or_create('foo')
|
2023-07-02 01:45:18 +00:00
|
|
|
self.assertFalse(obj2.new)
|
|
|
|
self.assertFalse(obj2.changed)
|
2023-07-01 17:02:01 +00:00
|
|
|
check(obj, obj2)
|
|
|
|
check([obj2], Object.query().fetch())
|
|
|
|
|
|
|
|
# non-null **props should be populated
|
2023-07-02 01:45:18 +00:00
|
|
|
obj3 = Object.get_or_create('foo', our_as1={'content': 'bar'},
|
2023-07-16 21:06:03 +00:00
|
|
|
source_protocol=None, notify=[])
|
2023-07-01 17:02:01 +00:00
|
|
|
self.assertEqual('foo', obj3.key.id())
|
2023-07-09 23:29:22 +00:00
|
|
|
self.assertEqual({'content': 'bar', 'id': 'foo'}, obj3.as1)
|
2023-07-02 01:45:18 +00:00
|
|
|
self.assertEqual('ui', obj3.source_protocol)
|
2023-07-16 21:06:03 +00:00
|
|
|
self.assertEqual([user], obj3.notify)
|
2023-07-02 01:45:18 +00:00
|
|
|
self.assertFalse(obj3.new)
|
|
|
|
self.assertTrue(obj3.changed)
|
2023-07-01 17:02:01 +00:00
|
|
|
check([obj3], Object.query().fetch())
|
|
|
|
check(obj3, Object.get_by_id('foo'))
|
|
|
|
|
2023-07-02 01:45:18 +00:00
|
|
|
obj4 = Object.get_or_create('foo', our_as1={'content': 'bar'})
|
2023-07-09 23:29:22 +00:00
|
|
|
self.assertEqual({'content': 'bar', 'id': 'foo'}, obj4.as1)
|
2023-07-02 01:45:18 +00:00
|
|
|
self.assertFalse(obj4.new)
|
|
|
|
self.assertFalse(obj4.changed)
|
|
|
|
check(obj4, Object.get_by_id('foo'))
|
|
|
|
|
|
|
|
obj5 = Object.get_or_create('bar')
|
|
|
|
self.assertTrue(obj5.new)
|
|
|
|
self.assertIsNone(obj5.changed)
|
|
|
|
|
2023-07-16 21:06:03 +00:00
|
|
|
obj6 = Object.get_or_create('baz', notify=[ndb.Key(Web, 'other')])
|
2023-07-02 01:45:18 +00:00
|
|
|
self.assertTrue(obj6.new)
|
|
|
|
self.assertIsNone(obj6.changed)
|
|
|
|
|
2023-07-01 17:02:01 +00:00
|
|
|
self.assertEqual(3, Object.query().count())
|
|
|
|
|
2023-08-08 17:26:00 +00:00
|
|
|
# if no data property is set, don't clear existing data properties
|
|
|
|
obj7 = Object.get_or_create('biff', as2={'a': 'b'}, mf2={'c': 'd'})
|
|
|
|
Object.get_or_create('biff', users=[ndb.Key(Web, 'me')])
|
|
|
|
self.assert_object('biff', as2={'a': 'b'}, mf2={'c': 'd'},
|
|
|
|
users=[ndb.Key(Web, 'me')])
|
|
|
|
|
2023-07-01 21:24:18 +00:00
|
|
|
def test_activity_changed(self):
|
|
|
|
obj = Object()
|
|
|
|
self.assertFalse(obj.activity_changed(None))
|
|
|
|
self.assertFalse(obj.activity_changed({}))
|
|
|
|
self.assertTrue(obj.activity_changed({'content': 'x'}))
|
|
|
|
|
|
|
|
obj.our_as1 = {}
|
|
|
|
self.assertFalse(obj.activity_changed(None))
|
|
|
|
self.assertFalse(obj.activity_changed({}))
|
|
|
|
self.assertTrue(obj.activity_changed({'content': 'x'}))
|
|
|
|
|
|
|
|
obj.our_as1 = {'content': 'x'}
|
|
|
|
self.assertTrue(obj.activity_changed(None))
|
|
|
|
self.assertTrue(obj.activity_changed({}))
|
|
|
|
self.assertFalse(obj.activity_changed({'content': 'x'}))
|
|
|
|
|
|
|
|
obj.our_as1 = {'content': 'y'}
|
|
|
|
self.assertTrue(obj.activity_changed(None))
|
|
|
|
self.assertTrue(obj.activity_changed({}))
|
|
|
|
self.assertTrue(obj.activity_changed({'content': 'x'}))
|
|
|
|
|
2017-10-11 05:42:19 +00:00
|
|
|
def test_proxy_url(self):
|
2023-07-26 15:44:20 +00:00
|
|
|
obj = Object(id='abc', source_protocol='activitypub')
|
|
|
|
self.assertEqual('https://ap.brid.gy/convert/web/abc',
|
2023-05-24 23:31:42 +00:00
|
|
|
obj.proxy_url())
|
2023-01-19 06:20:15 +00:00
|
|
|
|
2023-05-24 23:31:42 +00:00
|
|
|
obj = Object(id='ab#c', source_protocol='ui')
|
2023-07-26 15:44:20 +00:00
|
|
|
self.assertEqual('https://fed.brid.gy/convert/web/ab%23c',
|
2023-05-24 23:31:42 +00:00
|
|
|
obj.proxy_url())
|
2023-04-05 23:23:49 +00:00
|
|
|
|
|
|
|
def test_put(self):
|
|
|
|
with self.assertRaises(AssertionError):
|
|
|
|
Object(id='x^^y').put()
|
|
|
|
|
|
|
|
def test_get_by_id(self):
|
|
|
|
self.assertIsNone(Object.get_by_id('abc'))
|
|
|
|
self.assertIsNone(Object.get_by_id('ab^^c'))
|
|
|
|
|
|
|
|
obj = Object(id='abc')
|
|
|
|
obj.put()
|
|
|
|
self.assertIsNotNone(obj, Object.get_by_id('abc'))
|
|
|
|
|
|
|
|
obj = Object(id='ab#c')
|
|
|
|
obj.put()
|
|
|
|
self.assert_entities_equal(obj, Object.get_by_id('ab^^c'))
|
|
|
|
|
2023-06-23 18:05:12 +00:00
|
|
|
def test_get_by_id_uses_cache(self):
|
|
|
|
obj = Object(id='foo', our_as1={'x': 'y'})
|
|
|
|
protocol.objects_cache['foo'] = obj
|
|
|
|
loaded = Fake.load('foo')
|
|
|
|
self.assert_entities_equal(obj, loaded)
|
|
|
|
|
|
|
|
# check that it's a separate copy of the entity in the cache
|
|
|
|
# https://github.com/snarfed/bridgy-fed/issues/558#issuecomment-1603203927
|
|
|
|
loaded.our_as1 = {'a': 'b'}
|
|
|
|
self.assertEqual({'x': 'y'}, Protocol.load('foo').our_as1)
|
|
|
|
|
|
|
|
def test_put_cached_makes_copy(self):
|
|
|
|
obj = Object(id='foo', our_as1={'x': 'y'})
|
|
|
|
obj.put()
|
|
|
|
obj.our_as1 = {'a': 'b'}
|
|
|
|
# don't put()
|
|
|
|
|
|
|
|
self.assertEqual({'x': 'y'}, Fake.load('foo').our_as1)
|
|
|
|
|
|
|
|
def test_get_by_id_cached_makes_copy(self):
|
|
|
|
obj = Object(id='foo', our_as1={'x': 'y'})
|
|
|
|
protocol.objects_cache['foo'] = obj
|
|
|
|
loaded = Fake.load('foo')
|
|
|
|
self.assert_entities_equal(obj, loaded)
|
|
|
|
|
|
|
|
# check that it's a separate copy of the entity in the cache
|
|
|
|
# https://github.com/snarfed/bridgy-fed/issues/558#issuecomment-1603203927
|
|
|
|
loaded.our_as1 = {'a': 'b'}
|
|
|
|
self.assertEqual({'x': 'y'}, Protocol.load('foo').our_as1)
|
|
|
|
|
2023-02-07 05:08:52 +00:00
|
|
|
def test_actor_link(self):
|
2023-03-20 21:28:14 +00:00
|
|
|
for expected, as2 in (
|
|
|
|
('href="">', {}),
|
|
|
|
('href="http://foo">foo', {'actor': 'http://foo'}),
|
|
|
|
('href="">Alice', {'actor': {'name': 'Alice'}}),
|
2023-04-01 03:27:28 +00:00
|
|
|
('href="http://foo/">Alice', {'actor': {
|
2023-03-20 21:28:14 +00:00
|
|
|
'name': 'Alice',
|
|
|
|
'url': 'http://foo',
|
|
|
|
}}),
|
|
|
|
("""\
|
|
|
|
title="Alice">
|
2023-04-01 03:27:28 +00:00
|
|
|
<img class="profile" src="http://pic/" />
|
2023-03-20 21:28:14 +00:00
|
|
|
Alice""", {'actor': {
|
2023-06-20 18:22:54 +00:00
|
|
|
'name': 'Alice',
|
|
|
|
'icon': {'type': 'Image', 'url': 'http://pic'},
|
|
|
|
}}),
|
2023-03-20 21:28:14 +00:00
|
|
|
):
|
2023-06-27 03:22:06 +00:00
|
|
|
with self.subTest(expected=expected, as2=as2):
|
|
|
|
obj = Object(id='x', as2=as2)
|
|
|
|
self.assert_multiline_in(expected, obj.actor_link())
|
2023-02-03 16:10:09 +00:00
|
|
|
|
2023-02-07 05:08:52 +00:00
|
|
|
def test_actor_link_user(self):
|
2023-06-16 04:22:20 +00:00
|
|
|
g.user = Fake(id='user.com', obj=Object(id='a', as2={"name": "Alice"}))
|
2023-06-09 19:56:45 +00:00
|
|
|
obj = Object(id='x', source_protocol='ui', users=[g.user.key])
|
2023-03-14 13:54:16 +00:00
|
|
|
self.assertIn(
|
2023-06-11 15:14:17 +00:00
|
|
|
'href="/fa/user.com"><img src="" class="profile"> Alice</a>',
|
2023-03-20 21:28:14 +00:00
|
|
|
obj.actor_link())
|
2023-02-07 05:08:52 +00:00
|
|
|
|
2023-03-29 20:13:32 +00:00
|
|
|
def test_put_updates_load_cache(self):
|
2023-02-24 13:25:29 +00:00
|
|
|
obj = Object(id='x', as2={})
|
2023-02-16 16:21:56 +00:00
|
|
|
obj.put()
|
2023-04-03 03:36:23 +00:00
|
|
|
self.assert_entities_equal(obj, protocol.objects_cache['x'])
|
2023-02-16 16:21:56 +00:00
|
|
|
|
2023-03-29 20:13:32 +00:00
|
|
|
def test_put_fragment_id_doesnt_update_load_cache(self):
|
2023-02-24 13:25:29 +00:00
|
|
|
obj = Object(id='x#y', as2={})
|
2023-02-16 20:20:21 +00:00
|
|
|
obj.put()
|
2023-04-03 03:36:23 +00:00
|
|
|
self.assertNotIn('x#y', protocol.objects_cache)
|
|
|
|
self.assertNotIn('x', protocol.objects_cache)
|
2023-02-16 20:20:21 +00:00
|
|
|
|
2023-02-25 03:59:12 +00:00
|
|
|
def test_computed_properties_without_as1(self):
|
|
|
|
Object(id='a').put()
|
|
|
|
|
2023-04-18 16:08:45 +00:00
|
|
|
def test_expire(self):
|
|
|
|
obj = Object(id='a', our_as1={'objectType': 'activity', 'verb': 'update'})
|
|
|
|
self.assertEqual(NOW + OBJECT_EXPIRE_AGE, obj.expire)
|
|
|
|
|
2023-03-28 04:51:18 +00:00
|
|
|
def test_put_adds_removes_activity_label(self):
|
|
|
|
obj = Object(id='x#y', our_as1={})
|
|
|
|
obj.put()
|
|
|
|
self.assertEqual([], obj.labels)
|
|
|
|
|
|
|
|
obj.our_as1 = {'objectType': 'activity'}
|
|
|
|
obj.put()
|
|
|
|
self.assertEqual(['activity'], obj.labels)
|
|
|
|
|
|
|
|
obj.labels = ['user']
|
|
|
|
obj.put()
|
|
|
|
self.assertEqual(['user', 'activity'], obj.labels)
|
|
|
|
|
|
|
|
obj.labels = ['activity', 'user']
|
|
|
|
obj.put()
|
|
|
|
self.assertEqual(['activity', 'user'], obj.labels)
|
|
|
|
|
|
|
|
obj.our_as1 = {'foo': 'bar'}
|
|
|
|
obj.put()
|
|
|
|
self.assertEqual(['user'], obj.labels)
|
|
|
|
|
2023-07-09 14:53:33 +00:00
|
|
|
def test_as_as2(self):
|
2023-07-09 23:29:22 +00:00
|
|
|
obj = Object()
|
2023-06-16 21:09:28 +00:00
|
|
|
self.assertEqual({}, obj.as_as2())
|
|
|
|
|
|
|
|
obj.our_as1 = {}
|
|
|
|
self.assertEqual({}, obj.as_as2())
|
|
|
|
|
|
|
|
obj.our_as1 = {
|
|
|
|
'objectType': 'person',
|
|
|
|
'foo': 'bar',
|
|
|
|
}
|
|
|
|
self.assertEqual({
|
|
|
|
'@context': 'https://www.w3.org/ns/activitystreams',
|
|
|
|
'type': 'Person',
|
|
|
|
'foo': 'bar',
|
|
|
|
}, obj.as_as2())
|
|
|
|
|
|
|
|
obj.as2 = {'baz': 'biff'}
|
|
|
|
self.assertEqual({'baz': 'biff'}, obj.as_as2())
|
|
|
|
|
2023-09-18 18:19:55 +00:00
|
|
|
def test_as1_from_as2(self):
|
2023-07-10 19:23:00 +00:00
|
|
|
self.assert_equals({
|
2023-07-09 14:53:33 +00:00
|
|
|
'objectType': 'person',
|
|
|
|
'id': 'https://mas.to/users/swentel',
|
|
|
|
'displayName': 'Mrs. ☕ Foo',
|
|
|
|
'image': [{'url': 'https://user.com/me.jpg'}],
|
|
|
|
'inbox': 'http://mas.to/inbox',
|
|
|
|
}, Object(as2=ACTOR).as1)
|
|
|
|
|
|
|
|
self.assertEqual({'foo': 'bar'}, Object(our_as1={'foo': 'bar'}).as1)
|
|
|
|
self.assertEqual({'id': 'x', 'foo': 'bar'},
|
|
|
|
Object(id='x', our_as1={'foo': 'bar'}).as1)
|
|
|
|
|
2023-09-18 18:19:55 +00:00
|
|
|
def test_as1_from_bsky(self):
|
|
|
|
like_bsky = {
|
|
|
|
'$type': 'app.bsky.feed.like',
|
|
|
|
'subject': {
|
|
|
|
'uri': 'http://example.com/original/post',
|
|
|
|
'cid': 'TODO',
|
|
|
|
},
|
|
|
|
}
|
|
|
|
like_as1 = {
|
|
|
|
'objectType': 'activity',
|
|
|
|
'verb': 'like',
|
|
|
|
'id': 'at://did:plc:foo/co.ll/123',
|
2023-09-18 18:52:17 +00:00
|
|
|
'actor': 'did:plc:foo',
|
2023-09-18 18:19:55 +00:00
|
|
|
'object': 'http://example.com/original/post',
|
|
|
|
}
|
|
|
|
|
|
|
|
# no user
|
|
|
|
obj = Object(id='at://did:plc:foo/co.ll/123', bsky=like_bsky)
|
|
|
|
self.assert_equals(like_as1, obj.as1)
|
|
|
|
|
2023-09-19 23:48:16 +00:00
|
|
|
# matching user without Object
|
|
|
|
user = self.make_user(id='fake:user', cls=Fake,
|
|
|
|
copies=[Target(uri='did:plc:foo', protocol='atproto')])
|
2023-09-18 18:19:55 +00:00
|
|
|
self.assertEqual({
|
|
|
|
**like_as1,
|
2023-09-19 23:48:16 +00:00
|
|
|
'actor': 'fake:user',
|
2023-09-18 18:19:55 +00:00
|
|
|
}, obj.as1)
|
|
|
|
|
2023-09-19 23:48:16 +00:00
|
|
|
# matching user with Object
|
|
|
|
user.obj = self.store_object(id='at://did:plc:foo/profile/self',
|
|
|
|
our_as1={'foo': 'bar'})
|
|
|
|
user.put()
|
2023-09-18 18:41:01 +00:00
|
|
|
self.assertEqual({
|
|
|
|
**like_as1,
|
|
|
|
'actor': {
|
2023-09-18 18:52:17 +00:00
|
|
|
'id': 'fake:user',
|
2023-09-19 23:48:16 +00:00
|
|
|
'foo': 'bar',
|
2023-09-18 18:41:01 +00:00
|
|
|
},
|
|
|
|
}, obj.as1)
|
|
|
|
|
2023-07-09 23:29:22 +00:00
|
|
|
def test_as1_from_mf2_uses_url_as_id(self):
|
|
|
|
obj = Object(mf2={
|
|
|
|
'properties': {
|
|
|
|
'url': ['x', 'y'],
|
|
|
|
'author': [{'properties': {'url': ['a', 'b']}}],
|
|
|
|
'repost-of': [{'properties': {'url': ['c', 'd']}}],
|
|
|
|
},
|
|
|
|
})
|
|
|
|
self.assertEqual('x', obj.as1['id'])
|
|
|
|
self.assertEqual('a', obj.as1['actor']['id'])
|
|
|
|
self.assertEqual('c', obj.as1['object']['id'])
|
|
|
|
|
|
|
|
obj = Object(mf2={
|
|
|
|
'properties': {
|
|
|
|
'author': ['a', 'b'],
|
|
|
|
'repost-of': ['c', 'd'],
|
|
|
|
},
|
|
|
|
})
|
|
|
|
self.assertNotIn('id', obj.as1)
|
|
|
|
self.assertNotIn('id', obj.as1['actor'])
|
|
|
|
self.assertEqual(['c', 'd'], obj.as1['object'])
|
|
|
|
|
2023-08-08 17:26:00 +00:00
|
|
|
def test_clear(self):
|
|
|
|
ab = {'a': 'b'}
|
|
|
|
obj = Object(our_as1=ab, as2=ab, mf2=ab, bsky=ab)
|
|
|
|
obj.clear()
|
|
|
|
self.assertIsNone(obj.our_as1)
|
|
|
|
self.assertIsNone(obj.as2)
|
|
|
|
self.assertIsNone(obj.mf2)
|
|
|
|
self.assertIsNone(obj.bsky)
|
|
|
|
|
2023-09-13 19:51:34 +00:00
|
|
|
def test_validate_id(self):
|
|
|
|
# DID repo ids
|
|
|
|
Object(id='at://did:plc:123/app.bsky.feed.post/abc').put()
|
|
|
|
Object(id='at://did:plc:foo.com/app.bsky.actor.profile/self').put()
|
|
|
|
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
# non-DID (bare handle) repo id
|
|
|
|
Object(id='at://foo.com/app.bsky.feed.post/abc').put()
|
|
|
|
|
2023-01-19 06:20:15 +00:00
|
|
|
|
2023-05-26 23:07:36 +00:00
|
|
|
class FollowerTest(TestCase):
|
2023-01-19 14:49:39 +00:00
|
|
|
|
|
|
|
def setUp(self):
|
|
|
|
super().setUp()
|
2023-06-06 21:50:20 +00:00
|
|
|
g.user = self.make_user('foo', cls=Fake)
|
|
|
|
self.other_user = self.make_user('bar', cls=Fake)
|
|
|
|
|
2023-06-14 03:58:28 +00:00
|
|
|
def test_from_to_same_type_fails(self):
|
|
|
|
with self.assertRaises(AssertionError):
|
2023-06-15 17:52:11 +00:00
|
|
|
Follower(from_=Web.key_for('foo.com'), to=Web.key_for('bar.com')).put()
|
2023-06-14 03:58:28 +00:00
|
|
|
|
|
|
|
with self.assertRaises(AssertionError):
|
2023-06-15 17:52:11 +00:00
|
|
|
Follower.get_or_create(from_=Web(id='foo.com'), to=Web(id='bar.com'))
|
2023-06-14 03:58:28 +00:00
|
|
|
|
2023-06-06 21:50:20 +00:00
|
|
|
def test_get_or_create(self):
|
|
|
|
follower = Follower.get_or_create(from_=g.user, to=self.other_user)
|
|
|
|
|
|
|
|
self.assertEqual(g.user.key, follower.from_)
|
|
|
|
self.assertEqual(self.other_user.key, follower.to)
|
|
|
|
self.assertEqual(1, Follower.query().count())
|
|
|
|
|
|
|
|
follower2 = Follower.get_or_create(from_=g.user, to=self.other_user)
|
|
|
|
self.assert_entities_equal(follower, follower2)
|
|
|
|
self.assertEqual(1, Follower.query().count())
|
|
|
|
|
|
|
|
Follower.get_or_create(to=g.user, from_=self.other_user)
|
|
|
|
Follower.get_or_create(from_=g.user, to=self.make_user('baz', cls=Fake))
|
|
|
|
self.assertEqual(3, Follower.query().count())
|
|
|
|
|
|
|
|
# check that kwargs get set on existing entity
|
|
|
|
follower = Follower.get_or_create(from_=g.user, to=self.other_user,
|
|
|
|
status='inactive')
|
|
|
|
got = follower.key.get()
|
|
|
|
self.assertEqual('inactive', got.status)
|