Add federation test case for note deletion
This commit is contained in:
parent
64c1496c57
commit
bba598be66
3 changed files with 68 additions and 5 deletions
|
@ -119,7 +119,7 @@ class BaseActivity(object):
|
|||
actor = self._validate_person(actor)
|
||||
self._data['actor'] = actor
|
||||
else:
|
||||
if not self.NO_CONTEXT:
|
||||
if not self.NO_CONTEXT and self.ACTIVITY_TYPE != ActivityType.TOMBSTONE:
|
||||
actor = ID
|
||||
self._data['actor'] = actor
|
||||
|
||||
|
@ -299,6 +299,7 @@ class BaseActivity(object):
|
|||
self.verify()
|
||||
actor = self.get_actor()
|
||||
|
||||
# Check for Block activity
|
||||
if DB.outbox.find_one({'type': ActivityType.BLOCK.value,
|
||||
'activity.object': actor.id,
|
||||
'meta.undo': False}):
|
||||
|
@ -415,6 +416,9 @@ class BaseActivity(object):
|
|||
def build_undo(self) -> 'BaseActivity':
|
||||
raise NotImplementedError
|
||||
|
||||
def build_delete(self) -> 'BaseActivity':
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Person(BaseActivity):
|
||||
ACTIVITY_TYPE = ActivityType.PERSON
|
||||
|
@ -645,6 +649,7 @@ class Announce(BaseActivity):
|
|||
'$inc': {'meta.count_boost': 1},
|
||||
'$addToSet': {'meta.col_shares': self.to_dict(embed=True, embed_object_id_only=True)},
|
||||
})
|
||||
|
||||
def _undo_inbox(self) -> None:
|
||||
obj = self.get_object()
|
||||
# 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]
|
||||
|
||||
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}})
|
||||
# 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
|
||||
|
||||
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}})
|
||||
|
||||
|
||||
|
@ -870,6 +878,9 @@ class Note(BaseActivity):
|
|||
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 = {
|
||||
ActivityType.IMAGE: Image,
|
||||
|
@ -946,7 +957,7 @@ def build_inbox_json_feed(path: str, request_cursor: Optional[str] = None) -> Di
|
|||
data = []
|
||||
cursor = None
|
||||
|
||||
q: Dict[str, Any] = {'type': 'Create'}
|
||||
q: Dict[str, Any] = {'type': 'Create', 'meta.deleted': False}
|
||||
if request_cursor:
|
||||
q['_id'] = {'$lt': request_cursor}
|
||||
|
||||
|
|
16
app.py
16
app.py
|
@ -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')
|
||||
@api_required
|
||||
def api_boost():
|
||||
# FIXME(tsileo): ensure a Note and not a Create is given
|
||||
oid = request.args.get('id')
|
||||
obj = activitypub.parse_activity(OBJECT_SERVICE.get(oid))
|
||||
announce = obj.build_announce()
|
||||
|
|
|
@ -82,6 +82,13 @@ class Instance(object):
|
|||
time.sleep(self._create_delay)
|
||||
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:
|
||||
resp = self.session.get(f'{self.host_url}/api/undo', params={'id': oid})
|
||||
assert resp.status_code == 201
|
||||
|
@ -203,6 +210,35 @@ def test_post_content():
|
|||
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():
|
||||
instance1, instance2 = _instances()
|
||||
# Instance1 follows instance2
|
||||
|
|
Loading…
Reference in a new issue