microblog/tests/test_process_outgoing_activ...

260 lines
8.3 KiB
Python
Raw Normal View History

2022-06-22 18:11:22 +00:00
from uuid import uuid4
import httpx
2022-06-29 18:43:17 +00:00
import pytest
2022-06-22 18:11:22 +00:00
import respx
from fastapi.testclient import TestClient
2022-06-29 18:43:17 +00:00
from sqlalchemy import select
2022-06-22 18:11:22 +00:00
from app import models
from app.actor import LOCAL_ACTOR
from app.ap_object import RemoteObject
2022-06-29 18:43:17 +00:00
from app.database import AsyncSession
2022-06-24 09:33:05 +00:00
from app.outgoing_activities import _MAX_RETRIES
from app.outgoing_activities import fetch_next_outgoing_activity
2022-06-24 09:33:05 +00:00
from app.outgoing_activities import new_outgoing_activity
from app.outgoing_activities import process_next_outgoing_activity
2022-06-22 18:11:22 +00:00
from tests import factories
def _setup_outbox_object() -> models.OutboxObject:
ra = factories.RemoteActorFactory(
base_url="https://example.com",
username="toto",
public_key="pk",
)
# And a Follow activity in the outbox
follow_id = uuid4().hex
follow_from_outbox = RemoteObject(
factories.build_follow_activity(
from_remote_actor=LOCAL_ACTOR,
for_remote_actor=ra,
outbox_public_id=follow_id,
2022-06-29 22:28:07 +00:00
),
LOCAL_ACTOR,
2022-06-22 18:11:22 +00:00
)
outbox_object = factories.OutboxObjectFactory.from_remote_object(
follow_id, follow_from_outbox
)
return outbox_object
2022-06-29 18:43:17 +00:00
@pytest.mark.asyncio
async def test_new_outgoing_activity(
async_db_session: AsyncSession,
2022-06-22 18:11:22 +00:00
client: TestClient,
respx_mock: respx.MockRouter,
) -> None:
outbox_object = _setup_outbox_object()
inbox_url = "https://example.com/inbox"
2022-06-22 19:15:07 +00:00
if not outbox_object.id:
raise ValueError("Should never happen")
2022-06-22 18:11:22 +00:00
# When queuing the activity
2022-06-29 18:43:17 +00:00
outgoing_activity = await new_outgoing_activity(
async_db_session, inbox_url, outbox_object.id
)
2022-07-24 18:27:58 +00:00
await async_db_session.commit()
2022-06-22 18:11:22 +00:00
2022-06-29 18:43:17 +00:00
assert (
await async_db_session.execute(select(models.OutgoingActivity))
).scalar_one() == outgoing_activity
2022-06-22 18:11:22 +00:00
assert outgoing_activity.outbox_object_id == outbox_object.id
assert outgoing_activity.recipient == inbox_url
@pytest.mark.asyncio
async def test_process_next_outgoing_activity__no_next_activity(
2022-06-22 18:11:22 +00:00
respx_mock: respx.MockRouter,
async_db_session: AsyncSession,
2022-06-22 18:11:22 +00:00
) -> None:
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity is None
2022-06-22 18:11:22 +00:00
@pytest.mark.asyncio
async def test_process_next_outgoing_activity__server_200(
async_db_session: AsyncSession,
2022-06-22 18:11:22 +00:00
respx_mock: respx.MockRouter,
) -> None:
# And an outgoing activity
outbox_object = _setup_outbox_object()
recipient_inbox_url = "https://example.com/users/toto/inbox"
respx_mock.post(recipient_inbox_url).mock(return_value=httpx.Response(204))
outgoing_activity = factories.OutgoingActivityFactory(
recipient=recipient_inbox_url,
outbox_object_id=outbox_object.id,
2022-07-08 19:17:08 +00:00
inbox_object_id=None,
webmention_target=None,
)
# When processing the next outgoing activity
# Then it is processed
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity
await process_next_outgoing_activity(async_db_session, next_activity)
assert respx_mock.calls.call_count == 1
outgoing_activity = (
await async_db_session.execute(select(models.OutgoingActivity))
).scalar_one()
assert outgoing_activity.is_sent is True
assert outgoing_activity.last_status_code == 204
assert outgoing_activity.error is None
assert outgoing_activity.is_errored is False
@pytest.mark.asyncio
async def test_process_next_outgoing_activity__webmention(
async_db_session: AsyncSession,
respx_mock: respx.MockRouter,
) -> None:
# And an outgoing activity
outbox_object = _setup_outbox_object()
recipient_url = "https://example.com/webmention"
respx_mock.post(recipient_url).mock(return_value=httpx.Response(204))
outgoing_activity = factories.OutgoingActivityFactory(
recipient=recipient_url,
outbox_object_id=outbox_object.id,
inbox_object_id=None,
webmention_target="http://example.com",
2022-06-22 18:11:22 +00:00
)
# When processing the next outgoing activity
# Then it is processed
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity
await process_next_outgoing_activity(async_db_session, next_activity)
2022-06-22 18:11:22 +00:00
assert respx_mock.calls.call_count == 1
outgoing_activity = (
await async_db_session.execute(select(models.OutgoingActivity))
).scalar_one()
2022-06-22 18:11:22 +00:00
assert outgoing_activity.is_sent is True
assert outgoing_activity.last_status_code == 204
assert outgoing_activity.error is None
assert outgoing_activity.is_errored is False
@pytest.mark.asyncio
async def test_process_next_outgoing_activity__error_500(
async_db_session: AsyncSession,
2022-06-22 18:11:22 +00:00
respx_mock: respx.MockRouter,
) -> None:
outbox_object = _setup_outbox_object()
recipient_inbox_url = "https://example.com/inbox"
respx_mock.post(recipient_inbox_url).mock(
return_value=httpx.Response(500, text="oops")
)
# And an outgoing activity
outgoing_activity = factories.OutgoingActivityFactory(
recipient=recipient_inbox_url,
outbox_object_id=outbox_object.id,
inbox_object_id=None,
webmention_target=None,
2022-06-22 18:11:22 +00:00
)
# When processing the next outgoing activity
# Then it is processed
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity
await process_next_outgoing_activity(async_db_session, next_activity)
2022-06-22 18:11:22 +00:00
assert respx_mock.calls.call_count == 1
outgoing_activity = (
await async_db_session.execute(select(models.OutgoingActivity))
).scalar_one()
2022-06-22 18:11:22 +00:00
assert outgoing_activity.is_sent is False
assert outgoing_activity.last_status_code == 500
assert outgoing_activity.last_response == "oops"
assert outgoing_activity.is_errored is False
assert outgoing_activity.tries == 1
@pytest.mark.asyncio
async def test_process_next_outgoing_activity__errored(
async_db_session: AsyncSession,
2022-06-22 18:11:22 +00:00
respx_mock: respx.MockRouter,
) -> None:
outbox_object = _setup_outbox_object()
recipient_inbox_url = "https://example.com/inbox"
2022-06-27 18:55:44 +00:00
respx_mock.post(recipient_inbox_url).mock(
return_value=httpx.Response(500, text="oops")
)
2022-06-22 18:11:22 +00:00
# And an outgoing activity
2022-07-24 18:27:58 +00:00
outgoing_activity = factories.OutgoingActivityFactory.create(
2022-06-22 18:11:22 +00:00
recipient=recipient_inbox_url,
outbox_object_id=outbox_object.id,
inbox_object_id=None,
webmention_target=None,
2022-06-27 18:55:44 +00:00
tries=_MAX_RETRIES - 1,
2022-06-22 18:11:22 +00:00
)
# When processing the next outgoing activity
# Then it is processed
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity
await process_next_outgoing_activity(async_db_session, next_activity)
2022-06-22 18:11:22 +00:00
assert respx_mock.calls.call_count == 1
outgoing_activity = (
await async_db_session.execute(select(models.OutgoingActivity))
).scalar_one()
2022-06-22 18:11:22 +00:00
assert outgoing_activity.is_sent is False
2022-06-27 18:55:44 +00:00
assert outgoing_activity.last_status_code == 500
assert outgoing_activity.last_response == "oops"
assert outgoing_activity.is_errored is True
2022-06-22 18:11:22 +00:00
2022-06-27 18:55:44 +00:00
# And it is skipped from processing
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity is None
2022-06-22 18:11:22 +00:00
2022-06-27 18:55:44 +00:00
@pytest.mark.asyncio
async def test_process_next_outgoing_activity__connect_error(
async_db_session: AsyncSession,
2022-06-22 18:11:22 +00:00
respx_mock: respx.MockRouter,
) -> None:
outbox_object = _setup_outbox_object()
recipient_inbox_url = "https://example.com/inbox"
2022-06-27 18:55:44 +00:00
respx_mock.post(recipient_inbox_url).mock(side_effect=httpx.ConnectError)
2022-06-22 18:11:22 +00:00
# And an outgoing activity
outgoing_activity = factories.OutgoingActivityFactory(
recipient=recipient_inbox_url,
outbox_object_id=outbox_object.id,
2022-07-08 19:17:08 +00:00
inbox_object_id=None,
webmention_target=None,
2022-06-22 18:11:22 +00:00
)
# When processing the next outgoing activity
# Then it is processed
next_activity = await fetch_next_outgoing_activity(async_db_session, set())
assert next_activity
await process_next_outgoing_activity(async_db_session, next_activity)
2022-06-22 18:11:22 +00:00
assert respx_mock.calls.call_count == 1
outgoing_activity = (
await async_db_session.execute(select(models.OutgoingActivity))
).scalar_one()
2022-06-22 18:11:22 +00:00
assert outgoing_activity.is_sent is False
2022-06-27 18:55:44 +00:00
assert outgoing_activity.error is not None
assert outgoing_activity.tries == 1
2022-06-22 18:11:22 +00:00
# TODO(ts):
# - parse retry after