mirror of
https://git.sr.ht/~tsileo/microblog.pub
synced 2024-11-15 03:04:28 +00:00
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)
|
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
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')
|
@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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue