diff tests/unit/test_ap-gateway.py @ 3871:8c01d8ab9447

tests (unit/AP gateway): tests for item repeat/announce and noticed/like conversion: Add tests for - XMPP blog post repeat => AP Announce conversion and vice versa - XMPP pubsub `noticed` attachment => AP like conversion and vice versa rel 370
author Goffi <goffi@goffi.org>
date Fri, 22 Jul 2022 15:45:33 +0200
parents 56720561f45f
children c129234a5d0b
line wrap: on
line diff
--- a/tests/unit/test_ap-gateway.py	Fri Jul 22 15:42:59 2022 +0200
+++ b/tests/unit/test_ap-gateway.py	Fri Jul 22 15:45:33 2022 +0200
@@ -17,35 +17,37 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from copy import deepcopy
-from unittest.mock import MagicMock, AsyncMock, patch, DEFAULT
+from functools import partial
+import io
+import json
+from typing import Any, Dict, Optional, Union
+from unittest.mock import AsyncMock, DEFAULT, MagicMock, patch
 from urllib import parse
-from functools import partial
-from typing import Dict, Union
 
 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 @@
     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 @@
             "</title>"
         )
 
-    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 @@
         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 @@
             "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 @@
             "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"""
+            <item id="123" publisher="{TEST_JID}/res.123">
+              <entry xmlns="http://www.w3.org/2005/Atom">
+                <title type="text">test message 1</title>
+                <title type="xhtml">
+                  <div xmlns="http://www.w3.org/1999/xhtml">
+                    <p>test message 1</p>
+                  </div>
+                </title>
+                <author>
+                  <name>test_user</name>
+                  <uri>xmpp:{recipient_jid}</uri>
+                </author>
+                <updated>2022-07-21T14:38:53Z</updated>
+                <published>2022-07-21T14:38:53Z</published>
+                <id>{ap_item["id"]}</id>
+                <link href="{ap_item_url}" rel="via"/>
+              </entry>
+            </item>
+        """)
+        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 id="{TEST_JID.userhost()}" published="{TEST_JID.userhostJID()}">
+              <attachments xmlns="urn:xmpp:pubsub-attachments:0">
+                <noticed timestamp="2022-07-22T12:29:45Z"/>
+              </attachments>
+            </item>
+        """)
+        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