From 420f823dd4d88736cc0509572306dac3322f0c31 Mon Sep 17 00:00:00 2001
From: Simone Robutti
Date: Wed, 2 Mar 2022 08:59:49 +0100
Subject: [PATCH] platform tests (#150)
* added twitter error handling
* added facebook tests
* added header format test
* added multiple newlines check
---
.../publishers/platforms/facebook.py | 26 +++++---
.../publishers/platforms/telegram.py | 10 ++-
.../publishers/platforms/twitter.py | 7 +-
tests/formatting/test_output_format.py | 9 ++-
tests/publishers/test_facebook.py | 64 +++++++++++++++++++
tests/publishers/test_twitter.py | 44 +++++++++++++
6 files changed, 141 insertions(+), 19 deletions(-)
create mode 100644 tests/publishers/test_facebook.py
create mode 100644 tests/publishers/test_twitter.py
diff --git a/mobilizon_reshare/publishers/platforms/facebook.py b/mobilizon_reshare/publishers/platforms/facebook.py
index d7d9c2d..b703039 100644
--- a/mobilizon_reshare/publishers/platforms/facebook.py
+++ b/mobilizon_reshare/publishers/platforms/facebook.py
@@ -13,6 +13,8 @@ from mobilizon_reshare.publishers.abstract import (
from mobilizon_reshare.publishers.exceptions import (
InvalidCredentials,
InvalidEvent,
+ InvalidMessage,
+ PublisherError,
)
@@ -37,7 +39,8 @@ class FacebookFormatter(AbstractEventFormatter):
self._log_error("No text was found", raise_error=InvalidEvent)
def _validate_message(self, message) -> None:
- pass
+ if len(message) >= 63200:
+ self._log_error("Message is too long", raise_error=InvalidMessage)
def _preprocess_event(self, event: MobilizonEvent):
event.description = html_to_plaintext(event.description)
@@ -52,18 +55,23 @@ class FacebookPlatform(AbstractPlatform):
name = "facebook"
- def _get_api(self):
+ def _get_api(self) -> facebook.GraphAPI:
return facebook.GraphAPI(
access_token=self.conf["page_access_token"], version="8.0"
)
def _send(self, message: str, event: Optional[MobilizonEvent] = None):
- self._get_api().put_object(
- parent_object="me",
- connection_name="feed",
- message=message,
- link=event.mobilizon_link if event else None,
- )
+ try:
+ self._get_api().put_object(
+ parent_object="me",
+ connection_name="feed",
+ message=message,
+ link=event.mobilizon_link if event else None,
+ )
+ except GraphAPIError:
+ self._log_error(
+ "Facebook send failed", raise_error=PublisherError,
+ )
def validate_credentials(self):
@@ -76,7 +84,7 @@ class FacebookPlatform(AbstractPlatform):
raise_error=InvalidCredentials,
)
- self._log_debug("Facebook credentials are valid")
+ self._log_debug("Facebook credentials are valid")
def _validate_response(self, response):
pass
diff --git a/mobilizon_reshare/publishers/platforms/telegram.py b/mobilizon_reshare/publishers/platforms/telegram.py
index 9a4a8d4..10919b5 100644
--- a/mobilizon_reshare/publishers/platforms/telegram.py
+++ b/mobilizon_reshare/publishers/platforms/telegram.py
@@ -1,3 +1,4 @@
+import re
from typing import Optional
import pkg_resources
@@ -46,7 +47,11 @@ class TelegramFormatter(AbstractEventFormatter):
self._log_error("Message is too long", raise_error=InvalidMessage)
def _preprocess_message(self, message: str) -> str:
-
+ """
+ This function converts HTML5 to Telegram's HTML dialect
+ :param message: a HTML5 string
+ :return: a HTML string compatible with Telegram
+ """
html = BeautifulSoup(message, "html.parser")
# replacing paragraphs
for tag in html.findAll(["p", "br"]):
@@ -70,7 +75,8 @@ class TelegramFormatter(AbstractEventFormatter):
# cleaning html trailing whitespace
for tag in html.findAll("a"):
tag["href"] = tag["href"].replace(" ", "").strip().lstrip()
- return str(html)
+ s = str(html)
+ return re.sub(r"\n{2,}", "\n\n", s).strip() # remove multiple newlines
class TelegramPlatform(AbstractPlatform):
diff --git a/mobilizon_reshare/publishers/platforms/twitter.py b/mobilizon_reshare/publishers/platforms/twitter.py
index 7490cc9..0a1275c 100644
--- a/mobilizon_reshare/publishers/platforms/twitter.py
+++ b/mobilizon_reshare/publishers/platforms/twitter.py
@@ -11,7 +11,6 @@ from mobilizon_reshare.publishers.abstract import (
)
from mobilizon_reshare.publishers.exceptions import (
InvalidCredentials,
- InvalidEvent,
PublisherError,
InvalidMessage,
)
@@ -33,9 +32,7 @@ class TwitterFormatter(AbstractEventFormatter):
)
def _validate_event(self, event: MobilizonEvent) -> None:
- text = event.description
- if not (text and text.strip()):
- self._log_error("No text was found", raise_error=InvalidEvent)
+ pass # pragma: no cover
def _validate_message(self, message) -> None:
# TODO this is not precise. It should count the characters according to Twitter's logic but
@@ -76,7 +73,7 @@ class TwitterPlatform(AbstractPlatform):
)
def _validate_response(self, res: Status) -> dict:
- pass
+ pass # pragma: no cover
class TwitterPublisher(TwitterPlatform):
diff --git a/tests/formatting/test_output_format.py b/tests/formatting/test_output_format.py
index 7abbd7a..563cbbe 100644
--- a/tests/formatting/test_output_format.py
+++ b/tests/formatting/test_output_format.py
@@ -25,7 +25,8 @@ end_date = begin_date.shift(hours=1)
def event() -> MobilizonEvent:
return MobilizonEvent(
name="test event",
- description="description of the event
",
+ description="description of the event
another header
",
+ # "",
begin_datetime=begin_date,
end_datetime=end_date,
mobilizon_link="http://some_link.com/123",
@@ -49,7 +50,7 @@ def event() -> MobilizonEvent:
📍 location
-description of the event
+description of the event another header
Link: http://some_link.com/123
""",
@@ -61,7 +62,9 @@ Link: http://some_link.com/123
🕒 01 January, {begin_date.format('HH:mm')} - 01 January, {end_date.format('HH:mm')}
📍 location
-description of the event
+description of the event
+
+another header
Link""",
],
diff --git a/tests/publishers/test_facebook.py b/tests/publishers/test_facebook.py
new file mode 100644
index 0000000..51a12dd
--- /dev/null
+++ b/tests/publishers/test_facebook.py
@@ -0,0 +1,64 @@
+from logging import DEBUG
+from unittest.mock import patch
+
+import pytest
+from facebook import GraphAPI, GraphAPIError
+
+from mobilizon_reshare.publishers.exceptions import (
+ InvalidEvent,
+ InvalidMessage,
+ PublisherError,
+)
+from mobilizon_reshare.publishers.platforms.facebook import (
+ FacebookFormatter,
+ FacebookPublisher,
+)
+
+
+def test_message_length_success(event):
+ message = "a" * 500
+ event.description = message
+ assert FacebookFormatter().validate_event(event) is None
+
+
+def test_message_length_failure(event):
+ message = "a" * 80000
+ event.description = message
+
+ with pytest.raises(InvalidMessage):
+ FacebookFormatter().validate_event(event)
+
+
+def test_event_validation(event):
+ event.description = None
+ with pytest.raises(InvalidEvent):
+ FacebookFormatter().validate_event(event)
+
+
+def test_send_error(event):
+
+ with patch.object(
+ GraphAPI, "put_object", side_effect=GraphAPIError("some error")
+ ) as mock:
+ with pytest.raises(PublisherError):
+ FacebookPublisher().send("abc", event)
+ mock.assert_called()
+
+
+def test_validate_credentials_error(event):
+
+ with patch.object(
+ GraphAPI, "get_object", side_effect=GraphAPIError("some error")
+ ) as mock:
+ with pytest.raises(PublisherError):
+ FacebookPublisher().validate_credentials()
+ mock.assert_called()
+
+
+def test_validate_credentials(event, caplog):
+
+ with patch.object(GraphAPI, "get_object", return_value=None) as mock:
+ with caplog.at_level(DEBUG):
+ FacebookPublisher().validate_credentials()
+ mock.assert_called()
+ assert "Facebook credentials are valid" in caplog.text
diff --git a/tests/publishers/test_twitter.py b/tests/publishers/test_twitter.py
new file mode 100644
index 0000000..362f298
--- /dev/null
+++ b/tests/publishers/test_twitter.py
@@ -0,0 +1,44 @@
+from unittest.mock import patch
+
+import pytest
+from tweepy import API, TweepyException
+
+from mobilizon_reshare.publishers.exceptions import (
+ InvalidMessage,
+ InvalidCredentials,
+ PublisherError,
+)
+from mobilizon_reshare.publishers.platforms.twitter import (
+ TwitterFormatter,
+ TwitterPublisher,
+)
+
+
+def test_message_length_success(event):
+ message = "a" * 300
+ event.description = message
+ assert TwitterFormatter().validate_event(event) is None
+
+
+def test_message_length_failure(event):
+ message = "a" * 10000
+ event.name = message
+
+ with pytest.raises(InvalidMessage):
+ TwitterFormatter().validate_event(event)
+
+
+def test_validate_credentials_error():
+ with patch.object(API, "verify_credentials", return_value=False) as mock:
+ with pytest.raises(InvalidCredentials):
+ TwitterPublisher().validate_credentials()
+ mock.assert_called()
+
+
+def test_send_error(event):
+ with patch.object(
+ API, "update_status", side_effect=TweepyException("some error")
+ ) as mock:
+ with pytest.raises(PublisherError):
+ TwitterPublisher().send("abc", event)
+ mock.assert_called()