vintasend-celery 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vintasend_celery-0.1.0/LICENSE.txt +21 -0
- vintasend_celery-0.1.0/PKG-INFO +36 -0
- vintasend_celery-0.1.0/README.md +19 -0
- vintasend_celery-0.1.0/pyproject.toml +161 -0
- vintasend_celery-0.1.0/vintasend_celery/__init__.py +0 -0
- vintasend_celery-0.1.0/vintasend_celery/services/__init__.py +0 -0
- vintasend_celery-0.1.0/vintasend_celery/services/notification_adapters/__init__.py +0 -0
- vintasend_celery-0.1.0/vintasend_celery/services/notification_adapters/celery_adapter_factory.py +92 -0
- vintasend_celery-0.1.0/vintasend_celery/tasks/__init__.py +0 -0
- vintasend_celery-0.1.0/vintasend_celery/tasks/background_tasks.py +11 -0
- vintasend_celery-0.1.0/vintasend_celery/tasks/periodic_tasks.py +6 -0
- vintasend_celery-0.1.0/vintasend_celery/tests/__init__.py +0 -0
- vintasend_celery-0.1.0/vintasend_celery/tests/test_services/__init__.py +0 -0
- vintasend_celery-0.1.0/vintasend_celery/tests/test_services/test_adapters.py +136 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2017 Vinta Serviços e Soluções Tecnológicas Ltda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: vintasend-celery
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Celery adapter for VintaSend
|
|
5
|
+
License: MIT
|
|
6
|
+
Author: Hugo bessa
|
|
7
|
+
Author-email: hugo@vinta.com.br
|
|
8
|
+
Requires-Python: >=3.10,<3.13
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Requires-Dist: celery (>=5.4.0,<6.0.0)
|
|
15
|
+
Requires-Dist: vintasend
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# VintaSend Celery
|
|
19
|
+
|
|
20
|
+
Celery adapter base implementation for VintaSend.
|
|
21
|
+
|
|
22
|
+
This package doesn't implement an actual adapter, but it helps you to make an existing adapter to run from a Celery worker.
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
To install VintaSend Celery you need to run the following command:
|
|
28
|
+
|
|
29
|
+
```shell
|
|
30
|
+
pip install vinstasend vintasend-celery
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
## Quick start
|
|
35
|
+
|
|
36
|
+
To use VintaSend Celery you need to manually register your tasks
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# VintaSend Celery
|
|
2
|
+
|
|
3
|
+
Celery adapter base implementation for VintaSend.
|
|
4
|
+
|
|
5
|
+
This package doesn't implement an actual adapter, but it helps you to make an existing adapter to run from a Celery worker.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
To install VintaSend Celery you need to run the following command:
|
|
11
|
+
|
|
12
|
+
```shell
|
|
13
|
+
pip install vinstasend vintasend-celery
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## Quick start
|
|
18
|
+
|
|
19
|
+
To use VintaSend Celery you need to manually register your tasks
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "vintasend-celery"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Celery adapter for VintaSend"
|
|
5
|
+
authors = ["Hugo bessa <hugo@vinta.com.br>"]
|
|
6
|
+
license = "MIT"
|
|
7
|
+
readme = "README.md"
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "<3.13,>=3.10"
|
|
11
|
+
vintasend = "*"
|
|
12
|
+
celery = "^5.4.0"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
[tool.poetry.group.dev.dependencies]
|
|
16
|
+
coverage = "^7.6.4"
|
|
17
|
+
tox = "^4.23.2"
|
|
18
|
+
pytest = "^8.3.3"
|
|
19
|
+
pytest-xdist = {version = "^3.6.1", extras=["psutil"]}
|
|
20
|
+
coveralls = "^4.0.1"
|
|
21
|
+
pytest-cov = "^6.0.0"
|
|
22
|
+
pytest-celery = "^1.1.3"
|
|
23
|
+
|
|
24
|
+
[build-system]
|
|
25
|
+
requires = ["poetry-core"]
|
|
26
|
+
build-backend = "poetry.core.masonry.api"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
[tool.ruff]
|
|
30
|
+
select = [
|
|
31
|
+
# pycodestyle
|
|
32
|
+
"E",
|
|
33
|
+
# Pyflakes
|
|
34
|
+
"F",
|
|
35
|
+
# pep8-naming
|
|
36
|
+
"N",
|
|
37
|
+
# pyupgrade
|
|
38
|
+
"UP",
|
|
39
|
+
# flake8-bugbear
|
|
40
|
+
"B",
|
|
41
|
+
# flake8-bandit
|
|
42
|
+
"S",
|
|
43
|
+
# flake8-blind-except
|
|
44
|
+
"BLE",
|
|
45
|
+
# flake8-builtins
|
|
46
|
+
"A",
|
|
47
|
+
# flake8-django
|
|
48
|
+
"DJ",
|
|
49
|
+
# isort
|
|
50
|
+
"I",
|
|
51
|
+
# flake8-logging-format
|
|
52
|
+
"G",
|
|
53
|
+
# flake8-no-pep420
|
|
54
|
+
"INP",
|
|
55
|
+
# Ruff-specific rules
|
|
56
|
+
"RUF",
|
|
57
|
+
]
|
|
58
|
+
exclude = [
|
|
59
|
+
".bzr",
|
|
60
|
+
".direnv",
|
|
61
|
+
".eggs",
|
|
62
|
+
".git",
|
|
63
|
+
".git-rewrite",
|
|
64
|
+
".hg",
|
|
65
|
+
".mypy_cache",
|
|
66
|
+
".nox",
|
|
67
|
+
".pants.d",
|
|
68
|
+
".pytype",
|
|
69
|
+
".ruff_cache",
|
|
70
|
+
".svn",
|
|
71
|
+
".tox",
|
|
72
|
+
".venv",
|
|
73
|
+
"__pypackages__",
|
|
74
|
+
"_build",
|
|
75
|
+
"buck-out",
|
|
76
|
+
"build",
|
|
77
|
+
"dist",
|
|
78
|
+
"node_modules",
|
|
79
|
+
"venv",
|
|
80
|
+
"virtualenvs",
|
|
81
|
+
"*/migrations/*",
|
|
82
|
+
]
|
|
83
|
+
ignore = [
|
|
84
|
+
# Disable eradicate (commented code removal)
|
|
85
|
+
"ERA001",
|
|
86
|
+
# Disable Conflicting lint rules,
|
|
87
|
+
# see https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
|
|
88
|
+
"W191",
|
|
89
|
+
"E501",
|
|
90
|
+
"E111",
|
|
91
|
+
"E117",
|
|
92
|
+
"D206",
|
|
93
|
+
"D300",
|
|
94
|
+
"Q000",
|
|
95
|
+
"Q001",
|
|
96
|
+
"Q002",
|
|
97
|
+
"Q003",
|
|
98
|
+
"COM812",
|
|
99
|
+
"COM819",
|
|
100
|
+
"ISC001",
|
|
101
|
+
"ISC002",
|
|
102
|
+
# Allow `except Exception`:
|
|
103
|
+
"BLE001",
|
|
104
|
+
# Disable unused `noqa` directive
|
|
105
|
+
"RUF100",
|
|
106
|
+
# Disable pyupgrade UP rules that conflict with django-ninja
|
|
107
|
+
"UP006",
|
|
108
|
+
"UP035",
|
|
109
|
+
"UP037",
|
|
110
|
+
"UP040",
|
|
111
|
+
]
|
|
112
|
+
line-length = 100
|
|
113
|
+
indent-width = 4
|
|
114
|
+
target-version = "py312"
|
|
115
|
+
# Allow unused variables when underscore-prefixed:
|
|
116
|
+
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
|
117
|
+
|
|
118
|
+
[tool.ruff.pycodestyle]
|
|
119
|
+
ignore-overlong-task-comments = true
|
|
120
|
+
|
|
121
|
+
[tool.ruff.lint.isort]
|
|
122
|
+
section-order = [
|
|
123
|
+
"future",
|
|
124
|
+
"standard-library",
|
|
125
|
+
"django",
|
|
126
|
+
"third-party",
|
|
127
|
+
"first-party",
|
|
128
|
+
"local-folder",
|
|
129
|
+
]
|
|
130
|
+
lines-after-imports = 2
|
|
131
|
+
|
|
132
|
+
[tool.ruff.lint.isort.sections]
|
|
133
|
+
# Group all Django imports into a separate section.
|
|
134
|
+
"django" = ["django"]
|
|
135
|
+
|
|
136
|
+
[tool.ruff.per-file-ignores]
|
|
137
|
+
# Ignore "E402", "F403", "F405" (import violations) in __init__.py files.
|
|
138
|
+
# Ignore "S" (flake8-bandit) and "N802" (function name should be lowercase) in tests and docs.
|
|
139
|
+
# Ignore "RUF" (Ruff-specific rules) and "I" (isort) in migrations.
|
|
140
|
+
"__init__.py" = ["E402", "F403", "F405"]
|
|
141
|
+
"**/{tests,docs}/*" = ["E402", "F403", "F405", "S", "N802"]
|
|
142
|
+
"**/*test*.py" = ["E402", "F403", "F405", "S", "N802"]
|
|
143
|
+
"**/{settings}/*" = ["E402", "F403", "F405"]
|
|
144
|
+
"**/migrations/*" = ["RUF", "I"]
|
|
145
|
+
|
|
146
|
+
[tool.coverage.run]
|
|
147
|
+
branch = true
|
|
148
|
+
source = ["backend"]
|
|
149
|
+
omit = [
|
|
150
|
+
"**/venv/*",
|
|
151
|
+
"**/env/*",
|
|
152
|
+
"**/virtualenvs/*",
|
|
153
|
+
"**/node_modules/*",
|
|
154
|
+
"**/migrations/*",
|
|
155
|
+
"**/settings/*",
|
|
156
|
+
"**/tests/*",
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
[tool.pytest.ini_options]
|
|
160
|
+
python_files = ["test_*.py"]
|
|
161
|
+
addopts = "--dist=loadscope"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
vintasend_celery-0.1.0/vintasend_celery/services/notification_adapters/celery_adapter_factory.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import uuid
|
|
3
|
+
from typing import Generic, TypeVar, cast
|
|
4
|
+
|
|
5
|
+
from celery import Task # type: ignore
|
|
6
|
+
from vintasend.services.dataclasses import Notification, NotificationContextDict
|
|
7
|
+
from vintasend.services.notification_adapters.async_base import (
|
|
8
|
+
AsyncBaseNotificationAdapter,
|
|
9
|
+
NotificationDict,
|
|
10
|
+
)
|
|
11
|
+
from vintasend.services.notification_backends.base import BaseNotificationBackend
|
|
12
|
+
from vintasend.services.notification_template_renderers.base import BaseNotificationTemplateRenderer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
B = TypeVar("B", bound=BaseNotificationBackend)
|
|
16
|
+
T = TypeVar("T", bound=BaseNotificationTemplateRenderer)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CeleryNotificationAdapter(Generic[B, T], AsyncBaseNotificationAdapter[B, T]):
|
|
20
|
+
send_notification_task: Task
|
|
21
|
+
|
|
22
|
+
def delayed_send(self, notification_dict: NotificationDict, context_dict: dict) -> None:
|
|
23
|
+
notification = self.notification_from_dict(notification_dict)
|
|
24
|
+
context = NotificationContextDict(**context_dict)
|
|
25
|
+
super().send(notification, context) # type: ignore
|
|
26
|
+
|
|
27
|
+
def notification_to_dict(self, notification: "Notification") -> NotificationDict:
|
|
28
|
+
non_serializable_fields = ["send_after"]
|
|
29
|
+
serialized_notification = {}
|
|
30
|
+
for field in notification.__dataclass_fields__.keys():
|
|
31
|
+
if field in non_serializable_fields:
|
|
32
|
+
continue
|
|
33
|
+
serialized_notification[field] = getattr(notification, field)
|
|
34
|
+
|
|
35
|
+
serialized_notification["send_after"] = (
|
|
36
|
+
notification.send_after.isoformat() if notification.send_after else None
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
return cast(NotificationDict, serialized_notification)
|
|
40
|
+
|
|
41
|
+
def _convert_to_uuid(self, value: str) -> uuid.UUID | str:
|
|
42
|
+
try:
|
|
43
|
+
return uuid.UUID(value)
|
|
44
|
+
except ValueError:
|
|
45
|
+
return value
|
|
46
|
+
|
|
47
|
+
def notification_from_dict(self, notification_dict: NotificationDict) -> "Notification":
|
|
48
|
+
send_after = (
|
|
49
|
+
datetime.datetime.fromisoformat(notification_dict["send_after"])
|
|
50
|
+
if notification_dict["send_after"]
|
|
51
|
+
else None
|
|
52
|
+
)
|
|
53
|
+
return Notification(
|
|
54
|
+
id=(
|
|
55
|
+
self._convert_to_uuid(notification_dict["id"])
|
|
56
|
+
if isinstance(notification_dict["id"], str)
|
|
57
|
+
else notification_dict["id"]
|
|
58
|
+
),
|
|
59
|
+
user_id=(
|
|
60
|
+
self._convert_to_uuid(notification_dict["user_id"])
|
|
61
|
+
if isinstance(notification_dict["user_id"], str)
|
|
62
|
+
else notification_dict["user_id"]
|
|
63
|
+
),
|
|
64
|
+
context_kwargs={
|
|
65
|
+
key: self._convert_to_uuid(value) if isinstance(value, str) else value
|
|
66
|
+
for key, value in notification_dict["context_kwargs"].items()
|
|
67
|
+
},
|
|
68
|
+
notification_type=notification_dict["notification_type"],
|
|
69
|
+
title=notification_dict["title"],
|
|
70
|
+
body_template=notification_dict["body_template"],
|
|
71
|
+
context_name=notification_dict["context_name"],
|
|
72
|
+
subject_template=notification_dict["subject_template"],
|
|
73
|
+
preheader_template=notification_dict["preheader_template"],
|
|
74
|
+
status=notification_dict["status"],
|
|
75
|
+
context_used=notification_dict["context_used"],
|
|
76
|
+
send_after=send_after,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def send(self, notification: "Notification", context: "NotificationContextDict") -> None:
|
|
80
|
+
self.send_notification_task.delay(
|
|
81
|
+
notification=self.notification_to_dict(notification),
|
|
82
|
+
context=context,
|
|
83
|
+
backend=self.backend.backend_import_str,
|
|
84
|
+
adapters=[
|
|
85
|
+
(
|
|
86
|
+
self.adapter_import_str,
|
|
87
|
+
self.template_renderer.template_renderer_import_str,
|
|
88
|
+
)
|
|
89
|
+
],
|
|
90
|
+
backend_kwargs=self.backend.backend_kwargs,
|
|
91
|
+
config=self.serialize_config(),
|
|
92
|
+
)
|
|
File without changes
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
|
|
3
|
+
from celery import Celery # type: ignore
|
|
4
|
+
from vintasend.tasks.background_tasks import send_notification
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def send_notification_task_factory(celery_app: Celery):
|
|
11
|
+
return celery_app.task(send_notification)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from unittest.mock import patch
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
from vintasend.constants import NotificationStatus, NotificationTypes
|
|
6
|
+
from vintasend.services.dataclasses import Notification
|
|
7
|
+
from vintasend.services.notification_backends.stubs.fake_backend import (
|
|
8
|
+
Config,
|
|
9
|
+
FakeFileBackend,
|
|
10
|
+
FakeFileBackendWithNonSerializableKWArgs,
|
|
11
|
+
)
|
|
12
|
+
from vintasend.services.notification_service import NotificationService, register_context
|
|
13
|
+
from vintasend.services.notification_template_renderers.stubs.fake_templated_email_renderer import (
|
|
14
|
+
FakeTemplateRenderer,
|
|
15
|
+
FakeTemplateRendererWithException,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
from example_app.celery import (
|
|
19
|
+
AsyncCeleryFakeEmailAdapter,
|
|
20
|
+
AsyncCeleryFakeEmailAdapterWithBackendWithNonSerializableKWArgs,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@pytest.fixture()
|
|
25
|
+
def celery_app():
|
|
26
|
+
from example_app.celery import celery_app
|
|
27
|
+
celery_app.conf.update(task_always_eager=True)
|
|
28
|
+
return celery_app
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@pytest.fixture(scope="function")
|
|
32
|
+
def notification_backend():
|
|
33
|
+
backend = FakeFileBackend(database_file_name="celery-adapter-tests-notifications.json")
|
|
34
|
+
yield backend
|
|
35
|
+
backend.clear()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture()
|
|
39
|
+
def renderer():
|
|
40
|
+
return FakeTemplateRenderer()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.fixture()
|
|
44
|
+
def notification_service(notification_backend, renderer):
|
|
45
|
+
async_adapter = AsyncCeleryFakeEmailAdapter(
|
|
46
|
+
template_renderer=renderer, backend=notification_backend
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
notification_service = NotificationService[AsyncCeleryFakeEmailAdapter, FakeFileBackend](
|
|
50
|
+
[async_adapter],
|
|
51
|
+
notification_backend,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return notification_service
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def create_notification():
|
|
58
|
+
register_context("test_context")(create_notification_context)
|
|
59
|
+
return Notification(
|
|
60
|
+
id=uuid.uuid4(),
|
|
61
|
+
user_id=1,
|
|
62
|
+
notification_type=NotificationTypes.EMAIL.value,
|
|
63
|
+
title="Test Notification",
|
|
64
|
+
body_template="vintasend_django/emails/test/test_templated_email_body.html",
|
|
65
|
+
context_name="test_context",
|
|
66
|
+
context_kwargs={"test": "test"},
|
|
67
|
+
send_after=None,
|
|
68
|
+
subject_template="vintasend_django/emails/test/test_templated_email_subject.txt",
|
|
69
|
+
preheader_template="vintasend_django/emails/test/test_templated_email_preheader.html",
|
|
70
|
+
status=NotificationStatus.PENDING_SEND.value,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def create_notification_context(test):
|
|
75
|
+
if test != "test":
|
|
76
|
+
raise ValueError("Invalid test value")
|
|
77
|
+
return {"foo": "bar"}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def test_send_notification(notification_backend, notification_service, celery_app, celery_worker):
|
|
81
|
+
notification = create_notification()
|
|
82
|
+
notification_backend.notifications.append(notification)
|
|
83
|
+
notification_backend._store_notifications()
|
|
84
|
+
|
|
85
|
+
notification_service.send(notification)
|
|
86
|
+
assert len(notification_backend.notifications) == 1
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_send_notification_with_render_error(notification_backend, celery_app, celery_worker):
|
|
90
|
+
notification = create_notification()
|
|
91
|
+
|
|
92
|
+
renderer = FakeTemplateRendererWithException()
|
|
93
|
+
async_adapter = AsyncCeleryFakeEmailAdapter(
|
|
94
|
+
template_renderer=renderer, backend=notification_backend
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
notification_service = NotificationService(
|
|
98
|
+
[async_adapter],
|
|
99
|
+
notification_backend,
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
notification_backend.notifications.append(notification)
|
|
103
|
+
notification_backend._store_notifications()
|
|
104
|
+
|
|
105
|
+
with patch("vintasend.tasks.background_tasks.logger.exception") as mock_log_exception:
|
|
106
|
+
notification_service.send(notification)
|
|
107
|
+
|
|
108
|
+
mock_log_exception.assert_called_once()
|
|
109
|
+
|
|
110
|
+
assert len(async_adapter.sent_emails) == 0
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_backend_with_non_serializable_kwargs(renderer, celery_app, celery_worker):
|
|
114
|
+
notification = create_notification()
|
|
115
|
+
config = Config()
|
|
116
|
+
backend = FakeFileBackendWithNonSerializableKWArgs(
|
|
117
|
+
database_file_name="celery-adapter-tests-notifications.json",
|
|
118
|
+
config=config,
|
|
119
|
+
)
|
|
120
|
+
async_adapter = AsyncCeleryFakeEmailAdapterWithBackendWithNonSerializableKWArgs(
|
|
121
|
+
template_renderer=renderer, backend=backend, config=config
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
notification_service = NotificationService(
|
|
125
|
+
[async_adapter],
|
|
126
|
+
backend,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
backend.notifications.append(notification)
|
|
130
|
+
backend._store_notifications()
|
|
131
|
+
|
|
132
|
+
notification_service.send(notification)
|
|
133
|
+
|
|
134
|
+
assert len(backend.notifications) == 1
|
|
135
|
+
|
|
136
|
+
backend.clear()
|