diff --git a/app/models.py b/app/models.py index 22c956c..e44b110 100644 --- a/app/models.py +++ b/app/models.py @@ -281,7 +281,7 @@ class Follower(Base): updated_at = Column(DateTime(timezone=True), nullable=False, default=now) actor_id = Column(Integer, ForeignKey("actor.id"), nullable=False, unique=True) - actor = relationship(Actor, uselist=False) + actor: Mapped[Actor] = relationship(Actor, uselist=False) inbox_object_id = Column(Integer, ForeignKey("inbox.id"), nullable=False) inbox_object = relationship(InboxObject, uselist=False) diff --git a/tests/test_ldsig.py b/tests/test_ldsig.py index aabc325..d371994 100644 --- a/tests/test_ldsig.py +++ b/tests/test_ldsig.py @@ -1,7 +1,5 @@ from copy import deepcopy -import pytest - from app import activitypub as ap from app import ldsig from app.key import Key @@ -30,7 +28,6 @@ _SAMPLE_CREATE = { } -@pytest.mark.skip(reason="Working but slow") def test_linked_data_sig(): privkey, pubkey = factories.generate_key() ra = factories.RemoteActorFactory( diff --git a/tests/test_outbox.py b/tests/test_outbox.py index 4c5c1d8..6aba1d0 100644 --- a/tests/test_outbox.py +++ b/tests/test_outbox.py @@ -7,6 +7,7 @@ from fastapi.testclient import TestClient from sqlalchemy.orm import Session from app import activitypub as ap +from app import actor from app import models from app import webfinger from app.actor import LOCAL_ACTOR @@ -29,18 +30,47 @@ def test_outbox__no_activities( assert json_response["orderedItems"] == [] -def test_send_follow_request( - db: Session, - client: TestClient, - respx_mock: respx.MockRouter, -) -> None: - # given a remote actor +def _setup_remote_actor(respx_mock: respx.MockRouter) -> actor.RemoteActor: ra = factories.RemoteActorFactory( base_url="https://example.com", username="toto", public_key="pk", ) respx_mock.get(ra.ap_id).mock(return_value=httpx.Response(200, json=ra.ap_actor)) + return ra + + +def _remote_actor_as_follower(ra: actor.RemoteActor) -> models.Follower: + actor = factories.ActorFactory.from_remote_actor(ra) + + follow_id = uuid4().hex + follow_from_inbox = RemoteObject( + factories.build_follow_activity( + from_remote_actor=ra, + for_remote_actor=LOCAL_ACTOR, + outbox_public_id=follow_id, + ), + ra, + ) + inbox_object = factories.InboxObjectFactory.from_remote_object( + follow_from_inbox, actor + ) + + follower = factories.FollowerFactory( + inbox_object_id=inbox_object.id, + actor_id=actor.id, + ap_actor_id=actor.ap_id, + ) + return follower + + +def test_send_follow_request( + db: Session, + client: TestClient, + respx_mock: respx.MockRouter, +) -> None: + # given a remote actor + ra = _setup_remote_actor(respx_mock) response = client.post( "/admin/actions/follow", @@ -73,12 +103,7 @@ def test_send_create_activity__no_followers_and_with_mention( respx_mock: respx.MockRouter, ) -> None: # given a remote actor - ra = factories.RemoteActorFactory( - base_url="https://example.com", - username="toto", - public_key="pk", - ) - respx_mock.get(ra.ap_id).mock(return_value=httpx.Response(200, json=ra.ap_actor)) + ra = _setup_remote_actor(respx_mock) with mock.patch.object(webfinger, "get_actor_url", return_value=ra.ap_id): response = client.post( @@ -111,32 +136,10 @@ def test_send_create_activity__with_followers( respx_mock: respx.MockRouter, ) -> None: # given a remote actor - ra = factories.RemoteActorFactory( - base_url="https://example.com", - username="toto", - public_key="pk", - ) - respx_mock.get(ra.ap_id).mock(return_value=httpx.Response(200, json=ra.ap_actor)) - actor = factories.ActorFactory.from_remote_actor(ra) + ra = _setup_remote_actor(respx_mock) - follow_id = uuid4().hex - follow_from_inbox = RemoteObject( - factories.build_follow_activity( - from_remote_actor=ra, - for_remote_actor=LOCAL_ACTOR, - outbox_public_id=follow_id, - ), - ra, - ) - inbox_object = factories.InboxObjectFactory.from_remote_object( - follow_from_inbox, actor - ) - - follower = factories.FollowerFactory( - inbox_object_id=inbox_object.id, - actor_id=actor.id, - ap_actor_id=actor.ap_id, - ) + # who is a follower + follower = _remote_actor_as_follower(ra) with mock.patch.object(webfinger, "get_actor_url", return_value=ra.ap_id): response = client.post( @@ -169,32 +172,10 @@ def test_send_create_activity__question__one_of( respx_mock: respx.MockRouter, ) -> None: # given a remote actor - ra = factories.RemoteActorFactory( - base_url="https://example.com", - username="toto", - public_key="pk", - ) - respx_mock.get(ra.ap_id).mock(return_value=httpx.Response(200, json=ra.ap_actor)) - actor = factories.ActorFactory.from_remote_actor(ra) + ra = _setup_remote_actor(respx_mock) - follow_id = uuid4().hex - follow_from_inbox = RemoteObject( - factories.build_follow_activity( - from_remote_actor=ra, - for_remote_actor=LOCAL_ACTOR, - outbox_public_id=follow_id, - ), - ra, - ) - inbox_object = factories.InboxObjectFactory.from_remote_object( - follow_from_inbox, actor - ) - - follower = factories.FollowerFactory( - inbox_object_id=inbox_object.id, - actor_id=actor.id, - ap_actor_id=actor.ap_id, - ) + # who is a follower + follower = _remote_actor_as_follower(ra) with mock.patch.object(webfinger, "get_actor_url", return_value=ra.ap_id): response = client.post( @@ -235,32 +216,10 @@ def test_send_create_activity__question__any_of( respx_mock: respx.MockRouter, ) -> None: # given a remote actor - ra = factories.RemoteActorFactory( - base_url="https://example.com", - username="toto", - public_key="pk", - ) - respx_mock.get(ra.ap_id).mock(return_value=httpx.Response(200, json=ra.ap_actor)) - actor = factories.ActorFactory.from_remote_actor(ra) + ra = _setup_remote_actor(respx_mock) - follow_id = uuid4().hex - follow_from_inbox = RemoteObject( - factories.build_follow_activity( - from_remote_actor=ra, - for_remote_actor=LOCAL_ACTOR, - outbox_public_id=follow_id, - ), - ra, - ) - inbox_object = factories.InboxObjectFactory.from_remote_object( - follow_from_inbox, actor - ) - - follower = factories.FollowerFactory( - inbox_object_id=inbox_object.id, - actor_id=actor.id, - ap_actor_id=actor.ap_id, - ) + # who is a follower + follower = _remote_actor_as_follower(ra) with mock.patch.object(webfinger, "get_actor_url", return_value=ra.ap_id): response = client.post( @@ -295,3 +254,41 @@ def test_send_create_activity__question__any_of( outgoing_activity = db.query(models.OutgoingActivity).one() assert outgoing_activity.outbox_object_id == outbox_object.id assert outgoing_activity.recipient == follower.actor.inbox_url + + +def test_send_create_activity__article( + db: Session, + client: TestClient, + respx_mock: respx.MockRouter, +) -> None: + # given a remote actor + ra = _setup_remote_actor(respx_mock) + + # who is a follower + follower = _remote_actor_as_follower(ra) + + with mock.patch.object(webfinger, "get_actor_url", return_value=ra.ap_id): + response = client.post( + "/admin/actions/new", + data={ + "redirect_url": "http://testserver/", + "content": "hi followers", + "visibility": ap.VisibilityEnum.PUBLIC.name, + "csrf_token": generate_csrf_token(), + "name": "Article", + }, + cookies=generate_admin_session_cookies(), + ) + + # Then the server returns a 302 + assert response.status_code == 302 + + # And the Follow activity was created in the outbox + outbox_object = db.query(models.OutboxObject).one() + assert outbox_object.ap_type == "Article" + assert outbox_object.ap_object["name"] == "Article" + + # And an outgoing activity was queued + outgoing_activity = db.query(models.OutgoingActivity).one() + assert outgoing_activity.outbox_object_id == outbox_object.id + assert outgoing_activity.recipient == follower.actor.inbox_url