# HG changeset patch # User Goffi # Date 1657462509 -7200 # Node ID 56720561f45f1e6ceeb34912a1ebb2ffed5d0f77 # Parent e183f1fbfa8d83bcd27df3ab98302c1da8d30e14 test (unit/AP gateway): tests for XMPP reference/mention <=> AP mention conversion: this patch add 5 tests: - 2 to mention AP mention to XMPP conversion, with direct addressing or using "tag" field - 2 to test automatic mention parsing of body - 1 to test XEP-0372 Reference to AP mention conversion rel 369 diff -r e183f1fbfa8d -r 56720561f45f tests/unit/test_ap-gateway.py --- a/tests/unit/test_ap-gateway.py Sun Jul 10 16:15:09 2022 +0200 +++ b/tests/unit/test_ap-gateway.py Sun Jul 10 16:15:09 2022 +0200 @@ -20,6 +20,7 @@ from unittest.mock import MagicMock, AsyncMock, patch, DEFAULT from urllib import parse from functools import partial +from typing import Dict, Union import pytest from pytest_twisted import ensureDeferred as ed @@ -42,6 +43,7 @@ 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 @@ -391,6 +393,16 @@ return ret_items, {"rsm": rsm_resp.toDict(), "complete": True} +async def mock_getPubsubNode(client, service, node, with_subscriptions=False, **kwargs): + """Mock storage's getPubsubNode + + return an MagicMock with subscription attribute set to empty list + """ + fake_cached_node = MagicMock() + fake_cached_node.subscriptions = [] + return fake_cached_node + + def getVirtualClient(jid): client = MagicMock() client.jid = jid @@ -415,6 +427,7 @@ gateway.local_only = True gateway.public_url = PUBLIC_URL gateway.ap_path = '_ap' + gateway.auto_mentions = True gateway.base_ap_url = parse.urljoin( f"https://{gateway.public_url}", f"{gateway.ap_path}/" @@ -1142,3 +1155,198 @@ # only the first nickname should be used assert actor_data["name"] == "nick1" assert actor_data["summary"] == "test description" + + @ed + async def test_direct_addressing_mention_to_reference(self, ap_gateway, monkeypatch): + """AP mentions by direct addressing are converted to XEP-0372 references""" + 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()) + + direct_addr_mention = { + 'attributedTo': TEST_AP_ACTOR_ID, + 'cc': [], + 'content': '

test mention by direct addressing

', + 'id': f'{TEST_AP_ACTOR_ID}/statuses/direct_addr_123', + 'published': '2022-05-20T08:14:39Z', + 'to': [ap_const.NS_AP_PUBLIC, xmpp_actor_id], + 'type': 'Note' + } + client = ap_gateway.client.getVirtualClient( + ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT) + ) + monkeypatch.setattr(client, "sendMessage", MagicMock()) + + with patch.object(ap_gateway._refs, "sendReference") as sendReference: + await ap_gateway.newAPItem( + client, None, ap_gateway._m.namespace, direct_addr_mention + ) + + assert sendReference.call_count == 1 + assert sendReference.call_args.kwargs["to_jid"] == TEST_JID + + local_actor_jid = ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT) + expected_anchor = xmpp_uri.buildXMPPUri( + "pubsub", + path=local_actor_jid.full(), + node=ap_gateway._m.namespace, + item=direct_addr_mention["id"] + ) + + assert sendReference.call_args.kwargs["anchor"] == expected_anchor + + @ed + async def test_tag_mention_to_reference(self, ap_gateway, monkeypatch): + """AP mentions in "tag" field are converted to XEP-0372 references""" + 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()) + + direct_addr_mention = { + 'attributedTo': TEST_AP_ACTOR_ID, + 'cc': [], + 'content': '

test mention by tag

', + 'id': f'{TEST_AP_ACTOR_ID}/statuses/tag_123', + 'published': '2022-05-20T08:14:39Z', + 'to': [ap_const.NS_AP_PUBLIC], + "tag": [ + { + "type": "Mention", + "href": xmpp_actor_id, + "name": f"@{TEST_JID}'" + } + ], + 'type': 'Note' + } + client = ap_gateway.client.getVirtualClient( + ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT) + ) + monkeypatch.setattr(client, "sendMessage", MagicMock()) + + with patch.object(ap_gateway._refs, "sendReference") as sendReference: + await ap_gateway.newAPItem( + client, None, ap_gateway._m.namespace, direct_addr_mention + ) + + assert sendReference.call_count == 1 + assert sendReference.call_args.kwargs["to_jid"] == TEST_JID + + local_actor_jid = ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT) + expected_anchor = xmpp_uri.buildXMPPUri( + "pubsub", + path=local_actor_jid.full(), + node=ap_gateway._m.namespace, + item=direct_addr_mention["id"] + ) + + assert sendReference.call_args.kwargs["anchor"] == expected_anchor + + @ed + async def test_auto_mentions(self, ap_gateway, monkeypatch): + """Check that mentions in body are converted to AP mentions""" + 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) + + mb_data = { + "author_jid": TEST_JID.full(), + "content": f"mention of @{TEST_AP_ACCOUNT}", + "service": TEST_JID.full(), + "node": ap_gateway._m.namespace + } + ap_item = await ap_gateway.mbdata2APitem(ap_gateway.client, mb_data, public=True) + + ap_object = ap_item["object"] + assert TEST_AP_ACTOR_ID in ap_object["to"] + expected_mention = { + "type": ap_const.TYPE_MENTION, + "href": TEST_AP_ACTOR_ID, + "name": f"@{TEST_AP_ACCOUNT}" + } + assert expected_mention in ap_object["tag"] + + @ed + async def test_no_auto_mentions_when_not_public(self, ap_gateway, monkeypatch): + """Check that no mention is send when the message is not public""" + # this is the same test as test_auto_mentions above, except that public is not set + # in mbdata2APitem + 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) + + mb_data = { + "author_jid": TEST_JID.full(), + "content": f"mention of @{TEST_AP_ACCOUNT}", + "service": TEST_JID.full(), + "node": ap_gateway._m.namespace + } + ap_item = await ap_gateway.mbdata2APitem(ap_gateway.client, mb_data, public=False) + + ap_object = ap_item["object"] + assert "to" not in ap_object + assert "tag" not in ap_object + + @ed + async def test_xmpp_reference_to_ap_mention(self, ap_gateway, monkeypatch): + """Check that XEP-0372 references are converted to AP mention""" + 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) + + local_actor_jid = ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT) + item_elt = XMPP_ITEMS[0] + anchor = xmpp_uri.buildXMPPUri( + "pubsub", + path=TEST_JID.full(), + node=ap_gateway._m.namespace, + item=item_elt["id"] + ) + + ref_data: Dict[str, Union[str, int, dict]] = { + "uri": xmpp_uri.buildXMPPUri(None, path=local_actor_jid.full()), + "type_": "mention", + "anchor": anchor + } + reference_elt = ap_gateway._refs.buildRefElement(**ref_data) + + # we now update ref_data to look like what is received in the trigger + + ref_data["parsed_uri"] = xmpp_uri.parseXMPPUri(ref_data["uri"]) + ref_data["parsed_anchor"] = xmpp_uri.parseXMPPUri(ref_data["anchor"]) + + # "type" is a builtin function, thus "type_" is used in buildRefElement, but in + # ref_data is "type" without underscore + ref_data["type"] = ref_data["type_"] + del ref_data["type_"] + + message_elt = domish.Element((None, "message")) + message_elt.addChild(reference_elt) + + with patch.object(ap_gateway.host.memory.storage, "getItems") as getItems: + # getItems returns a sqla_mapping.PubsubItem, thus we need to fake it and set + # the item_elt we want to use in its "data" attribute + mock_pubsub_item = MagicMock + mock_pubsub_item.data = item_elt + getItems.return_value = ([mock_pubsub_item], {}) + with patch.object(ap_gateway, "signAndPost") as signAndPost: + signAndPost.return_value.code = 202 + await ap_gateway._onReferenceReceived(ap_gateway.client, message_elt, ref_data) + + # when reference is received, the referencing item must be sent to referenced + # actor, and they must be in "to" field and in "tag" + assert signAndPost.call_count == 1 + send_ap_item = signAndPost.call_args.args[-1] + ap_object = send_ap_item["object"] + assert TEST_AP_ACTOR_ID in ap_object["to"] + expected_mention = { + "type": ap_const.TYPE_MENTION, + "href": TEST_AP_ACTOR_ID, + # we don't have a prefixing "@" here, because it's not needed in referencing + # item with XMPP + "name": f"{TEST_AP_ACCOUNT}" + } + assert expected_mention in ap_object["tag"]