aa-killtracker 0.17.0__py3-none-any.whl → 0.18.0a2__py3-none-any.whl
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.
- {aa_killtracker-0.17.0.dist-info → aa_killtracker-0.18.0a2.dist-info}/METADATA +1 -1
- {aa_killtracker-0.17.0.dist-info → aa_killtracker-0.18.0a2.dist-info}/RECORD +28 -24
- killtracker/__init__.py +1 -1
- killtracker/admin.py +13 -8
- killtracker/app_settings.py +7 -4
- killtracker/apps.py +2 -4
- killtracker/core/discord_messages.py +129 -55
- killtracker/core/killmails.py +92 -59
- killtracker/core/worker_shutdown.py +47 -0
- killtracker/forms.py +1 -1
- killtracker/managers.py +3 -3
- killtracker/models/trackers.py +5 -7
- killtracker/models/webhooks.py +71 -90
- killtracker/providers.py +1 -1
- killtracker/signals.py +31 -0
- killtracker/tasks.py +141 -90
- killtracker/tests/core/test_discord_messages_1.py +54 -7
- killtracker/tests/core/test_discord_messages_2.py +8 -8
- killtracker/tests/core/test_killmails.py +74 -29
- killtracker/tests/core/test_worker_shutdown.py +34 -0
- killtracker/tests/models/test_trackers_1.py +23 -23
- killtracker/tests/models/test_trackers_2.py +5 -4
- killtracker/tests/models/test_webhook.py +54 -40
- killtracker/tests/test_integration.py +2 -2
- killtracker/tests/test_tasks.py +107 -48
- killtracker/tests/utils.py +16 -0
- {aa_killtracker-0.17.0.dist-info → aa_killtracker-0.18.0a2.dist-info}/WHEEL +0 -0
- {aa_killtracker-0.17.0.dist-info → aa_killtracker-0.18.0a2.dist-info}/licenses/LICENSE +0 -0
killtracker/tests/test_tasks.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import datetime as dt
|
1
2
|
from unittest.mock import patch
|
2
3
|
|
3
4
|
import celery
|
@@ -6,10 +7,13 @@ import dhooks_lite
|
|
6
7
|
from django.core.cache import cache
|
7
8
|
from django.test import TestCase
|
8
9
|
from django.test.utils import override_settings
|
10
|
+
from django.utils.timezone import now
|
9
11
|
|
12
|
+
from killtracker.core.discord_messages import DiscordMessage
|
10
13
|
from killtracker.exceptions import WebhookTooManyRequests
|
11
14
|
from killtracker.models import EveKillmail
|
12
15
|
from killtracker.tasks import (
|
16
|
+
ZKBTooManyRequestsError,
|
13
17
|
delete_stale_killmails,
|
14
18
|
generate_killmail_message,
|
15
19
|
run_killtracker,
|
@@ -21,6 +25,7 @@ from killtracker.tasks import (
|
|
21
25
|
|
22
26
|
from .testdata.factories import TrackerFactory
|
23
27
|
from .testdata.helpers import LoadTestDataMixin, load_eve_killmails, load_killmail
|
28
|
+
from .utils import reset_celery_once_locks
|
24
29
|
|
25
30
|
MODULE_PATH = "killtracker.tasks"
|
26
31
|
|
@@ -48,16 +53,13 @@ class TestTrackerBase(LoadTestDataMixin, TestCase):
|
|
48
53
|
)
|
49
54
|
|
50
55
|
|
51
|
-
@
|
56
|
+
@patch(MODULE_PATH + ".worker_shutdown.is_shutting_down", spec=True)
|
52
57
|
@patch(MODULE_PATH + ".is_esi_online", spec=True)
|
53
58
|
@patch(MODULE_PATH + ".delete_stale_killmails", spec=True)
|
54
59
|
@patch(MODULE_PATH + ".store_killmail", spec=True)
|
55
60
|
@patch(MODULE_PATH + ".Killmail.create_from_zkb_redisq")
|
56
61
|
@patch(MODULE_PATH + ".run_tracker", spec=True)
|
57
62
|
class TestRunKilltracker(TestTrackerBase):
|
58
|
-
def setUp(self) -> None:
|
59
|
-
cache.clear()
|
60
|
-
|
61
63
|
@staticmethod
|
62
64
|
def my_fetch_from_zkb():
|
63
65
|
for killmail_id in [10000001, 10000002, 10000003, None]:
|
@@ -66,6 +68,9 @@ class TestRunKilltracker(TestTrackerBase):
|
|
66
68
|
else:
|
67
69
|
yield None
|
68
70
|
|
71
|
+
def setUp(self):
|
72
|
+
reset_celery_once_locks()
|
73
|
+
|
69
74
|
@patch(MODULE_PATH + ".KILLTRACKER_STORING_KILLMAILS_ENABLED", False)
|
70
75
|
def test_should_run_normally(
|
71
76
|
self,
|
@@ -74,35 +79,41 @@ class TestRunKilltracker(TestTrackerBase):
|
|
74
79
|
mock_store_killmail,
|
75
80
|
mock_delete_stale_killmails,
|
76
81
|
mock_is_esi_online,
|
82
|
+
mock_is_shutting_down,
|
77
83
|
):
|
78
84
|
# given
|
85
|
+
mock_is_shutting_down.return_value = False
|
79
86
|
mock_create_from_zkb_redisq.side_effect = self.my_fetch_from_zkb()
|
80
87
|
mock_is_esi_online.return_value = True
|
81
|
-
self.webhook_1.
|
88
|
+
self.webhook_1._error_queue.enqueue(load_killmail(10000004).asjson())
|
82
89
|
# when
|
83
|
-
run_killtracker
|
90
|
+
got = run_killtracker()
|
84
91
|
# then
|
92
|
+
self.assertEqual(got, 3)
|
85
93
|
self.assertEqual(mock_run_tracker.delay.call_count, 6)
|
86
94
|
self.assertEqual(mock_store_killmail.si.call_count, 0)
|
87
95
|
self.assertFalse(mock_delete_stale_killmails.delay.called)
|
88
|
-
self.assertEqual(self.webhook_1.
|
89
|
-
self.assertEqual(self.webhook_1.
|
96
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 1)
|
97
|
+
self.assertEqual(self.webhook_1._error_queue.size(), 0)
|
90
98
|
|
91
99
|
@patch(MODULE_PATH + ".KILLTRACKER_STORING_KILLMAILS_ENABLED", False)
|
92
|
-
def
|
100
|
+
def test_should_abort_when_esi_is_offline(
|
93
101
|
self,
|
94
102
|
mock_run_tracker,
|
95
103
|
mock_create_from_zkb_redisq,
|
96
104
|
mock_store_killmail,
|
97
105
|
mock_delete_stale_killmails,
|
98
106
|
mock_is_esi_online,
|
107
|
+
mock_is_shutting_down,
|
99
108
|
):
|
100
109
|
# given
|
110
|
+
mock_is_shutting_down.return_value = False
|
101
111
|
mock_create_from_zkb_redisq.side_effect = self.my_fetch_from_zkb()
|
102
112
|
mock_is_esi_online.return_value = False
|
103
113
|
# when
|
104
|
-
run_killtracker
|
114
|
+
got = run_killtracker()
|
105
115
|
# then
|
116
|
+
self.assertEqual(got, 0)
|
106
117
|
self.assertEqual(mock_run_tracker.delay.call_count, 0)
|
107
118
|
self.assertEqual(mock_store_killmail.si.call_count, 0)
|
108
119
|
self.assertFalse(mock_delete_stale_killmails.delay.called)
|
@@ -115,13 +126,16 @@ class TestRunKilltracker(TestTrackerBase):
|
|
115
126
|
mock_store_killmail,
|
116
127
|
mock_delete_stale_killmails,
|
117
128
|
mock_is_esi_online,
|
129
|
+
mock_is_shutting_down,
|
118
130
|
):
|
119
131
|
# given
|
132
|
+
mock_is_shutting_down.return_value = False
|
120
133
|
mock_create_from_zkb_redisq.side_effect = self.my_fetch_from_zkb()
|
121
134
|
mock_is_esi_online.return_value = True
|
122
135
|
# when
|
123
|
-
run_killtracker
|
136
|
+
got = run_killtracker()
|
124
137
|
# then
|
138
|
+
self.assertEqual(got, 2)
|
125
139
|
self.assertEqual(mock_run_tracker.delay.call_count, 4)
|
126
140
|
|
127
141
|
@patch(MODULE_PATH + ".KILLTRACKER_PURGE_KILLMAILS_AFTER_DAYS", 30)
|
@@ -133,17 +147,63 @@ class TestRunKilltracker(TestTrackerBase):
|
|
133
147
|
mock_store_killmail,
|
134
148
|
mock_delete_stale_killmails,
|
135
149
|
mock_is_esi_online,
|
150
|
+
mock_is_shutting_down,
|
136
151
|
):
|
137
152
|
# given
|
153
|
+
mock_is_shutting_down.return_value = False
|
138
154
|
mock_create_from_zkb_redisq.side_effect = self.my_fetch_from_zkb()
|
139
155
|
mock_is_esi_online.return_value = True
|
140
156
|
# when
|
141
|
-
run_killtracker
|
157
|
+
run_killtracker()
|
142
158
|
# then
|
143
159
|
self.assertEqual(mock_run_tracker.delay.call_count, 6)
|
144
160
|
self.assertEqual(mock_store_killmail.si.call_count, 3)
|
145
161
|
self.assertTrue(mock_delete_stale_killmails.delay.called)
|
146
162
|
|
163
|
+
@patch(MODULE_PATH + ".KILLTRACKER_MAX_KILLMAILS_PER_RUN", 2)
|
164
|
+
def test_should_retry_when_too_many_errors_received(
|
165
|
+
self,
|
166
|
+
mock_run_tracker,
|
167
|
+
mock_create_from_zkb_redisq,
|
168
|
+
mock_store_killmail,
|
169
|
+
mock_delete_stale_killmails,
|
170
|
+
mock_is_esi_online,
|
171
|
+
mock_is_shutting_down,
|
172
|
+
):
|
173
|
+
# given
|
174
|
+
mock_is_shutting_down.return_value = False
|
175
|
+
mock_create_from_zkb_redisq.side_effect = ZKBTooManyRequestsError(
|
176
|
+
now() + dt.timedelta(minutes=1)
|
177
|
+
)
|
178
|
+
mock_is_esi_online.return_value = True
|
179
|
+
# when/then
|
180
|
+
with self.assertRaises(celery.exceptions.Retry):
|
181
|
+
run_killtracker()
|
182
|
+
# then
|
183
|
+
self.assertEqual(mock_run_tracker.delay.call_count, 0)
|
184
|
+
|
185
|
+
@patch(MODULE_PATH + ".KILLTRACKER_STORING_KILLMAILS_ENABLED", False)
|
186
|
+
def test_should_abort_when_worker_is_offline(
|
187
|
+
self,
|
188
|
+
mock_run_tracker,
|
189
|
+
mock_create_from_zkb_redisq,
|
190
|
+
mock_store_killmail,
|
191
|
+
mock_delete_stale_killmails,
|
192
|
+
mock_is_esi_online,
|
193
|
+
mock_is_shutting_down,
|
194
|
+
):
|
195
|
+
# given
|
196
|
+
mock_is_shutting_down.return_value = True
|
197
|
+
mock_create_from_zkb_redisq.side_effect = self.my_fetch_from_zkb()
|
198
|
+
mock_is_esi_online.return_value = True
|
199
|
+
# when
|
200
|
+
got = run_killtracker()
|
201
|
+
# then
|
202
|
+
self.assertEqual(got, 0)
|
203
|
+
self.assertEqual(mock_run_tracker.delay.call_count, 0)
|
204
|
+
self.assertEqual(mock_store_killmail.si.call_count, 0)
|
205
|
+
self.assertFalse(mock_delete_stale_killmails.delay.called)
|
206
|
+
|
147
207
|
|
148
208
|
@patch(MODULE_PATH + ".retry_task_if_esi_is_down", lambda x: None)
|
149
209
|
@patch(MODULE_PATH + ".send_messages_to_webhook", spec=True)
|
@@ -189,7 +249,7 @@ class TestRunTracker(TestTrackerBase):
|
|
189
249
|
# given
|
190
250
|
killmail = load_killmail(10000003)
|
191
251
|
killmail.save()
|
192
|
-
self.webhook_1.enqueue_message(content="test")
|
252
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="test"))
|
193
253
|
# when
|
194
254
|
run_tracker(self.tracker_1.pk, killmail.id)
|
195
255
|
# then
|
@@ -222,7 +282,7 @@ class TestGenerateKillmailMessage(TestTrackerBase):
|
|
222
282
|
generate_killmail_message(self.tracker_1.pk, self.killmail_id)
|
223
283
|
# then
|
224
284
|
self.assertTrue(mock_send_messages_to_webhook.delay.called)
|
225
|
-
self.assertEqual(self.webhook_1.
|
285
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 1)
|
226
286
|
self.assertFalse(mock_retry.called)
|
227
287
|
|
228
288
|
@patch(MODULE_PATH + ".KILLTRACKER_GENERATE_MESSAGE_MAX_RETRIES", 3)
|
@@ -238,87 +298,86 @@ class TestGenerateKillmailMessage(TestTrackerBase):
|
|
238
298
|
with self.assertRaises(RuntimeError):
|
239
299
|
generate_killmail_message(self.tracker_1.pk, self.killmail_id)
|
240
300
|
self.assertFalse(mock_send_messages_to_webhook.delay.called)
|
241
|
-
self.assertEqual(self.webhook_1.
|
301
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 0)
|
242
302
|
self.assertEqual(mock_retry.call_count, 4)
|
243
303
|
|
244
304
|
|
245
305
|
@patch("celery.app.task.Context.called_directly", False) # make retry work with eager
|
246
306
|
@override_settings(CELERY_ALWAYS_EAGER=True)
|
247
|
-
@patch(MODULE_PATH + ".Webhook.
|
307
|
+
@patch(MODULE_PATH + ".Webhook.send_message", spec=True)
|
248
308
|
class TestSendMessagesToWebhook(TestTrackerBase):
|
249
309
|
def setUp(self) -> None:
|
250
310
|
cache.clear()
|
251
311
|
|
252
|
-
def
|
253
|
-
"""when one message in queue, then send it and retry with delay"""
|
312
|
+
def test_should_send_one_message(self, mock_send_message):
|
254
313
|
# given
|
255
|
-
|
314
|
+
mock_send_message.return_value = dhooks_lite.WebhookResponse(
|
256
315
|
{}, status_code=200
|
257
316
|
)
|
258
|
-
self.webhook_1.enqueue_message(content="Test message")
|
317
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="Test message"))
|
259
318
|
# when
|
260
319
|
send_messages_to_webhook.delay(self.webhook_1.pk)
|
261
320
|
# then
|
262
|
-
self.assertEqual(
|
263
|
-
self.assertEqual(self.webhook_1.
|
264
|
-
self.assertEqual(self.webhook_1.
|
321
|
+
self.assertEqual(mock_send_message.call_count, 1)
|
322
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 0)
|
323
|
+
self.assertEqual(self.webhook_1._error_queue.size(), 0)
|
265
324
|
|
266
|
-
def test_three_message(self,
|
325
|
+
def test_three_message(self, mock_send_message):
|
267
326
|
"""when three messages in queue, then sends them and returns 3"""
|
268
327
|
# given
|
269
|
-
|
328
|
+
mock_send_message.return_value = dhooks_lite.WebhookResponse(
|
270
329
|
{}, status_code=200
|
271
330
|
)
|
272
|
-
self.webhook_1.enqueue_message(content="Test message")
|
273
|
-
self.webhook_1.enqueue_message(content="Test message")
|
274
|
-
self.webhook_1.enqueue_message(content="Test message")
|
331
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="Test message"))
|
332
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="Test message"))
|
333
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="Test message"))
|
275
334
|
# when
|
276
335
|
send_messages_to_webhook.delay(self.webhook_1.pk)
|
277
336
|
# then
|
278
|
-
self.assertEqual(
|
279
|
-
self.assertEqual(self.webhook_1.
|
280
|
-
self.assertEqual(self.webhook_1.
|
337
|
+
self.assertEqual(mock_send_message.call_count, 3)
|
338
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 0)
|
339
|
+
self.assertEqual(self.webhook_1._error_queue.size(), 0)
|
281
340
|
|
282
|
-
def test_no_messages(self,
|
341
|
+
def test_no_messages(self, mock_send_message):
|
283
342
|
"""when no messages in queue, then do nothing"""
|
284
343
|
# given
|
285
|
-
|
344
|
+
mock_send_message.return_value = dhooks_lite.WebhookResponse(
|
286
345
|
{}, status_code=200
|
287
346
|
)
|
288
347
|
# when
|
289
348
|
send_messages_to_webhook.delay(self.webhook_1.pk)
|
290
349
|
# then
|
291
|
-
self.assertEqual(
|
292
|
-
self.assertEqual(self.webhook_1.
|
293
|
-
self.assertEqual(self.webhook_1.
|
350
|
+
self.assertEqual(mock_send_message.call_count, 0)
|
351
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 0)
|
352
|
+
self.assertEqual(self.webhook_1._error_queue.size(), 0)
|
294
353
|
|
295
|
-
def test_failed_message(self,
|
354
|
+
def test_failed_message(self, mock_send_message):
|
296
355
|
"""when message sending failed, then put message in error queue"""
|
297
356
|
# given
|
298
|
-
|
357
|
+
mock_send_message.return_value = dhooks_lite.WebhookResponse(
|
299
358
|
{}, status_code=404
|
300
359
|
)
|
301
|
-
self.webhook_1.enqueue_message(content="Test message")
|
360
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="Test message"))
|
302
361
|
# when
|
303
362
|
send_messages_to_webhook.delay(self.webhook_1.pk)
|
304
363
|
# then
|
305
|
-
self.assertEqual(
|
306
|
-
self.assertEqual(self.webhook_1.
|
307
|
-
self.assertEqual(self.webhook_1.
|
364
|
+
self.assertEqual(mock_send_message.call_count, 1)
|
365
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 0)
|
366
|
+
self.assertEqual(self.webhook_1._error_queue.size(), 1)
|
308
367
|
|
309
|
-
def test_abort_on_too_many_requests(self,
|
368
|
+
def test_abort_on_too_many_requests(self, mock_send_message):
|
310
369
|
"""
|
311
370
|
when WebhookTooManyRequests exception is raised
|
312
371
|
then message is re-queued and retry once
|
313
372
|
"""
|
314
373
|
# given
|
315
|
-
|
316
|
-
self.webhook_1.enqueue_message(content="Test message")
|
374
|
+
mock_send_message.side_effect = WebhookTooManyRequests(10)
|
375
|
+
self.webhook_1.enqueue_message(DiscordMessage(content="Test message"))
|
317
376
|
# when
|
318
377
|
send_messages_to_webhook.delay(self.webhook_1.pk)
|
319
378
|
# then
|
320
|
-
self.assertEqual(
|
321
|
-
self.assertEqual(self.webhook_1.
|
379
|
+
self.assertEqual(mock_send_message.call_count, 1)
|
380
|
+
self.assertEqual(self.webhook_1._main_queue.size(), 1)
|
322
381
|
|
323
382
|
|
324
383
|
@patch(MODULE_PATH + ".logger", spec=True)
|
@@ -352,7 +411,7 @@ class TestStoreKillmail(TestTrackerBase):
|
|
352
411
|
@patch(MODULE_PATH + ".logger", spec=True)
|
353
412
|
class TestSendTestKillmailsToWebhook(TestTrackerBase):
|
354
413
|
def setUp(self) -> None:
|
355
|
-
self.webhook_1.
|
414
|
+
self.webhook_1._main_queue.clear()
|
356
415
|
|
357
416
|
def test_run_normal(self, mock_logger, mock_execute):
|
358
417
|
# given
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from app_utils.allianceauth import get_redis_client
|
4
|
+
|
5
|
+
logger = logging.getLogger(__name__)
|
6
|
+
|
7
|
+
|
8
|
+
def reset_celery_once_locks():
|
9
|
+
"""Reset celery once locks for given tasks."""
|
10
|
+
r = get_redis_client()
|
11
|
+
app_label = "killtracker"
|
12
|
+
if keys := r.keys(f":?:qo_{app_label}.*"):
|
13
|
+
deleted_count = r.delete(*keys)
|
14
|
+
logger.info("Removed %d stuck celery once keys", deleted_count)
|
15
|
+
else:
|
16
|
+
deleted_count = 0
|
File without changes
|
File without changes
|