tests (unit/ap gateway): message/item retractation tests:

this patch adds 4 tests to check pubsub <=> AP item retractation and message <=> AP item
retractation.

rel 367
This commit is contained in:
Goffi 2022-06-17 15:50:34 +02:00
commit 57c945dc9f

View file

@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from copy import deepcopy
from unittest.mock import MagicMock, AsyncMock, patch
from unittest.mock import MagicMock, AsyncMock, patch, DEFAULT
from urllib import parse
from functools import partial
@ -28,6 +28,7 @@ from twisted.words.protocols.jabber import jid
from twisted.web.server import Request
from twisted.words.xish import domish
from wokkel import rsm, pubsub
from treq.response import _Response as TReqResponse
from sat.core import exceptions
from sat.core.constants import Const as C
@ -35,10 +36,13 @@ from sat.plugins import plugin_comp_ap_gateway
from sat.plugins.plugin_comp_ap_gateway import constants as ap_const
from sat.plugins.plugin_comp_ap_gateway.http_server import HTTPServer
from sat.plugins.plugin_xep_0277 import NS_ATOM
from sat.plugins.plugin_xep_0422 import NS_FASTEN
from sat.plugins.plugin_xep_0424 import NS_MESSAGE_RETRACT
from sat.plugins.plugin_xep_0465 import NS_PPS
from sat.tools.utils import xmpp_date
from sat.tools import xml_tools
from sat.plugins.plugin_comp_ap_gateway import TYPE_ACTOR
from sat.memory.sqla_mapping import SubscriptionState
TEST_BASE_URL = "https://example.org"
@ -380,6 +384,10 @@ def getVirtualClient(jid):
return client
class FakeTReqPostResponse:
code = 202
@pytest.fixture(scope="session")
def ap_gateway(host):
gateway = plugin_comp_ap_gateway.APGateway(host)
@ -886,3 +894,197 @@ class TestActivityPubGateway:
assert sendMessage.called
assert sendMessage.call_args.args[0] == TEST_JID
assert sendMessage.call_args.args[1] == {"": "test direct message"}
@ed
async def test_pubsub_retract_to_ap_delete(self, ap_gateway, monkeypatch):
"""Pubsub retract requests are converted to AP delete activity"""
monkeypatch.setattr(plugin_comp_ap_gateway.treq, "get", mock_ap_get)
monkeypatch.setattr(plugin_comp_ap_gateway.treq, "json_content", mock_treq_json)
monkeypatch.setattr(ap_gateway, "apGet", mock_ap_get)
retract_id = "retract_123"
retract_elt = domish.Element((pubsub.NS_PUBSUB_EVENT, "retract"))
retract_elt["id"] = retract_id
items_event = pubsub.ItemsEvent(
sender=TEST_JID,
recipient=ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT),
nodeIdentifier=ap_gateway._m.namespace,
items=[retract_elt],
headers={}
)
with patch.object(ap_gateway, "signAndPost") as signAndPost:
signAndPost.return_value = FakeTReqPostResponse()
# we simulate the reception of a retract event
await ap_gateway._itemsReceived(ap_gateway.client, items_event)
url, actor_id, doc = signAndPost.call_args[0]
jid_account = await ap_gateway.getAPAccountFromJidAndNode(TEST_JID, None)
jid_actor_id = ap_gateway.buildAPURL(ap_const.TYPE_ACTOR, jid_account)
assert url == f"{TEST_BASE_URL}/users/{TEST_USER}/inbox"
assert actor_id == jid_actor_id
assert doc["type"] == "Delete"
assert doc["actor"] == jid_actor_id
obj = doc["object"]
assert obj["type"] == ap_const.TYPE_TOMBSTONE
url_item_id = ap_gateway.buildAPURL(ap_const.TYPE_ITEM, jid_account, retract_id)
assert obj["id"] == url_item_id
@ed
async def test_ap_delete_to_pubsub_retract(self, ap_gateway):
"""AP delete activity is converted to pubsub retract"""
client = ap_gateway.client.getVirtualClient(
ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT)
)
ap_item = {
"@context": "https://www.w3.org/ns/activitystreams",
"actor": TEST_AP_ACTOR_ID,
"id": "https://test.example/retract_123",
"type": "Delete",
"object": {"id": f"{TEST_AP_ACTOR_ID}/item/123",
"type": "Tombstone"},
"to": ["https://www.w3.org/ns/activitystreams#Public"]
}
with patch.multiple(
ap_gateway.host.memory.storage,
get=DEFAULT,
getPubsubNode=DEFAULT,
deletePubsubItems=DEFAULT,
) as mock_objs:
mock_objs["get"].return_value=None
cached_node = MagicMock()
mock_objs["getPubsubNode"].return_value=cached_node
subscription = MagicMock()
subscription.state = SubscriptionState.SUBSCRIBED
subscription.subscriber = TEST_JID
cached_node.subscriptions = [subscription]
with patch.object(
ap_gateway.pubsub_service, "notifyRetract"
) as notifyRetract:
# we simulate a received Delete activity
await ap_gateway.newAPDeleteItem(
client=client,
destinee=None,
node=ap_gateway._m.namespace,
item=ap_item
)
# item is deleted from database
deletePubsubItems = mock_objs["deletePubsubItems"]
assert deletePubsubItems.call_count == 1
assert deletePubsubItems.call_args.args[1] == [ap_item["id"]]
# retraction notification is sent to subscribers
assert notifyRetract.call_count == 1
assert notifyRetract.call_args.args[0] == client.jid
assert notifyRetract.call_args.args[1] == ap_gateway._m.namespace
notifications = notifyRetract.call_args.args[2]
assert len(notifications) == 1
subscriber, __, item_elts = notifications[0]
assert subscriber == TEST_JID
assert len(item_elts) == 1
item_elt = item_elts[0]
assert isinstance(item_elt, domish.Element)
assert item_elt.name == "item"
assert item_elt["id"] == ap_item["id"]
@ed
async def test_message_retract_to_ap_delete(self, ap_gateway, monkeypatch):
"""Message retract requests are converted to AP delete activity"""
monkeypatch.setattr(plugin_comp_ap_gateway.treq, "get", mock_ap_get)
monkeypatch.setattr(plugin_comp_ap_gateway.treq, "json_content", mock_treq_json)
monkeypatch.setattr(ap_gateway, "apGet", mock_ap_get)
# origin ID is the ID of the message to retract
origin_id = "mess_retract_123"
# we call retractByOriginId to get the message element of a retraction request
fake_client = MagicMock()
fake_client.jid = TEST_JID
dest_jid = ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT)
ap_gateway._r.retractByOriginId(fake_client, dest_jid, origin_id)
# message_retract_elt is the message which would be sent for a retraction
message_retract_elt = fake_client.send.call_args.args[0]
apply_to_elt = next(message_retract_elt.elements(NS_FASTEN, "apply-to"))
retract_elt = apply_to_elt.retract
with patch.object(ap_gateway, "signAndPost") as signAndPost:
signAndPost.return_value = FakeTReqPostResponse()
fake_fastened_elts = MagicMock()
fake_fastened_elts.id = origin_id
# we simulate the reception of a retract event using the message element that
# we generated above
await ap_gateway._onMessageRetract(
ap_gateway.client,
message_retract_elt,
retract_elt,
fake_fastened_elts
)
url, actor_id, doc = signAndPost.call_args[0]
# the AP delete activity must have been sent through signAndPost
# we check its values
jid_account = await ap_gateway.getAPAccountFromJidAndNode(TEST_JID, None)
jid_actor_id = ap_gateway.buildAPURL(ap_const.TYPE_ACTOR, jid_account)
assert url == f"{TEST_BASE_URL}/users/{TEST_USER}/inbox"
assert actor_id == jid_actor_id
assert doc["type"] == "Delete"
assert doc["actor"] == jid_actor_id
obj = doc["object"]
assert obj["type"] == ap_const.TYPE_TOMBSTONE
url_item_id = ap_gateway.buildAPURL(ap_const.TYPE_ITEM, jid_account, origin_id)
assert obj["id"] == url_item_id
@ed
async def test_ap_delete_to_message_retract(self, ap_gateway, monkeypatch):
"""AP delete activity is converted to message retract"""
# note: a message retract is used when suitable message is found in history,
# otherwise it should be in pubsub cache and it's a pubsub retract (tested above
# by ``test_ap_delete_to_pubsub_retract``)
# we don't want actual queries in database
retractDBHistory = AsyncMock()
monkeypatch.setattr(ap_gateway._r, "retractDBHistory", retractDBHistory)
client = ap_gateway.client.getVirtualClient(
ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT)
)
fake_send = MagicMock()
monkeypatch.setattr(client, "send", fake_send)
ap_item = {
"@context": "https://www.w3.org/ns/activitystreams",
"actor": TEST_AP_ACTOR_ID,
"id": "https://test.example/retract_123",
"type": "Delete",
"object": {"id": f"{TEST_AP_ACTOR_ID}/item/123",
"type": "Tombstone"},
"to": ["https://www.w3.org/ns/activitystreams#Public"]
}
with patch.object(ap_gateway.host.memory.storage, "get") as storage_get:
fake_history = MagicMock()
fake_history.source_jid = client.jid
fake_history.dest_jid = TEST_JID
fake_history.origin_id = ap_item["id"]
storage_get.return_value = fake_history
# we simulate a received Delete activity
await ap_gateway.newAPDeleteItem(
client=client,
destinee=None,
node=ap_gateway._m.namespace,
item=ap_item
)
# item is deleted from database
assert retractDBHistory.call_count == 1
assert retractDBHistory.call_args.args[0] == client
assert retractDBHistory.call_args.args[1] == fake_history
# retraction notification is sent to destinee
assert fake_send.call_count == 1
sent_elt = fake_send.call_args.args[0]
assert sent_elt.name == "message"
assert sent_elt["from"] == client.jid.full()
assert sent_elt["to"] == TEST_JID.full()
apply_to_elt = next(sent_elt.elements(NS_FASTEN, "apply-to"))
assert apply_to_elt["id"] == ap_item["id"]
retract_elt = apply_to_elt.retract
assert retract_elt is not None
assert retract_elt.uri == NS_MESSAGE_RETRACT