Add federation test case for note deletion

This commit is contained in:
Thomas Sileo 2018-05-28 22:38:48 +02:00
parent 64c1496c57
commit bba598be66
3 changed files with 68 additions and 5 deletions

View file

@ -119,7 +119,7 @@ class BaseActivity(object):
actor = self._validate_person(actor) actor = self._validate_person(actor)
self._data['actor'] = actor self._data['actor'] = actor
else: else:
if not self.NO_CONTEXT: if not self.NO_CONTEXT and self.ACTIVITY_TYPE != ActivityType.TOMBSTONE:
actor = ID actor = ID
self._data['actor'] = actor self._data['actor'] = actor
@ -299,6 +299,7 @@ class BaseActivity(object):
self.verify() self.verify()
actor = self.get_actor() actor = self.get_actor()
# Check for Block activity
if DB.outbox.find_one({'type': ActivityType.BLOCK.value, if DB.outbox.find_one({'type': ActivityType.BLOCK.value,
'activity.object': actor.id, 'activity.object': actor.id,
'meta.undo': False}): 'meta.undo': False}):
@ -415,6 +416,9 @@ class BaseActivity(object):
def build_undo(self) -> 'BaseActivity': def build_undo(self) -> 'BaseActivity':
raise NotImplementedError raise NotImplementedError
def build_delete(self) -> 'BaseActivity':
raise NotImplementedError
class Person(BaseActivity): class Person(BaseActivity):
ACTIVITY_TYPE = ActivityType.PERSON ACTIVITY_TYPE = ActivityType.PERSON
@ -645,6 +649,7 @@ class Announce(BaseActivity):
'$inc': {'meta.count_boost': 1}, '$inc': {'meta.count_boost': 1},
'$addToSet': {'meta.col_shares': self.to_dict(embed=True, embed_object_id_only=True)}, '$addToSet': {'meta.col_shares': self.to_dict(embed=True, embed_object_id_only=True)},
}) })
def _undo_inbox(self) -> None: def _undo_inbox(self) -> None:
obj = self.get_object() obj = self.get_object()
# Update the meta counter if the object is published by the server # Update the meta counter if the object is published by the server
@ -683,14 +688,17 @@ class Delete(BaseActivity):
ALLOWED_OBJECT_TYPES = [ActivityType.NOTE, ActivityType.TOMBSTONE] ALLOWED_OBJECT_TYPES = [ActivityType.NOTE, ActivityType.TOMBSTONE]
def _recipients(self) -> List[str]: def _recipients(self) -> List[str]:
return self.get_object().recipients() obj = self.get_object()
if obj.type_enum == ActivityType.TOMBSTONE:
obj = parse_activity(OBJECT_SERVICE.get(obj.id))
return obj._recipients()
def _process_from_inbox(self): def _process_from_inbox(self) -> None:
DB.inbox.update_one({'activity.object.id': self.get_object().id}, {'$set': {'meta.deleted': True}}) DB.inbox.update_one({'activity.object.id': self.get_object().id}, {'$set': {'meta.deleted': True}})
# TODO(tsileo): also delete copies stored in parents' `meta.replies` # TODO(tsileo): also delete copies stored in parents' `meta.replies`
# TODO(tsileo): also purge the cache if it's a reply of a published activity # TODO(tsileo): also purge the cache if it's a reply of a published activity
def _post_to_outbox(self, obj_id, activity, recipients): def _post_to_outbox(self, obj_id: str, activity: ObjectType, recipients: List[str]) -> None:
DB.outbox.update_one({'activity.object.id': self.get_object().id}, {'$set': {'meta.deleted': True}}) DB.outbox.update_one({'activity.object.id': self.get_object().id}, {'$set': {'meta.deleted': True}})
@ -870,6 +878,9 @@ class Note(BaseActivity):
published=datetime.utcnow().replace(microsecond=0).isoformat() + 'Z', published=datetime.utcnow().replace(microsecond=0).isoformat() + 'Z',
) )
def build_delete(self) -> BaseActivity:
return Delete(object=Tombstone(id=self.id).to_dict(embed=True))
_ACTIVITY_TYPE_TO_CLS = { _ACTIVITY_TYPE_TO_CLS = {
ActivityType.IMAGE: Image, ActivityType.IMAGE: Image,
@ -946,7 +957,7 @@ def build_inbox_json_feed(path: str, request_cursor: Optional[str] = None) -> Di
data = [] data = []
cursor = None cursor = None
q: Dict[str, Any] = {'type': 'Create'} q: Dict[str, Any] = {'type': 'Create', 'meta.deleted': False}
if request_cursor: if request_cursor:
q['_id'] = {'$lt': request_cursor} q['_id'] = {'$lt': request_cursor}

16
app.py
View file

@ -622,9 +622,25 @@ def notifications():
) )
@app.route('/api/delete')
@api_required
def api_delete():
# FIXME(tsileo): ensure a Note and not a Create is given
oid = request.args.get('id')
obj = activitypub.parse_activity(OBJECT_SERVICE.get(oid))
delete = obj.build_delete()
delete.post_to_outbox()
if request.args.get('redirect'):
return redirect(request.args.get('redirect'))
return Response(
status=201,
headers={'Microblogpub-Created-Activity': delete.id},
)
@app.route('/api/boost') @app.route('/api/boost')
@api_required @api_required
def api_boost(): def api_boost():
# FIXME(tsileo): ensure a Note and not a Create is given
oid = request.args.get('id') oid = request.args.get('id')
obj = activitypub.parse_activity(OBJECT_SERVICE.get(oid)) obj = activitypub.parse_activity(OBJECT_SERVICE.get(oid))
announce = obj.build_announce() announce = obj.build_announce()

View file

@ -82,6 +82,13 @@ class Instance(object):
time.sleep(self._create_delay) time.sleep(self._create_delay)
return resp.headers.get('microblogpub-created-activity') return resp.headers.get('microblogpub-created-activity')
def delete(self, oid: str) -> None:
resp = self.session.get(f'{self.host_url}/api/delete', params={'id': oid})
assert resp.status_code == 201
time.sleep(self._create_delay)
return resp.headers.get('microblogpub-created-activity')
def undo(self, oid: str) -> None: def undo(self, oid: str) -> None:
resp = self.session.get(f'{self.host_url}/api/undo', params={'id': oid}) resp = self.session.get(f'{self.host_url}/api/undo', params={'id': oid})
assert resp.status_code == 201 assert resp.status_code == 201
@ -203,6 +210,35 @@ def test_post_content():
assert inbox_stream['items'][0]['id'] == create_id assert inbox_stream['items'][0]['id'] == create_id
def test_post_content_and_delete():
instance1, instance2 = _instances()
# Instance1 follows instance2
instance1.follow(instance2)
instance2.follow(instance1)
inbox_stream = instance2.stream_jsonfeed()
assert len(inbox_stream['items']) == 0
create_id = instance1.new_note('hello')
instance2_debug = instance2.debug()
assert instance2_debug['inbox'] == 3 # An Follow, Accept and Create activity should be there
instance2_debug['outbox'] == 2 # We've sent a Accept and a Follow activity
# Ensure the post is visible in instance2's stream
inbox_stream = instance2.stream_jsonfeed()
assert len(inbox_stream['items']) == 1
assert inbox_stream['items'][0]['id'] == create_id
instance1.delete(f'{create_id}/activity')
instance2_debug = instance2.debug()
assert instance2_debug['inbox'] == 4 # An Follow, Accept and Create and Delete activity should be there
instance2_debug['outbox'] == 2 # We've sent a Accept and a Follow activity
# Ensure the post has been delete from instance2's stream
inbox_stream = instance2.stream_jsonfeed()
assert len(inbox_stream['items']) == 0
def test_post_content_and_like(): def test_post_content_and_like():
instance1, instance2 = _instances() instance1, instance2 = _instances()
# Instance1 follows instance2 # Instance1 follows instance2