diff --git a/tests/unit/test_ap-gateway.py b/tests/unit/test_ap-gateway.py
index 0ed508a0..8fb1af24 100644
--- a/tests/unit/test_ap-gateway.py
+++ b/tests/unit/test_ap-gateway.py
@@ -17,35 +17,37 @@
# along with this program. If not, see .
from copy import deepcopy
-from unittest.mock import MagicMock, AsyncMock, patch, DEFAULT
-from urllib import parse
from functools import partial
-from typing import Dict, Union
+import io
+import json
+from typing import Any, Dict, Optional, Union
+from unittest.mock import AsyncMock, DEFAULT, MagicMock, patch
+from urllib import parse
import pytest
from pytest_twisted import ensureDeferred as ed
+from treq.response import _Response as TReqResponse
from twisted.internet import defer
+from twisted.web.server import Request
from twisted.words.protocols.jabber import jid
from twisted.words.protocols.jabber.error import StanzaError
-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 wokkel import pubsub, rsm
from sat.core import exceptions
from sat.core.constants import Const as C
+from sat.memory.sqla_mapping import SubscriptionState
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 import TYPE_ACTOR
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.tools.common import uri as xmpp_uri
-from sat.plugins.plugin_comp_ap_gateway import TYPE_ACTOR
-from sat.memory.sqla_mapping import SubscriptionState
+from sat.tools.utils import xmpp_date
TEST_BASE_URL = "https://example.org"
@@ -362,6 +364,13 @@ XMPP_ITEMS = [
for i in range(1, 5)
]
+TEST_USER_DATA = AP_REQUESTS[f"{TEST_BASE_URL}/users/{TEST_USER}"]
+OUTBOX_FIRST_PAGE = f"{TEST_BASE_URL}/users/{TEST_USER}/outbox?page=true"
+TEST_AP_ITEMS = AP_REQUESTS[OUTBOX_FIRST_PAGE]["orderedItems"]
+# request on an item ID must return the ID
+for item in TEST_AP_ITEMS:
+ AP_REQUESTS[item["id"]] = item
+
async def mock_ap_get(url):
return deepcopy(AP_REQUESTS[url])
@@ -649,11 +658,21 @@ class TestActivityPubGateway:
""
)
- def ap_request_params(self, ap_gateway, type_=None, url=None, query_data=None):
+ def ap_request_params(
+ self,
+ ap_gateway,
+ type_: Optional[str] = None,
+ url: Optional[str] = None,
+ doc: Optional[Any] = None,
+ query_data: Optional[dict] = None,
+ signing_actor: Optional[str] = None
+ ) -> Dict[str, Any]:
"""Generate parameters for HTTPAPGServer's AP*Request
@param type_: one of the AP query type (e.g. "outbox")
@param url: URL to query (mutually exclusif with type_)
+ @param doc: if set, a document to embed in content of the request (will be
+ converted to JSON)
@param query_data: query data as returned by parse.parse_qs
@return dict with kw params to use
"""
@@ -676,6 +695,9 @@ class TestActivityPubGateway:
request = Request(MagicMock())
request.path = path.encode()
request.uri = uri.encode()
+ if doc is not None:
+ request.content = io.BytesIO(json.dumps(doc).encode())
+
ap_url = parse.urljoin(
f"https://{ap_gateway.public_url}",
path
@@ -686,7 +708,7 @@ class TestActivityPubGateway:
"node": None,
"ap_account": test_jid.full(),
"ap_url": ap_url,
- "signing_actor": None
+ "signing_actor": signing_actor
}
if type_ == "outbox" and query_data:
kwargs["query_data"] = query_data
@@ -1350,3 +1372,220 @@ class TestActivityPubGateway:
"name": f"{TEST_AP_ACCOUNT}"
}
assert expected_mention in ap_object["tag"]
+
+ @ed
+ async def test_xmpp_repeat_to_ap_announce(self, ap_gateway, monkeypatch):
+ """XEP-0272 post repeat is converted to AP Announce 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)
+
+ # JID repeated AP actor (also the recipient of the message)
+ recipient_jid = ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT)
+ # repeated item
+ ap_item = TEST_AP_ITEMS[0]
+ ap_item_url = xmpp_uri.buildXMPPUri(
+ "pubsub",
+ path=recipient_jid.full(),
+ node=ap_gateway._m.namespace,
+ item=ap_item["id"]
+ )
+ item_elt = xml_tools.parse(f"""
+ -
+
+ test message 1
+
+
+
+
+ test_user
+ xmpp:{recipient_jid}
+
+ 2022-07-21T14:38:53Z
+ 2022-07-21T14:38:53Z
+ {ap_item["id"]}
+
+
+
+ """)
+ item_elt.uri = pubsub.NS_PUBSUB_EVENT
+
+ with patch.object(ap_gateway, "signAndPost") as signAndPost:
+ signAndPost.return_value.code = 202
+ await ap_gateway.convertAndPostItems(
+ ap_gateway.client,
+ TEST_AP_ACCOUNT,
+ TEST_JID,
+ ap_gateway._m.namespace,
+ [item_elt]
+ )
+
+ assert signAndPost.called
+ url, actor_id, doc = signAndPost.call_args.args
+ assert url == TEST_USER_DATA["inbox"]
+ assert actor_id == ap_gateway.buildAPURL(ap_const.TYPE_ACTOR, TEST_JID.userhost())
+ assert doc["type"] == "Announce"
+ assert ap_const.NS_AP_PUBLIC in doc["to"]
+ assert doc["object"] == ap_item["id"]
+
+ @ed
+ async def test_ap_announce_to_xmpp_repeat(self, ap_gateway, monkeypatch):
+ """AP Announce activity is converted to XEP-0272 post repeat"""
+ 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)
+
+ xmpp_actor_id = ap_gateway.buildAPURL(ap_const.TYPE_ACTOR, TEST_JID.userhost())
+ # announced item
+ xmpp_item = XMPP_ITEMS[0]
+ xmpp_item_url = ap_gateway.buildAPURL(
+ ap_const.TYPE_ITEM, TEST_JID.userhost(), xmpp_item["id"]
+ )
+ announce = {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "type": "Announce",
+ "actor": TEST_AP_ACTOR_ID,
+ "cc": [
+ xmpp_actor_id,
+ TEST_USER_DATA["followers"]
+ ],
+ "id": "https://example.org/announce/123",
+ "object": xmpp_item_url,
+ "published": "2022-07-22T09:24:12Z",
+ "to": [ap_const.NS_AP_PUBLIC]
+ }
+ with patch.object(ap_gateway.host.memory.storage, "getItems") as getItems:
+ mock_pubsub_item = MagicMock
+ mock_pubsub_item.data = xmpp_item
+ getItems.return_value = ([mock_pubsub_item], {})
+ with patch.object(
+ ap_gateway.host.memory.storage,
+ "cachePubsubItems") as cachePubsubItems:
+ await ap_gateway.server.resource.handleAnnounceActivity(
+ Request(MagicMock()),
+ announce,
+ None,
+ None,
+ None,
+ "",
+ TEST_AP_ACTOR_ID
+ )
+
+ assert cachePubsubItems.called
+ # the microblog data put in cache correspond to the item sent to subscribers
+ __, __, __, [mb_data] = cachePubsubItems.call_args.args
+ extra = mb_data["extra"]
+ assert "repeated" in extra
+ repeated = extra["repeated"]
+ assert repeated["by"] == ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT).full()
+ xmpp_item_xmpp_url = xmpp_uri.buildXMPPUri(
+ "pubsub",
+ path=TEST_JID.full(),
+ node=ap_gateway._m.namespace,
+ item=xmpp_item["id"]
+ )
+ assert repeated["uri"] == xmpp_item_xmpp_url
+
+ @ed
+ async def test_xmpp_attachment_noticed_to_ap_like(self, ap_gateway, monkeypatch):
+ """Pubsub-attachments ``noticed`` is converted to AP Like 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)
+
+ recipient_jid = ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT)
+ # noticed item
+ ap_item = TEST_AP_ITEMS[0]
+ ap_item_url = xmpp_uri.buildXMPPUri(
+ "pubsub",
+ path=recipient_jid.full(),
+ node=ap_gateway._m.namespace,
+ item=ap_item["id"]
+ )
+ attachment_node = ap_gateway._pa.getAttachmentNodeName(
+ recipient_jid,
+ ap_gateway._m.namespace,
+ ap_item["id"]
+ )
+ item_elt = xml_tools.parse(f"""
+ -
+
+
+
+
+ """)
+ item_elt.uri = pubsub.NS_PUBSUB_EVENT
+ items_event = pubsub.ItemsEvent(
+ TEST_JID,
+ recipient_jid,
+ attachment_node,
+ [item_elt],
+ {}
+ )
+
+ with patch.object(ap_gateway, "signAndPost") as signAndPost:
+ signAndPost.return_value.code = 202
+ await ap_gateway._itemsReceived(
+ ap_gateway.client,
+ items_event
+ )
+
+ assert signAndPost.called
+ url, actor_id, doc = signAndPost.call_args.args
+ assert url == TEST_USER_DATA["inbox"]
+ assert actor_id == ap_gateway.buildAPURL(ap_const.TYPE_ACTOR, TEST_JID.userhost())
+ assert doc["type"] == "Like"
+ assert ap_const.NS_AP_PUBLIC in doc["to"]
+ assert doc["object"] == ap_item["id"]
+
+ @ed
+ async def test_ap_like_to_xmpp_noticed_attachment(self, ap_gateway, monkeypatch):
+ """AP Like activity is converted to ``noticed`` attachment"""
+ 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)
+
+ xmpp_actor_id = ap_gateway.buildAPURL(ap_const.TYPE_ACTOR, TEST_JID.userhost())
+ # liked item
+ xmpp_item = XMPP_ITEMS[0]
+ xmpp_item_url = ap_gateway.buildAPURL(
+ ap_const.TYPE_ITEM, TEST_JID.userhost(), xmpp_item["id"]
+ )
+ like = {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "type": "Like",
+ "actor": TEST_AP_ACTOR_ID,
+ "cc": [
+ xmpp_actor_id,
+ TEST_USER_DATA["followers"]
+ ],
+ "id": "https://example.org/like/123",
+ "object": xmpp_item_url,
+ "published": "2022-07-22T09:24:12Z",
+ "to": [ap_const.NS_AP_PUBLIC]
+ }
+ with patch.object(ap_gateway.host.memory.storage, "getItems") as getItems:
+ getItems.return_value = ([], {})
+ with patch.object(ap_gateway._p, "sendItems") as sendItems:
+ await ap_gateway.server.resource.APInboxRequest(
+ **self.ap_request_params(
+ ap_gateway,
+ "inbox",
+ doc=like,
+ signing_actor=TEST_AP_ACTOR_ID
+ )
+ )
+
+ assert sendItems.called
+ si_client, si_service, si_node, [si_item] = sendItems.call_args.args
+ assert si_client.jid == ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT)
+ assert si_service == TEST_JID
+ assert si_node == ap_gateway._pa.getAttachmentNodeName(
+ TEST_JID, ap_gateway._m.namespace, xmpp_item["id"]
+ )
+ [parsed_item] = ap_gateway._pa.items2attachmentData(si_client, [si_item])
+ assert parsed_item["from"] == si_client.jid.full()
+ assert "noticed" in parsed_item
+ assert parsed_item["noticed"]["noticed"] == True