refactor coordinators (#171)

* introduced CommandConfig object

* added dry_run for start command

* added test to start_dry_run

* added dry_run recap

* fixed import

* improved printing

* fixed zulip debug info

* improved recap dry-run print

* moved coordinator classes into dedicated package

* removed unused init

* divided event coordinators from recap coordinators

* added some docstrings
This commit is contained in:
Simone Robutti 2022-09-24 16:46:03 +02:00 committed by GitHub
parent 2d328a30bf
commit 63a30bb483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 432 additions and 368 deletions

View File

@ -6,18 +6,22 @@ from mobilizon_reshare.event.event import MobilizonEvent
from mobilizon_reshare.event.event_selection_strategies import select_event_to_publish
from mobilizon_reshare.publishers import get_active_publishers
from mobilizon_reshare.publishers.abstract import EventPublication
from mobilizon_reshare.publishers.coordinator import (
from mobilizon_reshare.publishers.coordinators.event_publishing.notify import (
PublicationFailureNotifiersCoordinator,
PublisherCoordinatorReport,
)
from mobilizon_reshare.publishers.coordinator import PublisherCoordinator
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
PublisherCoordinatorReport,
PublisherCoordinator,
)
from mobilizon_reshare.storage.query.read import (
get_published_events,
build_publications,
events_without_publications,
)
from mobilizon_reshare.storage.query.write import save_publication_report
from mobilizon_reshare.publishers.coordinator import DryRunPublisherCoordinator
from mobilizon_reshare.publishers.coordinators.event_publishing.dry_run import (
DryRunPublisherCoordinator,
)
logger = logging.getLogger(__name__)

View File

@ -7,17 +7,21 @@ from mobilizon_reshare.config.command import CommandConfig
from mobilizon_reshare.event.event import EventPublicationStatus, MobilizonEvent
from mobilizon_reshare.publishers import get_active_publishers
from mobilizon_reshare.publishers.abstract import RecapPublication
from mobilizon_reshare.publishers.coordinator import (
RecapCoordinator,
from mobilizon_reshare.publishers.coordinators.event_publishing.notify import (
PublicationFailureNotifiersCoordinator,
BaseCoordinatorReport,
)
from mobilizon_reshare.publishers.coordinators.recap_publishing.recap import (
RecapCoordinator,
)
from mobilizon_reshare.publishers.coordinators import BaseCoordinatorReport
from mobilizon_reshare.publishers.platforms.platform_mapping import (
get_publisher_class,
get_formatter_class,
)
from mobilizon_reshare.storage.query.read import events_with_status
from mobilizon_reshare.publishers.coordinator import DryRunRecapCoordinator
from mobilizon_reshare.publishers.coordinators.recap_publishing.dry_run import (
DryRunRecapCoordinator,
)
logger = logging.getLogger(__name__)

View File

@ -3,7 +3,7 @@ from typing import Optional
from uuid import UUID
from mobilizon_reshare.main.publish import publish_publications
from mobilizon_reshare.publishers.coordinator import (
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
PublisherCoordinatorReport,
)
from mobilizon_reshare.storage.query.exceptions import EventNotFound

View File

@ -3,7 +3,9 @@ import logging.config
from mobilizon_reshare.config.command import CommandConfig
from mobilizon_reshare.main.publish import select_and_publish
from mobilizon_reshare.main.pull import pull
from mobilizon_reshare.publishers.coordinator import PublisherCoordinatorReport
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
PublisherCoordinatorReport,
)
logger = logging.getLogger(__name__)

View File

@ -1,299 +0,0 @@
import dataclasses
import logging
from abc import abstractmethod, ABC
from dataclasses import dataclass
from typing import List, Optional, Sequence
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers import get_active_notifiers
from mobilizon_reshare.publishers.abstract import (
EventPublication,
AbstractPlatform,
RecapPublication,
)
from mobilizon_reshare.publishers.exceptions import PublisherError
from mobilizon_reshare.publishers.platforms.platform_mapping import get_notifier_class
logger = logging.getLogger(__name__)
@dataclass
class BasePublicationReport:
status: PublicationStatus
reason: Optional[str]
@property
def succesful(self):
return self.status == PublicationStatus.COMPLETED
def get_failure_message(self):
return (
f"Publication failed with status: {self.status}.\n" f"Reason: {self.reason}"
)
@dataclass
class RecapPublicationReport(BasePublicationReport):
publication: RecapPublication
published_content: Optional[str] = dataclasses.field(default=None)
@dataclass
class EventPublicationReport(BasePublicationReport):
publication: EventPublication
published_content: Optional[str] = dataclasses.field(default=None)
def get_failure_message(self):
if not self.reason:
logger.error("Report of failure without reason.", exc_info=True)
return (
f"Publication {self.publication.id} failed with status: {self.status}.\n"
f"Reason: {self.reason}\n"
f"Publisher: {self.publication.publisher.name}\n"
f"Event: {self.publication.event.name}"
)
@dataclass
class BaseCoordinatorReport:
reports: Sequence[BasePublicationReport]
@property
def successful(self):
return all(r.status == PublicationStatus.COMPLETED for r in self.reports)
@dataclass
class RecapCoordinatorReport(BaseCoordinatorReport):
reports: Sequence[RecapPublicationReport]
def __str__(self):
platform_messages = []
for report in self.reports:
intro = f"Message for: {report.publication.publisher.name}"
platform_messages.append(
f"""{intro}
{"*"*len(intro)}
{report.published_content}
{"-"*80}"""
)
return "\n".join(platform_messages)
@dataclass
class PublisherCoordinatorReport(BaseCoordinatorReport):
reports: Sequence[EventPublicationReport]
publications: Sequence[EventPublication] = dataclasses.field(default_factory=list)
def __str__(self):
platform_messages = []
for report in self.reports:
intro = f"Message for: {report.publication.publisher.name}"
platform_messages.append(
f"""{intro}
{"*"*len(intro)}
{report.published_content}
{"-"*80}"""
)
return "\n".join(platform_messages)
class PublisherCoordinator:
def __init__(self, publications: List[EventPublication]):
self.publications = publications
def run(self) -> PublisherCoordinatorReport:
errors = self._validate()
if errors:
return PublisherCoordinatorReport(
reports=errors, publications=self.publications
)
return self._post()
def _post(self) -> PublisherCoordinatorReport:
reports = []
for publication in self.publications:
try:
logger.info(f"Publishing to {publication.publisher.name}")
message = publication.formatter.get_message_from_event(
publication.event
)
publication.publisher.send(message, publication.event)
reports.append(
EventPublicationReport(
status=PublicationStatus.COMPLETED,
publication=publication,
reason=None,
published_content=message,
)
)
except PublisherError as e:
logger.error(str(e))
reports.append(
EventPublicationReport(
status=PublicationStatus.FAILED,
reason=str(e),
publication=publication,
)
)
return PublisherCoordinatorReport(
publications=self.publications, reports=reports
)
def _safe_run(self, reasons, f, *args, **kwargs):
try:
f(*args, **kwargs)
return reasons
except Exception as e:
return reasons + [str(e)]
def _validate(self):
errors = []
for publication in self.publications:
reasons = []
reasons = self._safe_run(
reasons, publication.publisher.validate_credentials,
)
reasons = self._safe_run(
reasons, publication.formatter.validate_event, publication.event
)
if len(reasons) > 0:
errors.append(
EventPublicationReport(
status=PublicationStatus.FAILED,
reason=", ".join(reasons),
publication=publication,
)
)
return errors
class DryRunPublisherCoordinator(PublisherCoordinator):
def __init__(self, publications: List[EventPublication]):
self.publications = publications
def run(self) -> PublisherCoordinatorReport:
errors = self._validate()
if errors:
coord_report = PublisherCoordinatorReport(
reports=errors, publications=self.publications
)
else:
reports = [
EventPublicationReport(
status=PublicationStatus.COMPLETED,
publication=publication,
reason=None,
published_content=publication.formatter.get_message_from_event(
publication.event
),
)
for publication in self.publications
]
coord_report = PublisherCoordinatorReport(
publications=self.publications, reports=reports
)
return coord_report
class Sender:
def __init__(self, message: str, platforms: List[AbstractPlatform] = None):
self.message = message
self.platforms = platforms
def send_to_all(self):
for platform in self.platforms:
try:
platform.send(self.message)
except Exception as e:
logger.critical(f"Failed to send message:\n{self.message}")
logger.exception(e)
class AbstractNotifiersCoordinator(ABC):
def __init__(
self, report: EventPublicationReport, notifiers: List[AbstractPlatform] = None
):
self.platforms = notifiers or [
get_notifier_class(notifier)() for notifier in get_active_notifiers()
]
self.report = report
@abstractmethod
def notify_failure(self):
pass
class PublicationFailureNotifiersCoordinator(AbstractNotifiersCoordinator):
"""
Sends a notification of a failure report to the active platforms
"""
def notify_failure(self):
logger.info("Sending failure notifications")
if self.report.status == PublicationStatus.FAILED:
Sender(self.report.get_failure_message(), self.platforms).send_to_all()
class PublicationFailureLoggerCoordinator(PublicationFailureNotifiersCoordinator):
"""
Logs a report to console
"""
def notify_failure(self):
if self.report.status == PublicationStatus.FAILED:
logger.error(self.report.get_failure_message())
class RecapCoordinator:
def __init__(self, recap_publications: List[RecapPublication]):
self.recap_publications = recap_publications
def _build_recap_content(self, recap_publication: RecapPublication):
fragments = [recap_publication.formatter.get_recap_header()]
for event in recap_publication.events:
fragments.append(recap_publication.formatter.get_recap_fragment(event))
return "\n\n".join(fragments)
def _send(self, content, recap_publication):
recap_publication.publisher.send(content)
def run(self) -> RecapCoordinatorReport:
reports = []
for recap_publication in self.recap_publications:
try:
message = self._build_recap_content(recap_publication)
self._send(message, recap_publication)
reports.append(
RecapPublicationReport(
status=PublicationStatus.COMPLETED,
reason=None,
published_content=message,
publication=recap_publication,
)
)
except PublisherError as e:
reports.append(
RecapPublicationReport(
status=PublicationStatus.FAILED,
reason=str(e),
publication=recap_publication,
)
)
return RecapCoordinatorReport(reports=reports)
class DryRunRecapCoordinator(RecapCoordinator):
def _send(self, content, recap_publication):
pass

View File

@ -0,0 +1,32 @@
import logging
from dataclasses import dataclass
from typing import Optional, Sequence
from mobilizon_reshare.models.publication import PublicationStatus
@dataclass
class BasePublicationReport:
status: PublicationStatus
reason: Optional[str]
@property
def succesful(self):
return self.status == PublicationStatus.COMPLETED
def get_failure_message(self):
return (
f"Publication failed with status: {self.status}.\n" f"Reason: {self.reason}"
)
@dataclass
class BaseCoordinatorReport:
reports: Sequence[BasePublicationReport]
@property
def successful(self):
return all(r.status == PublicationStatus.COMPLETED for r in self.reports)
logger = logging.getLogger(__name__)

View File

@ -0,0 +1,63 @@
import dataclasses
import logging
from dataclasses import dataclass
from typing import List, Optional
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers.abstract import EventPublication
from mobilizon_reshare.publishers.coordinators import BasePublicationReport
logger = logging.getLogger(__name__)
@dataclass
class EventPublicationReport(BasePublicationReport):
publication: EventPublication
published_content: Optional[str] = dataclasses.field(default=None)
def get_failure_message(self):
if not self.reason:
logger.error("Report of failure without reason.", exc_info=True)
return (
f"Publication {self.publication.id} failed with status: {self.status}.\n"
f"Reason: {self.reason}\n"
f"Publisher: {self.publication.publisher.name}\n"
f"Event: {self.publication.event.name}"
)
class BaseEventPublishingCoordinator:
def __init__(self, publications: List[EventPublication]):
self.publications = publications
def _safe_run(self, reasons, f, *args, **kwargs):
try:
f(*args, **kwargs)
return reasons
except Exception as e:
return reasons + [str(e)]
def _validate(self):
errors = []
for publication in self.publications:
reasons = []
reasons = self._safe_run(
reasons, publication.publisher.validate_credentials,
)
reasons = self._safe_run(
reasons, publication.formatter.validate_event, publication.event
)
if len(reasons) > 0:
errors.append(
EventPublicationReport(
status=PublicationStatus.FAILED,
reason=", ".join(reasons),
publication=publication,
)
)
return errors

View File

@ -0,0 +1,38 @@
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers.coordinators.event_publishing import (
BaseEventPublishingCoordinator,
)
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
PublisherCoordinatorReport,
EventPublicationReport,
)
class DryRunPublisherCoordinator(BaseEventPublishingCoordinator):
"""
Coordinator to perform a dry-run on the event publication
"""
def run(self) -> PublisherCoordinatorReport:
errors = self._validate()
if errors:
coord_report = PublisherCoordinatorReport(
reports=errors, publications=self.publications
)
else:
reports = [
EventPublicationReport(
status=PublicationStatus.COMPLETED,
publication=publication,
reason=None,
published_content=publication.formatter.get_message_from_event(
publication.event
),
)
for publication in self.publications
]
coord_report = PublisherCoordinatorReport(
publications=self.publications, reports=reports
)
return coord_report

View File

@ -0,0 +1,60 @@
from abc import ABC, abstractmethod
from typing import List
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers import get_active_notifiers
from mobilizon_reshare.publishers.abstract import AbstractPlatform
from mobilizon_reshare.publishers.coordinators import logger
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
EventPublicationReport,
)
from mobilizon_reshare.publishers.platforms.platform_mapping import get_notifier_class
class Sender:
def __init__(self, message: str, platforms: List[AbstractPlatform] = None):
self.message = message
self.platforms = platforms
def send_to_all(self):
for platform in self.platforms:
try:
platform.send(self.message)
except Exception as e:
logger.critical(f"Failed to send message:\n{self.message}")
logger.exception(e)
class AbstractNotifiersCoordinator(ABC):
def __init__(
self, report: EventPublicationReport, notifiers: List[AbstractPlatform] = None
):
self.platforms = notifiers or [
get_notifier_class(notifier)() for notifier in get_active_notifiers()
]
self.report = report
@abstractmethod
def notify_failure(self):
pass
class PublicationFailureNotifiersCoordinator(AbstractNotifiersCoordinator):
"""
Sends a notification of a failure report to the active platforms
"""
def notify_failure(self):
logger.info("Sending failure notifications")
if self.report.status == PublicationStatus.FAILED:
Sender(self.report.get_failure_message(), self.platforms).send_to_all()
class PublicationFailureLoggerCoordinator(PublicationFailureNotifiersCoordinator):
"""
Logs a report to console
"""
def notify_failure(self):
if self.report.status == PublicationStatus.FAILED:
logger.error(self.report.get_failure_message())

View File

@ -0,0 +1,87 @@
import dataclasses
from dataclasses import dataclass
from typing import Sequence
import logging
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers.abstract import EventPublication
from mobilizon_reshare.publishers.coordinators import BaseCoordinatorReport
from mobilizon_reshare.publishers.coordinators.event_publishing import (
BaseEventPublishingCoordinator,
EventPublicationReport,
)
from mobilizon_reshare.publishers.exceptions import PublisherError
logger = logging.getLogger(__name__)
@dataclass
class PublisherCoordinatorReport(BaseCoordinatorReport):
reports: Sequence[EventPublicationReport]
publications: Sequence[EventPublication] = dataclasses.field(default_factory=list)
def __str__(self):
platform_messages = []
for report in self.reports:
intro = f"Message for: {report.publication.publisher.name}"
platform_messages.append(
f"""{intro}
{"*" * len(intro)}
{report.published_content}
{"-" * 80}"""
)
return "\n".join(platform_messages)
class PublisherCoordinator(BaseEventPublishingCoordinator):
"""
Coordinator to publish an event on every active platform
"""
def run(self) -> PublisherCoordinatorReport:
errors = self._validate()
if errors:
return PublisherCoordinatorReport(
reports=errors, publications=self.publications
)
return self._publish()
def _publish(self) -> PublisherCoordinatorReport:
reports = []
for publication in self.publications:
try:
publication_report = self._publish_publication(publication)
reports.append(publication_report)
except PublisherError as e:
logger.error(str(e))
reports.append(
EventPublicationReport(
status=PublicationStatus.FAILED,
reason=str(e),
publication=publication,
)
)
return PublisherCoordinatorReport(
publications=self.publications, reports=reports
)
@staticmethod
def _publish_publication(publication):
"""
Publishes a single publication
:param publication:
:return:
"""
logger.info("Publishing to %s", publication.publisher.name)
message = publication.formatter.get_message_from_event(publication.event)
publication.publisher.send(message, publication.event)
return EventPublicationReport(
status=PublicationStatus.COMPLETED,
publication=publication,
reason=None,
published_content=message,
)

View File

@ -0,0 +1,20 @@
from mobilizon_reshare.publishers.coordinators.recap_publishing.recap import (
RecapCoordinator,
)
class DryRunRecapCoordinator(RecapCoordinator):
"""
Coordinator to perform a dry-run on the event recap
"""
def _send(self, content, recap_publication):
"""
Overrides the Recap Coordinator _send on the assumption that _send is just a side effect publishing
on a given platform. The report generated by RecapCoordinator should be sufficient to perform
the dry-run print at CLI level.
:param content:
:param recap_publication:
:return:
"""
pass

View File

@ -0,0 +1,78 @@
import dataclasses
from dataclasses import dataclass
from typing import Optional, Sequence, List
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers.abstract import RecapPublication
from mobilizon_reshare.publishers.coordinators import (
BasePublicationReport,
BaseCoordinatorReport,
)
from mobilizon_reshare.publishers.exceptions import PublisherError
@dataclass
class RecapPublicationReport(BasePublicationReport):
publication: RecapPublication
published_content: Optional[str] = dataclasses.field(default=None)
@dataclass
class RecapCoordinatorReport(BaseCoordinatorReport):
reports: Sequence[RecapPublicationReport]
def __str__(self):
platform_messages = []
for report in self.reports:
intro = f"Message for: {report.publication.publisher.name}"
platform_messages.append(
f"""{intro}
{"*"*len(intro)}
{report.published_content}
{"-"*80}"""
)
return "\n".join(platform_messages)
class RecapCoordinator:
"""
Coordinator to publish a recap on future events
"""
def __init__(self, recap_publications: List[RecapPublication]):
self.recap_publications = recap_publications
def _build_recap_content(self, recap_publication: RecapPublication):
fragments = [recap_publication.formatter.get_recap_header()]
for event in recap_publication.events:
fragments.append(recap_publication.formatter.get_recap_fragment(event))
return "\n\n".join(fragments)
def _send(self, content, recap_publication):
recap_publication.publisher.send(content)
def run(self) -> RecapCoordinatorReport:
reports = []
for recap_publication in self.recap_publications:
try:
message = self._build_recap_content(recap_publication)
self._send(message, recap_publication)
reports.append(
RecapPublicationReport(
status=PublicationStatus.COMPLETED,
reason=None,
published_content=message,
publication=recap_publication,
)
)
except PublisherError as e:
reports.append(
RecapPublicationReport(
status=PublicationStatus.FAILED,
reason=str(e),
publication=recap_publication,
)
)
return RecapCoordinatorReport(reports=reports)

View File

@ -63,8 +63,7 @@ async def events_with_status(
async def get_all_publications(
from_date: Optional[Arrow] = None,
to_date: Optional[Arrow] = None,
from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None,
) -> Iterable[Publication]:
return await prefetch_publication_relations(
_add_date_window(Publication.all(), "timestamp", from_date, to_date)
@ -72,8 +71,7 @@ async def get_all_publications(
async def get_all_events(
from_date: Optional[Arrow] = None,
to_date: Optional[Arrow] = None,
from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None,
) -> list[MobilizonEvent]:
return [
event_from_model(event)
@ -133,8 +131,7 @@ async def publications_with_status(
async def events_without_publications(
from_date: Optional[Arrow] = None,
to_date: Optional[Arrow] = None,
from_date: Optional[Arrow] = None, to_date: Optional[Arrow] = None,
) -> list[MobilizonEvent]:
query = Event.filter(publications__id=None)
events = await prefetch_event_relations(

View File

@ -8,7 +8,9 @@ from mobilizon_reshare.event.event import MobilizonEvent
from mobilizon_reshare.models.event import Event
from mobilizon_reshare.models.publication import Publication
from mobilizon_reshare.models.publisher import Publisher
from mobilizon_reshare.publishers.coordinator import PublisherCoordinatorReport
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
PublisherCoordinatorReport,
)
from mobilizon_reshare.storage.query.converter import event_to_model
from mobilizon_reshare.storage.query.read import (
events_without_publications,
@ -91,9 +93,7 @@ async def create_unpublished_events(
@atomic()
async def update_publishers(
names: Iterable[str],
) -> None:
async def update_publishers(names: Iterable[str],) -> None:
names = set(names)
known_publisher_names = set(p.name for p in await Publisher.all())
for name in names.difference(known_publisher_names):

View File

@ -8,7 +8,7 @@ import mobilizon_reshare.publishers
import mobilizon_reshare.storage.query.read
from mobilizon_reshare.models.publisher import Publisher
import mobilizon_reshare.main.recap
from mobilizon_reshare.publishers import coordinator
from mobilizon_reshare.publishers.coordinators.event_publishing import notify
from tests import today
from tests.conftest import event_1, event_0
@ -118,14 +118,10 @@ async def mock_publisher_config(monkeypatch, publisher_class, mock_formatter_cla
mobilizon_reshare.main.recap, "get_active_publishers", _mock_active_pub
)
monkeypatch.setattr(
mobilizon_reshare.main.recap,
"get_publisher_class",
_mock_pub_class,
mobilizon_reshare.main.recap, "get_publisher_class", _mock_pub_class,
)
monkeypatch.setattr(
mobilizon_reshare.main.recap,
"get_formatter_class",
_mock_format_class,
mobilizon_reshare.main.recap, "get_formatter_class", _mock_format_class,
)
return p
@ -142,9 +138,7 @@ async def mock_notifier_config(monkeypatch, publisher_class, mock_formatter_clas
return mock_formatter_class
monkeypatch.setattr(
coordinator,
"get_notifier_class",
_mock_notifier_class,
notify, "get_notifier_class", _mock_notifier_class,
)
monkeypatch.setattr(
mobilizon_reshare.publishers.platforms.platform_mapping,
@ -152,7 +146,7 @@ async def mock_notifier_config(monkeypatch, publisher_class, mock_formatter_clas
_mock_format_class,
)
monkeypatch.setattr(coordinator, "get_active_notifiers", _mock_active_notifier)
monkeypatch.setattr(notify, "get_active_notifiers", _mock_active_notifier)
@pytest.fixture

View File

@ -155,7 +155,7 @@ def initialize_db_tests(request) -> None:
await Tortoise.init(config)
try:
await Tortoise._drop_databases()
except: # pragma: nocoverage
except: # noqa
pass
await Tortoise.init(config, _create_db=True)
@ -197,9 +197,7 @@ def event_model_generator():
@pytest.fixture()
def publisher_model_generator():
def _publisher_model_generator(
idx=1,
):
def _publisher_model_generator(idx=1,):
return Publisher(name=f"publisher_{idx}", account_ref=f"account_ref_{idx}")
return _publisher_model_generator
@ -436,10 +434,7 @@ def mock_mobilizon_success_answer(mobilizon_answer, mobilizon_url):
with responses.RequestsMock() as rsps:
rsps.add(
responses.POST,
mobilizon_url,
json=mobilizon_answer,
status=200,
responses.POST, mobilizon_url, json=mobilizon_answer, status=200,
)
yield
@ -451,10 +446,7 @@ def mock_multiple_success_answer(multiple_answers, mobilizon_url):
for answer in multiple_answers:
rsps.add(
responses.POST,
mobilizon_url,
json=answer,
status=200,
responses.POST, mobilizon_url, json=answer, status=200,
)
yield

View File

@ -12,11 +12,15 @@ from mobilizon_reshare.models.publication import (
)
from mobilizon_reshare.models.publisher import Publisher
from mobilizon_reshare.publishers.abstract import EventPublication, RecapPublication
from mobilizon_reshare.publishers.coordinator import (
PublisherCoordinatorReport,
EventPublicationReport,
PublisherCoordinator,
from mobilizon_reshare.publishers.coordinators.event_publishing.notify import (
PublicationFailureNotifiersCoordinator,
)
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
EventPublicationReport,
PublisherCoordinatorReport,
PublisherCoordinator,
)
from mobilizon_reshare.publishers.coordinators.recap_publishing.recap import (
RecapCoordinator,
)
from mobilizon_reshare.storage.query.converter import (

View File

@ -4,7 +4,9 @@ import responses
from mobilizon_reshare.config.config import get_settings
from mobilizon_reshare.models.publication import PublicationStatus
from mobilizon_reshare.publishers.coordinator import PublisherCoordinator
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
PublisherCoordinator,
)
from mobilizon_reshare.publishers.exceptions import (
InvalidEvent,
InvalidResponse,
@ -88,9 +90,7 @@ def mocked_client_error_response():
@pytest.fixture
@pytest.mark.asyncio
async def setup_db(
generate_models
):
async def setup_db(generate_models):
settings = get_settings()
settings["publisher"]["zulip"][
"bot_email"

View File

@ -5,9 +5,9 @@ import pytest
from mobilizon_reshare.models.publication import PublicationStatus, Publication
from mobilizon_reshare.models.publisher import Publisher
from mobilizon_reshare.publishers.abstract import EventPublication
from mobilizon_reshare.publishers.coordinator import (
PublisherCoordinatorReport,
from mobilizon_reshare.publishers.coordinators.event_publishing.publish import (
EventPublicationReport,
PublisherCoordinatorReport,
)
from mobilizon_reshare.publishers.platforms.telegram import (
TelegramFormatter,
@ -18,9 +18,7 @@ from mobilizon_reshare.storage.query.write import (
update_publishers,
create_unpublished_events,
)
from tests.storage import (
complete_specification,
)
from tests.storage import complete_specification
from tests.conftest import event_6, event_0, event_1, event_2, event_3, event_3_updated
two_publishers_specification = {"publisher": ["telegram", "twitter"]}
@ -68,10 +66,7 @@ two_events_specification = {
],
)
async def test_update_publishers(
specification,
names,
expected_result,
generate_models,
specification, names, expected_result, generate_models,
):
await generate_models(specification)
await update_publishers(names)
@ -127,10 +122,7 @@ async def test_update_publishers(
],
)
async def test_create_unpublished_events(
specification,
events_from_mobilizon,
expected_result,
generate_models,
specification, events_from_mobilizon, expected_result, generate_models,
):
await generate_models(specification)
@ -171,11 +163,7 @@ async def test_create_unpublished_events(
],
)
async def test_save_publication_report(
specification,
report,
event,
expected_result,
generate_models,
specification, report, event, expected_result, generate_models,
):
await generate_models(specification)
known_publication_ids = set(p.id for p in await Publication.all())