diff --git a/tests/unit/test_ap-gateway.py b/tests/unit/test_ap-gateway.py
index eba51b9a..8241e206 100644
--- a/tests/unit/test_ap-gateway.py
+++ b/tests/unit/test_ap-gateway.py
@@ -17,8 +17,9 @@
# along with this program. If not, see .
from copy import deepcopy
-from unittest.mock import MagicMock, patch
+from unittest.mock import MagicMock, AsyncMock, patch
from urllib import parse
+from functools import partial
import pytest
from pytest_twisted import ensureDeferred as ed
@@ -29,13 +30,16 @@ from wokkel import rsm, pubsub
from sat.core import exceptions
from sat.plugins import plugin_comp_ap_gateway
from sat.plugins.plugin_comp_ap_gateway.http_server import HTTPServer
+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
TEST_BASE_URL = "https://example.org"
TEST_USER = "test_user"
TEST_AP_ACCOUNT = f"{TEST_USER}@example.org"
+PUBLIC_URL = "test.example"
AP_REQUESTS = {
f"{TEST_BASE_URL}/.well-known/webfinger?"
@@ -71,7 +75,38 @@ AP_REQUESTS = {
"type": "Person",
"url": f"{TEST_BASE_URL}/@{TEST_USER}"
},
-
+ f"{TEST_BASE_URL}/.well-known/webfinger?"
+ f"resource=acct:{parse.quote('ext_user@example.org')}": {
+ "aliases": [
+ f"{TEST_BASE_URL}/@ext_user",
+ f"{TEST_BASE_URL}/users/ext_user"
+ ],
+ "links": [
+ {
+ "href": f"{TEST_BASE_URL}/users/ext_user",
+ "rel": "self",
+ "type": "application/activity+json"
+ },
+ ],
+ "subject": f"acct:ext_user@example.org"
+ },
+ f"{TEST_BASE_URL}/users/ext_user": {
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ ],
+ "endpoints": {
+ "sharedInbox": f"{TEST_BASE_URL}/inbox"
+ },
+ "followers": f"{TEST_BASE_URL}/users/ext_user/followers",
+ "following": f"{TEST_BASE_URL}/users/ext_user/following",
+ "id": f"{TEST_BASE_URL}/users/ext_user",
+ "inbox": f"{TEST_BASE_URL}/users/ext_user/inbox",
+ "name": "",
+ "outbox": f"{TEST_BASE_URL}/users/ext_user/outbox",
+ "preferredUsername": f"ext_user",
+ "type": "Person",
+ "url": f"{TEST_BASE_URL}/@ext_user"
+ },
f"{TEST_BASE_URL}/users/{TEST_USER}/outbox": {
"@context": "https://www.w3.org/ns/activitystreams",
"first": f"{TEST_BASE_URL}/users/{TEST_USER}/outbox?page=true",
@@ -222,7 +257,43 @@ AP_REQUESTS = {
"partOf": f"{TEST_BASE_URL}/users/{TEST_USER}/outbox",
"prev": None,
"type": "OrderedCollectionPage"
- }
+ },
+ f"{TEST_BASE_URL}/users/{TEST_USER}/following": {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "first": f"{TEST_BASE_URL}/users/{TEST_USER}/following?page=1",
+ "id": f"{TEST_BASE_URL}/users/{TEST_USER}/following",
+ "totalItems": 2,
+ "type": "OrderedCollection"
+ },
+ f"{TEST_BASE_URL}/users/{TEST_USER}/following?page=1": {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": f"{TEST_BASE_URL}/users/{TEST_USER}/following?page=1",
+ "orderedItems": [
+ f"{TEST_BASE_URL}/users/ext_user",
+ f"https://{PUBLIC_URL}/_ap/{TYPE_ACTOR}/local_user%40{PUBLIC_URL}",
+ ],
+ "partOf": "{TEST_BASE_URL}/users/{TEST_USER}/following",
+ "totalItems": 2,
+ "type": "OrderedCollectionPage"
+ },
+ f"{TEST_BASE_URL}/users/{TEST_USER}/followers": {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "first": f"{TEST_BASE_URL}/users/{TEST_USER}/followers?page=1",
+ "id": f"{TEST_BASE_URL}/users/{TEST_USER}/followers",
+ "totalItems": 2,
+ "type": "OrderedCollection"
+ },
+ f"{TEST_BASE_URL}/users/{TEST_USER}/followers?page=1": {
+ "@context": "https://www.w3.org/ns/activitystreams",
+ "id": f"{TEST_BASE_URL}/users/{TEST_USER}/followers?page=1",
+ "orderedItems": [
+ f"{TEST_BASE_URL}/users/ext_user",
+ f"https://{PUBLIC_URL}/_ap/{TYPE_ACTOR}/local_user%40{PUBLIC_URL}",
+ ],
+ "partOf": "{TEST_BASE_URL}/users/{TEST_USER}/followers",
+ "totalItems": 2,
+ "type": "OrderedCollectionPage"
+ },
}
@@ -276,6 +347,7 @@ XMPP_ITEMS = [
for i in range(1, 5)
]
+
async def mock_ap_get(url):
return deepcopy(AP_REQUESTS[url])
@@ -285,25 +357,28 @@ async def mock_treq_json(data):
async def mock_getItems(*args, **kwargs):
+ ret_items = kwargs.pop("ret_items", XMPP_ITEMS)
rsm_resp = rsm.RSMResponse(
- first=XMPP_ITEMS[0]["id"],
- last=XMPP_ITEMS[-1]["id"],
+ first=ret_items[0]["id"],
+ last=ret_items[-1]["id"],
index=0,
- count=len(XMPP_ITEMS)
+ count=len(ret_items)
)
- return XMPP_ITEMS, {"rsm": rsm_resp.toDict(), "complete": True}
+ return ret_items, {"rsm": rsm_resp.toDict(), "complete": True}
@pytest.fixture(scope="session")
def ap_gateway(host):
gateway = plugin_comp_ap_gateway.APGateway(host)
gateway.initialised = True
+ gateway.isPubsub = AsyncMock()
+ gateway.isPubsub.return_value = False
client = MagicMock()
client.jid = jid.JID("ap.test.example")
client.host = "test.example"
gateway.client = client
gateway.local_only = True
- gateway.public_url = "test.example"
+ gateway.public_url = PUBLIC_URL
gateway.ap_path = '_ap'
gateway.base_ap_url = parse.urljoin(
f"https://{gateway.public_url}",
@@ -407,7 +482,7 @@ class TestActivityPubGateway:
monkeypatch.setattr(plugin_comp_ap_gateway.treq, "json_content", mock_treq_json)
monkeypatch.setattr(ap_gateway, "apGet", mock_ap_get)
- actor_data = await ap_gateway.getAPActorDataFromId(TEST_AP_ACCOUNT)
+ actor_data = await ap_gateway.getAPActorDataFromAccount(TEST_AP_ACCOUNT)
outbox = await ap_gateway.apGetObject(actor_data, "outbox")
items, rsm_resp = await ap_gateway.getAPItems(outbox, 2)
@@ -548,9 +623,12 @@ class TestActivityPubGateway:
"node": None,
"ap_account": test_jid.full(),
"ap_url": ap_url,
+ "signing_actor": None
}
if type_ == "outbox" and query_data:
kwargs["query_data"] = query_data
+ # signing_actor is not used for page requests
+ del kwargs["signing_actor"]
return kwargs
@ed
@@ -586,4 +664,127 @@ class TestActivityPubGateway:
assert first_item_obj["published"] == "2022-01-28T16:02:19Z"
assert first_item_obj["attributedTo"] == first_item["actor"]
assert first_item_obj["content"] == "
"
- assert first_item_obj["to"] == "https://www.w3.org/ns/activitystreams#Public"
+ assert first_item_obj["to"] == ["https://www.w3.org/ns/activitystreams#Public"]
+
+ @ed
+ async def test_following_to_pps(self, ap_gateway, monkeypatch):
+ """AP following items are converted to Public Pubsub Subscription subscriptions"""
+ 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)
+
+ items, __ = await ap_gateway.pubsub_service.items(
+ jid.JID("toto@example.org"),
+ ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT),
+ ap_gateway._pps.subscriptions_node,
+ None,
+ None,
+ None
+ )
+ assert len(items) == 2
+ for idx, entity in enumerate((
+ "local_user@test.example",
+ "ext_user\\40example.org@ap.test.example"
+ )):
+ subscription_elt = next(items[idx].elements(NS_PPS, "subscription"), None)
+ assert subscription_elt is not None
+ assert subscription_elt["node"] == ap_gateway._m.namespace
+ assert subscription_elt["service"] == entity
+
+ @ed
+ async def test_followers_to_pps(self, ap_gateway, monkeypatch):
+ """AP followers items are converted to Public Pubsub Subscription subscribers"""
+ 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)
+
+ items, __ = await ap_gateway.pubsub_service.items(
+ jid.JID("toto@example.org"),
+ ap_gateway.getLocalJIDFromAccount(TEST_AP_ACCOUNT),
+ ap_gateway._pps.getPublicSubscribersNode(ap_gateway._m.namespace),
+ None,
+ None,
+ None
+ )
+ assert len(items) == 2
+ for idx, entity in enumerate((
+ "local_user@test.example",
+ "ext_user\\40example.org@ap.test.example"
+ )):
+ subscriber_elt = next(items[idx].elements(NS_PPS, "subscriber"), None)
+ assert subscriber_elt is not None
+ assert subscriber_elt["jid"] == entity
+
+ @ed
+ async def test_pps_to_following(self, ap_gateway, monkeypatch):
+ """Public Pubsub Subscription subscriptions are converted to AP following items"""
+ subscriptions = [
+ pubsub.Item(
+ id="subscription_1",
+ payload = ap_gateway._pps.buildSubscriptionElt(
+ ap_gateway._m.namespace,
+ jid.JID("local_user@test.example")
+ )
+ ),
+ pubsub.Item(
+ id="subscription_2",
+ payload = ap_gateway._pps.buildSubscriptionElt(
+ ap_gateway._m.namespace,
+ jid.JID("ext_user\\40example.org@ap.test.example")
+ )
+ )
+ ]
+ monkeypatch.setattr(ap_gateway._p, "getItems", partial(
+ mock_getItems,
+ ret_items=subscriptions
+ ))
+ following = await ap_gateway.server.resource.APFollowingRequest(
+ **self.ap_request_params(ap_gateway, "following")
+ )
+ assert following["@context"] == "https://www.w3.org/ns/activitystreams"
+ assert following["id"] == "https://test.example/_ap/following/some_user%40test.example"
+ assert following["totalItems"] == len(subscriptions)
+ assert following["type"] == "OrderedCollection"
+ assert following.get("first")
+
+ first_page = following["first"]
+ assert first_page["type"] == "OrderedCollectionPage"
+ assert len(first_page["orderedItems"]) == len(subscriptions)
+ items = first_page["orderedItems"]
+ assert items == ['local_user@test.example', 'ext_user@example.org']
+
+ @ed
+ async def test_pps_to_followers(self, ap_gateway, monkeypatch):
+ """Public Pubsub Subscription subscribers are converted to AP followers"""
+ subscribers = [
+ pubsub.Item(
+ id="subscriber_1",
+ payload = ap_gateway._pps.buildSubscriberElt(
+ jid.JID("local_user@test.example")
+ )
+ ),
+ pubsub.Item(
+ id="subscriber_2",
+ payload = ap_gateway._pps.buildSubscriberElt(
+ jid.JID("ext_user\\40example.org@ap.test.example")
+ )
+ )
+ ]
+ monkeypatch.setattr(ap_gateway._p, "getItems", partial(
+ mock_getItems,
+ ret_items=subscribers
+ ))
+ followers = await ap_gateway.server.resource.APFollowersRequest(
+ **self.ap_request_params(ap_gateway, "followers")
+ )
+ assert followers["@context"] == "https://www.w3.org/ns/activitystreams"
+ assert followers["id"] == "https://test.example/_ap/followers/some_user%40test.example"
+ assert followers["totalItems"] == len(subscribers)
+ assert followers["type"] == "OrderedCollection"
+ assert followers.get("first")
+
+ first_page = followers["first"]
+ assert first_page["type"] == "OrderedCollectionPage"
+ assert len(first_page["orderedItems"]) == len(subscribers)
+ items = first_page["orderedItems"]
+ assert items == ['local_user@test.example', 'ext_user@example.org']