aa-fleetfinder 2.7.1__py3-none-any.whl → 3.0.0b1__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.
Potentially problematic release.
This version of aa-fleetfinder might be problematic. Click here for more details.
- {aa_fleetfinder-2.7.1.dist-info → aa_fleetfinder-3.0.0b1.dist-info}/METADATA +5 -14
- {aa_fleetfinder-2.7.1.dist-info → aa_fleetfinder-3.0.0b1.dist-info}/RECORD +35 -35
- fleetfinder/__init__.py +5 -3
- fleetfinder/apps.py +5 -4
- fleetfinder/locale/cs_CZ/LC_MESSAGES/django.po +21 -22
- fleetfinder/locale/de/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/de/LC_MESSAGES/django.po +27 -24
- fleetfinder/locale/django.pot +22 -23
- fleetfinder/locale/es/LC_MESSAGES/django.po +25 -22
- fleetfinder/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/fr_FR/LC_MESSAGES/django.po +67 -59
- fleetfinder/locale/it_IT/LC_MESSAGES/django.po +21 -22
- fleetfinder/locale/ja/LC_MESSAGES/django.po +25 -22
- fleetfinder/locale/ko_KR/LC_MESSAGES/django.po +25 -22
- fleetfinder/locale/nl_NL/LC_MESSAGES/django.po +21 -22
- fleetfinder/locale/pl_PL/LC_MESSAGES/django.po +21 -22
- fleetfinder/locale/ru/LC_MESSAGES/django.po +25 -22
- fleetfinder/locale/sk/LC_MESSAGES/django.po +21 -22
- fleetfinder/locale/uk/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/uk/LC_MESSAGES/django.po +50 -56
- fleetfinder/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
- fleetfinder/locale/zh_Hans/LC_MESSAGES/django.po +28 -25
- fleetfinder/providers.py +22 -4
- fleetfinder/tasks.py +279 -110
- fleetfinder/tests/__init__.py +39 -1
- fleetfinder/tests/test_access.py +2 -2
- fleetfinder/tests/test_auth_hooks.py +2 -2
- fleetfinder/tests/test_settings.py +3 -2
- fleetfinder/tests/test_tasks.py +1010 -34
- fleetfinder/tests/test_templatetags.py +2 -4
- fleetfinder/tests/test_user_agent.py +62 -14
- fleetfinder/tests/test_views.py +700 -52
- fleetfinder/views.py +102 -55
- {aa_fleetfinder-2.7.1.dist-info → aa_fleetfinder-3.0.0b1.dist-info}/WHEEL +0 -0
- {aa_fleetfinder-2.7.1.dist-info → aa_fleetfinder-3.0.0b1.dist-info}/licenses/LICENSE +0 -0
fleetfinder/tests/test_tasks.py
CHANGED
|
@@ -3,19 +3,285 @@ Tests for the fleetfinder.tasks module.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
# Standard Library
|
|
6
|
-
from
|
|
7
|
-
from unittest.mock import Mock, patch
|
|
6
|
+
from datetime import timedelta
|
|
7
|
+
from unittest.mock import MagicMock, Mock, patch
|
|
8
|
+
|
|
9
|
+
# Third Party
|
|
10
|
+
from aiopenapi3 import ContentTypeError
|
|
11
|
+
|
|
12
|
+
# Django
|
|
13
|
+
from django.utils import timezone
|
|
14
|
+
|
|
15
|
+
# Alliance Auth
|
|
16
|
+
from esi.exceptions import HTTPClientError
|
|
8
17
|
|
|
9
18
|
# AA Fleet Finder
|
|
19
|
+
from fleetfinder.models import Fleet
|
|
10
20
|
from fleetfinder.tasks import (
|
|
21
|
+
ESI_ERROR_GRACE_TIME,
|
|
22
|
+
ESI_MAX_ERROR_COUNT,
|
|
23
|
+
FleetViewAggregate,
|
|
24
|
+
_check_for_esi_fleet,
|
|
25
|
+
_close_esi_fleet,
|
|
26
|
+
_esi_fleet_error_handling,
|
|
27
|
+
_fetch_chunk,
|
|
11
28
|
_get_fleet_aggregate,
|
|
29
|
+
_make_name_lookup,
|
|
30
|
+
_process_fleet,
|
|
31
|
+
_send_invitation,
|
|
12
32
|
check_fleet_adverts,
|
|
33
|
+
get_fleet_composition,
|
|
34
|
+
send_fleet_invitation,
|
|
13
35
|
)
|
|
36
|
+
from fleetfinder.tests import BaseTestCase
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestFleetViewAggregateClass(BaseTestCase):
|
|
40
|
+
"""
|
|
41
|
+
Tests for the FleetViewAggregate class.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
def test_initializes_with_valid_fleet_and_aggregate(self):
|
|
45
|
+
"""
|
|
46
|
+
Test that FleetViewAggregate initializes correctly with valid fleet and aggregate data.
|
|
47
|
+
|
|
48
|
+
:return:
|
|
49
|
+
:rtype:
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
fleet_data = [{"id": 1, "name": "Fleet Member 1"}]
|
|
53
|
+
aggregate_data = {"ship_type": 5}
|
|
54
|
+
aggregate = FleetViewAggregate(fleet=fleet_data, aggregate=aggregate_data)
|
|
55
|
+
|
|
56
|
+
self.assertEqual(aggregate.fleet, fleet_data)
|
|
57
|
+
self.assertEqual(aggregate.aggregate, aggregate_data)
|
|
58
|
+
|
|
59
|
+
def test_initializes_with_empty_fleet_and_aggregate(self):
|
|
60
|
+
"""
|
|
61
|
+
Test that FleetViewAggregate initializes correctly with empty fleet and aggregate data.
|
|
62
|
+
|
|
63
|
+
:return:
|
|
64
|
+
:rtype:
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
fleet_data = []
|
|
68
|
+
aggregate_data = {}
|
|
69
|
+
aggregate = FleetViewAggregate(fleet=fleet_data, aggregate=aggregate_data)
|
|
70
|
+
|
|
71
|
+
self.assertEqual(aggregate.fleet, fleet_data)
|
|
72
|
+
self.assertEqual(aggregate.aggregate, aggregate_data)
|
|
73
|
+
|
|
74
|
+
def test_handles_large_fleet_and_aggregate(self):
|
|
75
|
+
"""
|
|
76
|
+
Test that FleetViewAggregate handles large fleet and aggregate data.
|
|
77
|
+
|
|
78
|
+
:return:
|
|
79
|
+
:rtype:
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
fleet_data = [{"id": i, "name": f"Fleet Member {i}"} for i in range(1000)]
|
|
83
|
+
aggregate_data = {"ship_type": 1000}
|
|
84
|
+
aggregate = FleetViewAggregate(fleet=fleet_data, aggregate=aggregate_data)
|
|
85
|
+
|
|
86
|
+
self.assertEqual(len(aggregate.fleet), 1000)
|
|
87
|
+
self.assertEqual(aggregate.aggregate, aggregate_data)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class TestHelperSendInvitation(BaseTestCase):
|
|
91
|
+
"""
|
|
92
|
+
Tests for the _send_invitation helper function.
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
def test_sends_invitation_successfully(self):
|
|
96
|
+
"""
|
|
97
|
+
Test that _send_invitation sends an invitation successfully.
|
|
98
|
+
|
|
99
|
+
:return:
|
|
100
|
+
:rtype:
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
mock_esi_client = MagicMock()
|
|
104
|
+
mock_esi_client.Fleets.PostFleetsFleetIdMembers.return_value.result.return_value = (
|
|
105
|
+
None
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
109
|
+
_send_invitation(12345, "mock_token", 67890)
|
|
110
|
+
|
|
111
|
+
mock_esi_client.Fleets.PostFleetsFleetIdMembers.assert_called_once_with(
|
|
112
|
+
fleet_id=67890,
|
|
113
|
+
token="mock_token",
|
|
114
|
+
body={"character_id": 12345, "role": "squad_member"},
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
def test_handles_esi_client_error_gracefully(self):
|
|
118
|
+
"""
|
|
119
|
+
Test that _send_invitation handles ESIClientError gracefully.
|
|
120
|
+
|
|
121
|
+
:return:
|
|
122
|
+
:rtype:
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
mock_esi_client = MagicMock()
|
|
126
|
+
mock_esi_client.Fleets.PostFleetsFleetIdMembers.return_value.result.side_effect = HTTPClientError(
|
|
127
|
+
500, {}, b""
|
|
128
|
+
)
|
|
14
129
|
|
|
130
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
131
|
+
with self.assertRaises(HTTPClientError):
|
|
132
|
+
_send_invitation(12345, "mock_token", 67890)
|
|
15
133
|
|
|
16
|
-
|
|
134
|
+
mock_esi_client.Fleets.PostFleetsFleetIdMembers.assert_called_once_with(
|
|
135
|
+
fleet_id=67890,
|
|
136
|
+
token="mock_token",
|
|
137
|
+
body={"character_id": 12345, "role": "squad_member"},
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
class TestHelperCloseEsiFleet(BaseTestCase):
|
|
142
|
+
"""
|
|
143
|
+
Tests for the _close_esi_fleet helper function.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
def test_closes_fleet_and_logs_reason(self):
|
|
147
|
+
"""
|
|
148
|
+
Test that _close_esi_fleet closes the fleet and logs the reason.
|
|
149
|
+
|
|
150
|
+
:return:
|
|
151
|
+
:rtype:
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
mock_fleet = MagicMock()
|
|
155
|
+
|
|
156
|
+
with patch("fleetfinder.tasks.logger.info") as mock_info:
|
|
157
|
+
_close_esi_fleet(fleet=mock_fleet, reason="Test reason")
|
|
158
|
+
|
|
159
|
+
mock_fleet.delete.assert_called_once()
|
|
160
|
+
mock_info.assert_called_once()
|
|
161
|
+
args, kwargs = mock_info.call_args
|
|
162
|
+
|
|
163
|
+
self.assertTrue(
|
|
164
|
+
any("Closing: Test reason" in str(x) for x in args + tuple(kwargs.values()))
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
def test_does_not_fail_with_empty_reason(self):
|
|
168
|
+
"""
|
|
169
|
+
Test that _close_esi_fleet does not fail when given an empty reason.
|
|
170
|
+
|
|
171
|
+
:return:
|
|
172
|
+
:rtype:
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
mock_fleet = MagicMock()
|
|
176
|
+
|
|
177
|
+
with patch("fleetfinder.tasks.logger.info") as mock_info:
|
|
178
|
+
_close_esi_fleet(fleet=mock_fleet, reason="")
|
|
179
|
+
|
|
180
|
+
mock_fleet.delete.assert_called_once()
|
|
181
|
+
mock_info.assert_called_once()
|
|
182
|
+
args, kwargs = mock_info.call_args
|
|
183
|
+
|
|
184
|
+
self.assertTrue(
|
|
185
|
+
any("Closing: " in str(x) for x in args + tuple(kwargs.values()))
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class TestHelperEsiFleetErrorHandling(BaseTestCase):
|
|
17
190
|
"""
|
|
18
|
-
Tests for the
|
|
191
|
+
Tests for the _esi_fleet_error_handling helper function.
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
def test_closes_fleet_when_error_count_exceeds_limit(self):
|
|
195
|
+
"""
|
|
196
|
+
Test that _esi_fleet_error_handling closes the fleet when error count exceeds limit.
|
|
197
|
+
|
|
198
|
+
:return:
|
|
199
|
+
:rtype:
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
mock_fleet = MagicMock()
|
|
203
|
+
mock_fleet.last_esi_error = Fleet.EsiError.NO_FLEET
|
|
204
|
+
mock_fleet.last_esi_error_time = timezone.now() - timedelta(
|
|
205
|
+
seconds=ESI_ERROR_GRACE_TIME - 10
|
|
206
|
+
)
|
|
207
|
+
mock_fleet.esi_error_count = ESI_MAX_ERROR_COUNT
|
|
208
|
+
|
|
209
|
+
with patch("fleetfinder.tasks._close_esi_fleet") as mock_close_fleet:
|
|
210
|
+
_esi_fleet_error_handling(
|
|
211
|
+
fleet=mock_fleet, error_key=Fleet.EsiError.NO_FLEET
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
mock_close_fleet.assert_called_once_with(
|
|
215
|
+
fleet=mock_fleet, reason=Fleet.EsiError.NO_FLEET.label
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
def test_increments_error_count_for_same_error_within_grace_period(self):
|
|
219
|
+
"""
|
|
220
|
+
Test that _esi_fleet_error_handling increments error count for the same error within grace period.
|
|
221
|
+
|
|
222
|
+
:return:
|
|
223
|
+
:rtype:
|
|
224
|
+
"""
|
|
225
|
+
|
|
226
|
+
mock_fleet = MagicMock()
|
|
227
|
+
mock_fleet.last_esi_error = Fleet.EsiError.NO_FLEET
|
|
228
|
+
mock_fleet.last_esi_error_time = timezone.now() - timedelta(
|
|
229
|
+
seconds=ESI_ERROR_GRACE_TIME - 10
|
|
230
|
+
)
|
|
231
|
+
mock_fleet.esi_error_count = 1
|
|
232
|
+
|
|
233
|
+
_esi_fleet_error_handling(fleet=mock_fleet, error_key=Fleet.EsiError.NO_FLEET)
|
|
234
|
+
|
|
235
|
+
self.assertEqual(mock_fleet.esi_error_count, 2)
|
|
236
|
+
mock_fleet.save.assert_called_once()
|
|
237
|
+
|
|
238
|
+
def test_resets_error_count_for_new_error(self):
|
|
239
|
+
"""
|
|
240
|
+
Test that _esi_fleet_error_handling resets error count for a new error.
|
|
241
|
+
|
|
242
|
+
:return:
|
|
243
|
+
:rtype:
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
mock_fleet = MagicMock()
|
|
247
|
+
mock_fleet.last_esi_error = Fleet.EsiError.NO_FLEET
|
|
248
|
+
mock_fleet.last_esi_error_time = timezone.now() - timedelta(
|
|
249
|
+
seconds=ESI_ERROR_GRACE_TIME - 10
|
|
250
|
+
)
|
|
251
|
+
mock_fleet.esi_error_count = 3
|
|
252
|
+
|
|
253
|
+
_esi_fleet_error_handling(
|
|
254
|
+
fleet=mock_fleet, error_key=Fleet.EsiError.NOT_IN_FLEET
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
self.assertEqual(mock_fleet.esi_error_count, 1)
|
|
258
|
+
self.assertEqual(mock_fleet.last_esi_error, Fleet.EsiError.NOT_IN_FLEET)
|
|
259
|
+
mock_fleet.save.assert_called_once()
|
|
260
|
+
|
|
261
|
+
def test_resets_error_count_for_same_error_outside_grace_period(self):
|
|
262
|
+
"""
|
|
263
|
+
Test that _esi_fleet_error_handling resets error count for the same error outside grace period.
|
|
264
|
+
|
|
265
|
+
:return:
|
|
266
|
+
:rtype:
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
mock_fleet = MagicMock()
|
|
270
|
+
mock_fleet.last_esi_error = Fleet.EsiError.NO_FLEET
|
|
271
|
+
mock_fleet.last_esi_error_time = timezone.now() - timedelta(
|
|
272
|
+
seconds=ESI_ERROR_GRACE_TIME + 10
|
|
273
|
+
)
|
|
274
|
+
mock_fleet.esi_error_count = 3
|
|
275
|
+
|
|
276
|
+
_esi_fleet_error_handling(fleet=mock_fleet, error_key=Fleet.EsiError.NO_FLEET)
|
|
277
|
+
|
|
278
|
+
self.assertEqual(mock_fleet.esi_error_count, 1)
|
|
279
|
+
mock_fleet.save.assert_called_once()
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class TestHelperGetFleetAggregate(BaseTestCase):
|
|
283
|
+
"""
|
|
284
|
+
Tests for the _get_fleet_aggregate helper function.
|
|
19
285
|
"""
|
|
20
286
|
|
|
21
287
|
def test_returns_correct_counts_for_valid_fleet_infos(self):
|
|
@@ -34,7 +300,7 @@ class TestGetFleetAggregate(TestCase):
|
|
|
34
300
|
|
|
35
301
|
result = _get_fleet_aggregate(fleet_infos)
|
|
36
302
|
|
|
37
|
-
|
|
303
|
+
self.assertEqual(result, {"Cruiser": 2, "Battleship": 1})
|
|
38
304
|
|
|
39
305
|
def test_returns_empty_dict_for_empty_fleet_infos(self):
|
|
40
306
|
"""
|
|
@@ -48,7 +314,7 @@ class TestGetFleetAggregate(TestCase):
|
|
|
48
314
|
|
|
49
315
|
result = _get_fleet_aggregate(fleet_infos)
|
|
50
316
|
|
|
51
|
-
|
|
317
|
+
self.assertEqual(result, {})
|
|
52
318
|
|
|
53
319
|
def test_returns_only_valid_ship_type_names(self):
|
|
54
320
|
"""
|
|
@@ -68,31 +334,381 @@ class TestGetFleetAggregate(TestCase):
|
|
|
68
334
|
|
|
69
335
|
result = _get_fleet_aggregate(fleet_infos)
|
|
70
336
|
|
|
71
|
-
|
|
337
|
+
self.assertEqual(result, {"Cruiser": 1, "Battleship": 1})
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class TestHelperCheckForEsiFleet(BaseTestCase):
|
|
341
|
+
"""
|
|
342
|
+
Tests for the _check_for_esi_fleet helper function.
|
|
343
|
+
"""
|
|
344
|
+
|
|
345
|
+
def test_returns_fleet_data_when_fleet_exists(self):
|
|
346
|
+
"""
|
|
347
|
+
Test that _check_for_esi_fleet returns fleet data when fleet exists.
|
|
348
|
+
|
|
349
|
+
:return:
|
|
350
|
+
:rtype:
|
|
351
|
+
"""
|
|
352
|
+
|
|
353
|
+
mock_fleet = MagicMock()
|
|
354
|
+
mock_token = MagicMock()
|
|
355
|
+
mock_esi_fleet = {"fleet_id": 12345}
|
|
356
|
+
|
|
357
|
+
# create a mock response object whose .result() returns the dict
|
|
358
|
+
mock_response = MagicMock()
|
|
359
|
+
mock_response.result.return_value = mock_esi_fleet
|
|
360
|
+
|
|
361
|
+
# ensure the client call returns the mock response
|
|
362
|
+
mock_get = MagicMock(return_value=mock_response)
|
|
363
|
+
mock_client = MagicMock()
|
|
364
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet = mock_get
|
|
365
|
+
|
|
366
|
+
with patch("fleetfinder.tasks.Token.get_token", return_value=mock_token):
|
|
367
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
368
|
+
result = _check_for_esi_fleet(fleet=mock_fleet)
|
|
369
|
+
|
|
370
|
+
self.assertIsInstance(result, dict)
|
|
371
|
+
self.assertIn("fleet", result)
|
|
372
|
+
self.assertIn("token", result)
|
|
373
|
+
self.assertEqual(result["fleet"], mock_esi_fleet)
|
|
374
|
+
self.assertEqual(result["token"], mock_token)
|
|
375
|
+
|
|
376
|
+
def test_returns_false_when_content_type_error_occurs(self):
|
|
377
|
+
"""
|
|
378
|
+
Test that _check_for_esi_fleet returns False when a ContentTypeError occurs.
|
|
379
|
+
|
|
380
|
+
:return:
|
|
381
|
+
:rtype:
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
mock_fleet = MagicMock()
|
|
385
|
+
mock_token = MagicMock()
|
|
386
|
+
|
|
387
|
+
# .result() raises a properly constructed ContentTypeError instance
|
|
388
|
+
mock_response = MagicMock()
|
|
389
|
+
mock_response.result.side_effect = ContentTypeError(
|
|
390
|
+
operation="GetCharactersCharacterIdFleet",
|
|
391
|
+
content_type="application/json",
|
|
392
|
+
message="Unexpected content type",
|
|
393
|
+
response=Mock(),
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
mock_client = MagicMock()
|
|
397
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet.return_value = mock_response
|
|
398
|
+
|
|
399
|
+
with patch(
|
|
400
|
+
"fleetfinder.tasks.Token.get_token", return_value=mock_token
|
|
401
|
+
) as mock_get_token:
|
|
402
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
403
|
+
result = _check_for_esi_fleet(fleet=mock_fleet)
|
|
404
|
+
|
|
405
|
+
self.assertIs(result, False)
|
|
406
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet.assert_called_once()
|
|
407
|
+
mock_response.result.assert_called_once()
|
|
408
|
+
mock_get_token.assert_called_once()
|
|
409
|
+
|
|
410
|
+
def test_handles_http_client_error_for_fleet_not_found(self):
|
|
411
|
+
"""
|
|
412
|
+
Test that _check_for_esi_fleet handles HTTPClientError for fleet not found.
|
|
413
|
+
|
|
414
|
+
:return:
|
|
415
|
+
:rtype:
|
|
416
|
+
"""
|
|
417
|
+
|
|
418
|
+
mock_fleet = MagicMock()
|
|
419
|
+
mock_token = MagicMock()
|
|
420
|
+
|
|
421
|
+
# concrete response whose .result() raises an instance of HTTPClientError (404)
|
|
422
|
+
mock_response = MagicMock()
|
|
423
|
+
mock_response.result.side_effect = HTTPClientError(404, {}, b"")
|
|
424
|
+
|
|
425
|
+
mock_client = MagicMock()
|
|
426
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet.return_value = mock_response
|
|
427
|
+
|
|
428
|
+
with patch("fleetfinder.tasks.Token.get_token", return_value=mock_token):
|
|
429
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
430
|
+
with patch(
|
|
431
|
+
"fleetfinder.tasks._esi_fleet_error_handling"
|
|
432
|
+
) as mock_error_handling:
|
|
433
|
+
_check_for_esi_fleet(fleet=mock_fleet)
|
|
434
|
+
|
|
435
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet.assert_called_once()
|
|
436
|
+
mock_response.result.assert_called_once()
|
|
437
|
+
mock_error_handling.assert_called_once_with(
|
|
438
|
+
error_key=Fleet.EsiError.NOT_IN_FLEET, fleet=mock_fleet
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
def test_handles_http_client_error_for_other_errors(self):
|
|
442
|
+
"""
|
|
443
|
+
Test that _check_for_esi_fleet handles HTTPClientError for other errors.
|
|
444
|
+
|
|
445
|
+
:return:
|
|
446
|
+
:rtype:
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
mock_fleet = MagicMock()
|
|
450
|
+
mock_token = MagicMock()
|
|
451
|
+
|
|
452
|
+
# concrete response whose .result() raises an instance of HTTPClientError (403)
|
|
453
|
+
mock_response = MagicMock()
|
|
454
|
+
mock_response.result.side_effect = HTTPClientError(403, {}, b"")
|
|
455
|
+
|
|
456
|
+
mock_client = MagicMock()
|
|
457
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet.return_value = mock_response
|
|
458
|
+
|
|
459
|
+
with patch("fleetfinder.tasks.Token.get_token", return_value=mock_token):
|
|
460
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
461
|
+
with patch(
|
|
462
|
+
"fleetfinder.tasks._esi_fleet_error_handling"
|
|
463
|
+
) as mock_error_handling:
|
|
464
|
+
_check_for_esi_fleet(fleet=mock_fleet)
|
|
465
|
+
|
|
466
|
+
mock_client.Fleets.GetCharactersCharacterIdFleet.assert_called_once()
|
|
467
|
+
mock_response.result.assert_called_once()
|
|
468
|
+
mock_error_handling.assert_called_once_with(
|
|
469
|
+
error_key=Fleet.EsiError.NO_FLEET, fleet=mock_fleet
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def handles_generic_exception(self):
|
|
473
|
+
mock_fleet = MagicMock()
|
|
474
|
+
|
|
475
|
+
mock_esi_client = MagicMock()
|
|
476
|
+
mock_esi_client.Fleets.GetCharactersCharacterIdFleet.return_value.result.side_effect = (
|
|
477
|
+
Exception
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
with patch("fleetfinder.tasks.Token.get_token"):
|
|
481
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
482
|
+
with patch(
|
|
483
|
+
"fleetfinder.tasks._esi_fleet_error_handling"
|
|
484
|
+
) as mock_error_handling:
|
|
485
|
+
_check_for_esi_fleet(fleet=mock_fleet)
|
|
486
|
+
|
|
487
|
+
mock_error_handling.assert_called_once_with(
|
|
488
|
+
error_key=Fleet.EsiError.NO_FLEET, fleet=mock_fleet
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
class TestHelperProcessFleet(BaseTestCase):
|
|
493
|
+
"""
|
|
494
|
+
Tests for the _process_fleet helper function.
|
|
495
|
+
"""
|
|
496
|
+
|
|
497
|
+
def test_processes_fleet_successfully(self):
|
|
498
|
+
"""
|
|
499
|
+
Test that _process_fleet processes a fleet successfully.
|
|
500
|
+
|
|
501
|
+
:return:
|
|
502
|
+
:rtype:
|
|
503
|
+
"""
|
|
504
|
+
|
|
505
|
+
mock_fleet = MagicMock()
|
|
506
|
+
mock_fleet.name = "Test Fleet"
|
|
507
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
508
|
+
mock_fleet.fleet_id = 67890
|
|
509
|
+
|
|
510
|
+
mock_esi_fleet = {"fleet": MagicMock(fleet_id=67890), "token": MagicMock()}
|
|
511
|
+
|
|
512
|
+
mock_esi_client = MagicMock()
|
|
513
|
+
mock_esi_client.Fleets.GetFleetsFleetIdMembers.return_value.result.return_value = (
|
|
514
|
+
[]
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
with patch(
|
|
518
|
+
"fleetfinder.tasks._check_for_esi_fleet", return_value=mock_esi_fleet
|
|
519
|
+
) as mock_check:
|
|
520
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
521
|
+
_process_fleet(fleet=mock_fleet)
|
|
522
|
+
|
|
523
|
+
mock_check.assert_called_once_with(fleet=mock_fleet)
|
|
524
|
+
mock_esi_client.Fleets.GetFleetsFleetIdMembers.assert_called_once_with(
|
|
525
|
+
fleet_id=67890, token=mock_esi_fleet["token"]
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
def test_skips_processing_when_fleet_does_not_exist(self):
|
|
529
|
+
"""
|
|
530
|
+
Test that _process_fleet skips processing when fleet does not exist.
|
|
531
|
+
|
|
532
|
+
:return:
|
|
533
|
+
:rtype:
|
|
534
|
+
"""
|
|
535
|
+
|
|
536
|
+
mock_fleet = MagicMock()
|
|
537
|
+
|
|
538
|
+
with patch(
|
|
539
|
+
"fleetfinder.tasks._check_for_esi_fleet", return_value=False
|
|
540
|
+
) as mock_check:
|
|
541
|
+
_process_fleet(fleet=mock_fleet)
|
|
542
|
+
|
|
543
|
+
mock_check.assert_called_once_with(fleet=mock_fleet)
|
|
544
|
+
|
|
545
|
+
def test_handles_fleet_commander_change(self):
|
|
546
|
+
"""
|
|
547
|
+
Test that _process_fleet handles fleet commander change.
|
|
548
|
+
|
|
549
|
+
:return:
|
|
550
|
+
:rtype:
|
|
551
|
+
"""
|
|
552
|
+
|
|
553
|
+
mock_fleet = MagicMock()
|
|
554
|
+
mock_fleet.name = "Test Fleet"
|
|
555
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
556
|
+
mock_fleet.fleet_id = 67890
|
|
557
|
+
|
|
558
|
+
mock_esi_fleet = {"fleet": MagicMock(fleet_id=11111), "token": MagicMock()}
|
|
559
|
+
|
|
560
|
+
with patch(
|
|
561
|
+
"fleetfinder.tasks._check_for_esi_fleet", return_value=mock_esi_fleet
|
|
562
|
+
):
|
|
563
|
+
with patch(
|
|
564
|
+
"fleetfinder.tasks._esi_fleet_error_handling"
|
|
565
|
+
) as mock_error_handling:
|
|
566
|
+
_process_fleet(fleet=mock_fleet)
|
|
567
|
+
|
|
568
|
+
mock_error_handling.assert_called_once_with(
|
|
569
|
+
fleet=mock_fleet, error_key=Fleet.EsiError.FC_CHANGED_FLEET
|
|
570
|
+
)
|
|
72
571
|
|
|
572
|
+
def test_handles_not_fleet_boss_error(self):
|
|
573
|
+
"""
|
|
574
|
+
Test that _process_fleet handles not fleet boss error.
|
|
575
|
+
|
|
576
|
+
:return:
|
|
577
|
+
:rtype:
|
|
578
|
+
"""
|
|
579
|
+
|
|
580
|
+
mock_fleet = MagicMock()
|
|
581
|
+
mock_fleet.name = "Test Fleet"
|
|
582
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
583
|
+
mock_fleet.fleet_id = 67890
|
|
584
|
+
|
|
585
|
+
mock_esi_fleet = {"fleet": MagicMock(fleet_id=67890), "token": MagicMock()}
|
|
73
586
|
|
|
74
|
-
|
|
587
|
+
mock_esi_client = MagicMock()
|
|
588
|
+
mock_esi_client.Fleets.GetFleetsFleetIdMembers.return_value.result.side_effect = (
|
|
589
|
+
Exception()
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
with patch(
|
|
593
|
+
"fleetfinder.tasks._check_for_esi_fleet", return_value=mock_esi_fleet
|
|
594
|
+
):
|
|
595
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
596
|
+
with patch(
|
|
597
|
+
"fleetfinder.tasks._esi_fleet_error_handling"
|
|
598
|
+
) as mock_error_handling:
|
|
599
|
+
_process_fleet(fleet=mock_fleet)
|
|
600
|
+
|
|
601
|
+
mock_error_handling.assert_called_once_with(
|
|
602
|
+
fleet=mock_fleet, error_key=Fleet.EsiError.NOT_FLEETBOSS
|
|
603
|
+
)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class TestSendFleetInvitation(BaseTestCase):
|
|
607
|
+
"""
|
|
608
|
+
Tests for the send_fleet_invitation function.
|
|
609
|
+
"""
|
|
610
|
+
|
|
611
|
+
def test_sends_invitations_to_all_characters_successfully(self):
|
|
612
|
+
"""
|
|
613
|
+
Test that send_fleet_invitation sends invitations to all characters successfully.
|
|
614
|
+
|
|
615
|
+
:return:
|
|
616
|
+
:rtype:
|
|
617
|
+
"""
|
|
618
|
+
|
|
619
|
+
mock_fleet = MagicMock()
|
|
620
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
621
|
+
|
|
622
|
+
mock_token = MagicMock()
|
|
623
|
+
mock_character_ids = [111, 222, 333]
|
|
624
|
+
|
|
625
|
+
with patch("fleetfinder.tasks.Fleet.objects.get", return_value=mock_fleet):
|
|
626
|
+
with patch("fleetfinder.tasks.Token.get_token", return_value=mock_token):
|
|
627
|
+
with patch(
|
|
628
|
+
"fleetfinder.tasks._send_invitation"
|
|
629
|
+
) as mock_send_invitation:
|
|
630
|
+
send_fleet_invitation(
|
|
631
|
+
fleet_id=67890, character_ids=mock_character_ids
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
mock_send_invitation.assert_any_call(
|
|
635
|
+
character_id=111, fleet_commander_token=mock_token, fleet_id=67890
|
|
636
|
+
)
|
|
637
|
+
mock_send_invitation.assert_any_call(
|
|
638
|
+
character_id=222, fleet_commander_token=mock_token, fleet_id=67890
|
|
639
|
+
)
|
|
640
|
+
mock_send_invitation.assert_any_call(
|
|
641
|
+
character_id=333, fleet_commander_token=mock_token, fleet_id=67890
|
|
642
|
+
)
|
|
643
|
+
self.assertEqual(mock_send_invitation.call_count, 3)
|
|
644
|
+
|
|
645
|
+
def test_handles_empty_character_list_gracefully(self):
|
|
646
|
+
"""
|
|
647
|
+
Test that send_fleet_invitation handles an empty character list gracefully.
|
|
648
|
+
|
|
649
|
+
:return:
|
|
650
|
+
:rtype:
|
|
651
|
+
"""
|
|
652
|
+
|
|
653
|
+
mock_fleet = MagicMock()
|
|
654
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
655
|
+
|
|
656
|
+
with patch("fleetfinder.tasks.Fleet.objects.get", return_value=mock_fleet):
|
|
657
|
+
with patch("fleetfinder.tasks.Token.get_token"):
|
|
658
|
+
with patch(
|
|
659
|
+
"fleetfinder.tasks._send_invitation"
|
|
660
|
+
) as mock_send_invitation:
|
|
661
|
+
send_fleet_invitation(fleet_id=67890, character_ids=[])
|
|
662
|
+
|
|
663
|
+
mock_send_invitation.assert_not_called()
|
|
664
|
+
|
|
665
|
+
def test_raises_exception_when_fleet_not_found(self):
|
|
666
|
+
"""
|
|
667
|
+
Test that send_fleet_invitation raises an exception when the fleet is not found.
|
|
668
|
+
|
|
669
|
+
:return:
|
|
670
|
+
:rtype:
|
|
671
|
+
"""
|
|
672
|
+
|
|
673
|
+
with patch(
|
|
674
|
+
"fleetfinder.tasks.Fleet.objects.get", side_effect=Fleet.DoesNotExist
|
|
675
|
+
):
|
|
676
|
+
with self.assertRaises(Fleet.DoesNotExist):
|
|
677
|
+
send_fleet_invitation(fleet_id=67890, character_ids=[111, 222, 333])
|
|
678
|
+
|
|
679
|
+
def test_raises_exception_when_token_retrieval_fails(self):
|
|
680
|
+
"""
|
|
681
|
+
Test that send_fleet_invitation raises an exception when token retrieval fails.
|
|
682
|
+
|
|
683
|
+
:return:
|
|
684
|
+
:rtype:
|
|
685
|
+
"""
|
|
686
|
+
|
|
687
|
+
mock_fleet = MagicMock()
|
|
688
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
689
|
+
|
|
690
|
+
with patch("fleetfinder.tasks.Fleet.objects.get", return_value=mock_fleet):
|
|
691
|
+
with patch("fleetfinder.tasks.Token.get_token", side_effect=Exception):
|
|
692
|
+
with self.assertRaises(Exception):
|
|
693
|
+
send_fleet_invitation(fleet_id=67890, character_ids=[111, 222, 333])
|
|
694
|
+
|
|
695
|
+
|
|
696
|
+
class TestCheckFleetAdverts(BaseTestCase):
|
|
75
697
|
"""
|
|
76
698
|
Tests for the check_fleet_adverts function.
|
|
77
699
|
"""
|
|
78
700
|
|
|
79
701
|
@patch("fleetfinder.models.Fleet.objects.all")
|
|
80
|
-
|
|
81
|
-
def test_processes_registered_fleets_when_available(
|
|
82
|
-
self, mock_fetch_esi_status, mock_fleet_objects
|
|
83
|
-
):
|
|
702
|
+
def test_processes_registered_fleets_when_available(self, mock_fleet_objects):
|
|
84
703
|
"""
|
|
85
704
|
Test that check_fleet_adverts processes registered fleets when ESI is available.
|
|
86
705
|
|
|
87
|
-
:param mock_fetch_esi_status:
|
|
88
|
-
:type mock_fetch_esi_status:
|
|
89
706
|
:param mock_fleet_objects:
|
|
90
707
|
:type mock_fleet_objects:
|
|
91
708
|
:return:
|
|
92
709
|
:rtype:
|
|
93
710
|
"""
|
|
94
711
|
|
|
95
|
-
mock_fetch_esi_status.return_value.is_ok = True
|
|
96
712
|
mock_fleet_objects.return_value.exists.return_value = True
|
|
97
713
|
mock_fleet_objects.return_value.count.return_value = 2
|
|
98
714
|
mock_fleet_objects.return_value.__iter__.return_value = iter([Mock(), Mock()])
|
|
@@ -100,41 +716,401 @@ class TestCheckFleetAdvert(TestCase):
|
|
|
100
716
|
check_fleet_adverts()
|
|
101
717
|
|
|
102
718
|
mock_fleet_objects.return_value.__iter__.assert_called_once()
|
|
103
|
-
mock_fetch_esi_status.assert_called_once()
|
|
104
719
|
|
|
105
720
|
@patch("fleetfinder.models.Fleet.objects.all")
|
|
106
|
-
|
|
107
|
-
def test_logs_no_registered_fleets_when_none_exist(
|
|
108
|
-
self, mock_fetch_esi_status, mock_fleet_objects
|
|
109
|
-
):
|
|
721
|
+
def test_logs_no_registered_fleets_when_none_exist(self, mock_fleet_objects):
|
|
110
722
|
"""
|
|
111
723
|
Test that check_fleet_adverts logs a message when no registered fleets exist.
|
|
112
724
|
|
|
113
|
-
:param mock_fetch_esi_status:
|
|
114
|
-
:type mock_fetch_esi_status:
|
|
115
725
|
:param mock_fleet_objects:
|
|
116
726
|
:type mock_fleet_objects:
|
|
117
727
|
:return:
|
|
118
728
|
:rtype:
|
|
119
729
|
"""
|
|
120
730
|
|
|
121
|
-
mock_fetch_esi_status.return_value.is_ok = True
|
|
122
731
|
mock_fleet_objects.return_value.exists.return_value = False
|
|
123
732
|
|
|
124
733
|
check_fleet_adverts()
|
|
125
734
|
|
|
126
735
|
mock_fleet_objects.return_value.exists.assert_called_once()
|
|
127
|
-
mock_fetch_esi_status.assert_not_called()
|
|
128
736
|
|
|
129
|
-
@patch("fleetfinder.models.Fleet.objects.all")
|
|
130
|
-
@patch("fleetfinder.tasks.fetch_esi_status")
|
|
131
|
-
def test_aborts_processing_when_esi_is_unavailable(
|
|
132
|
-
self, mock_fetch_esi_status, mock_fleet_objects
|
|
133
|
-
):
|
|
134
|
-
mock_fetch_esi_status.return_value.is_ok = False
|
|
135
|
-
mock_fleet_objects.return_value.exists.return_value = True
|
|
136
737
|
|
|
137
|
-
|
|
738
|
+
class TestHelperMakeNameLookup(BaseTestCase):
|
|
739
|
+
"""
|
|
740
|
+
Tests for the _make_name_lookup helper function.
|
|
741
|
+
"""
|
|
742
|
+
|
|
743
|
+
def test_creates_lookup_from_dicts(self):
|
|
744
|
+
"""
|
|
745
|
+
Test that _make_name_lookup creates a lookup from a list of dictionaries.
|
|
746
|
+
|
|
747
|
+
:return:
|
|
748
|
+
:rtype:
|
|
749
|
+
"""
|
|
750
|
+
|
|
751
|
+
input_data = [{"id": 1, "name": "Name1"}, {"id": 2, "name": "Name2"}]
|
|
752
|
+
expected_output = {1: "Name1", 2: "Name2"}
|
|
753
|
+
|
|
754
|
+
result = _make_name_lookup(input_data)
|
|
755
|
+
|
|
756
|
+
self.assertEqual(result, expected_output)
|
|
757
|
+
|
|
758
|
+
def test_creates_lookup_from_objects(self):
|
|
759
|
+
"""
|
|
760
|
+
Test that _make_name_lookup creates a lookup from a list of objects.
|
|
761
|
+
|
|
762
|
+
:return:
|
|
763
|
+
:rtype:
|
|
764
|
+
"""
|
|
765
|
+
|
|
766
|
+
mock_item1 = MagicMock()
|
|
767
|
+
mock_item1.id = 1
|
|
768
|
+
mock_item1.name = "Name1"
|
|
769
|
+
mock_item2 = MagicMock()
|
|
770
|
+
mock_item2.id = 2
|
|
771
|
+
mock_item2.name = "Name2"
|
|
772
|
+
input_data = [mock_item1, mock_item2]
|
|
773
|
+
expected_output = {1: "Name1", 2: "Name2"}
|
|
774
|
+
|
|
775
|
+
result = _make_name_lookup(input_data)
|
|
776
|
+
|
|
777
|
+
self.assertEqual(result, expected_output)
|
|
778
|
+
|
|
779
|
+
def test_handles_mixed_input_types(self):
|
|
780
|
+
"""
|
|
781
|
+
Test that _make_name_lookup handles mixed input types (dicts and objects).
|
|
782
|
+
|
|
783
|
+
:return:
|
|
784
|
+
:rtype:
|
|
785
|
+
"""
|
|
786
|
+
|
|
787
|
+
mock_item = MagicMock()
|
|
788
|
+
mock_item.id = 3
|
|
789
|
+
mock_item.name = "Name3"
|
|
790
|
+
input_data = [{"id": 1, "name": "Name1"}, mock_item, {"id": 2, "name": "Name2"}]
|
|
791
|
+
expected_output = {1: "Name1", 2: "Name2", 3: "Name3"}
|
|
792
|
+
|
|
793
|
+
result = _make_name_lookup(input_data)
|
|
794
|
+
|
|
795
|
+
self.assertEqual(result, expected_output)
|
|
796
|
+
|
|
797
|
+
def test_ignores_items_with_missing_id_or_name(self):
|
|
798
|
+
"""
|
|
799
|
+
Test that _make_name_lookup ignores items with missing id or name.
|
|
800
|
+
|
|
801
|
+
:return:
|
|
802
|
+
:rtype:
|
|
803
|
+
"""
|
|
804
|
+
|
|
805
|
+
input_data = [
|
|
806
|
+
{"id": 1, "name": "Name1"},
|
|
807
|
+
{"id": None, "name": "Name2"},
|
|
808
|
+
{"id": 2, "name": None},
|
|
809
|
+
{"name": "Name3"},
|
|
810
|
+
{"id": 3},
|
|
811
|
+
]
|
|
812
|
+
expected_output = {1: "Name1"}
|
|
813
|
+
|
|
814
|
+
result = _make_name_lookup(input_data)
|
|
815
|
+
|
|
816
|
+
self.assertEqual(result, expected_output)
|
|
817
|
+
|
|
818
|
+
def test_returns_empty_dict_for_empty_input(self):
|
|
819
|
+
"""
|
|
820
|
+
Test that _make_name_lookup returns an empty dictionary for empty input.
|
|
821
|
+
|
|
822
|
+
:return:
|
|
823
|
+
:rtype:
|
|
824
|
+
"""
|
|
825
|
+
|
|
826
|
+
input_data = []
|
|
827
|
+
expected_output = {}
|
|
828
|
+
|
|
829
|
+
result = _make_name_lookup(input_data)
|
|
830
|
+
|
|
831
|
+
self.assertEqual(result, expected_output)
|
|
832
|
+
|
|
833
|
+
def test_skips_none_items_in_input(self):
|
|
834
|
+
"""
|
|
835
|
+
Test that _make_name_lookup skips None items in the input list.
|
|
836
|
+
|
|
837
|
+
:return:
|
|
838
|
+
:rtype:
|
|
839
|
+
"""
|
|
840
|
+
|
|
841
|
+
input_data = [{"id": 1, "name": "Name1"}, None, {"id": 2, "name": "Name2"}]
|
|
842
|
+
expected_output = {1: "Name1", 2: "Name2"}
|
|
843
|
+
|
|
844
|
+
result = _make_name_lookup(input_data)
|
|
845
|
+
|
|
846
|
+
self.assertEqual(result, expected_output)
|
|
847
|
+
|
|
848
|
+
|
|
849
|
+
class TestHelperFetchChunk(BaseTestCase):
|
|
850
|
+
"""
|
|
851
|
+
Tests for the _fetch_chunk helper function.
|
|
852
|
+
"""
|
|
853
|
+
|
|
854
|
+
def test_fetches_names_for_valid_ids(self):
|
|
855
|
+
"""
|
|
856
|
+
Test that _fetch_chunk fetches names for valid IDs.
|
|
857
|
+
|
|
858
|
+
:return:
|
|
859
|
+
:rtype:
|
|
860
|
+
"""
|
|
861
|
+
|
|
862
|
+
ids = [1, 2, 3]
|
|
863
|
+
mock_result = [
|
|
864
|
+
{"id": 1, "name": "Name1"},
|
|
865
|
+
{"id": 2, "name": "Name2"},
|
|
866
|
+
{"id": 3, "name": "Name3"},
|
|
867
|
+
]
|
|
868
|
+
|
|
869
|
+
mock_client = MagicMock()
|
|
870
|
+
mock_client.Universe.PostUniverseNames.return_value.result.return_value = (
|
|
871
|
+
mock_result
|
|
872
|
+
)
|
|
873
|
+
|
|
874
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
875
|
+
result = _fetch_chunk(ids)
|
|
876
|
+
|
|
877
|
+
self.assertEqual(result, mock_result)
|
|
878
|
+
mock_client.Universe.PostUniverseNames.assert_called_once_with(body=ids)
|
|
879
|
+
|
|
880
|
+
def test_handles_single_id_failure_gracefully(self):
|
|
881
|
+
"""
|
|
882
|
+
Test that _fetch_chunk handles single ID failure gracefully.
|
|
883
|
+
|
|
884
|
+
:return:
|
|
885
|
+
:rtype:
|
|
886
|
+
"""
|
|
887
|
+
|
|
888
|
+
ids = [1]
|
|
889
|
+
|
|
890
|
+
mock_client = MagicMock()
|
|
891
|
+
mock_client.Universe.PostUniverseNames.return_value.result.side_effect = (
|
|
892
|
+
Exception()
|
|
893
|
+
)
|
|
894
|
+
|
|
895
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
896
|
+
result = _fetch_chunk(ids)
|
|
897
|
+
|
|
898
|
+
self.assertEqual(result, [])
|
|
899
|
+
mock_client.Universe.PostUniverseNames.assert_called_once_with(body=ids)
|
|
900
|
+
|
|
901
|
+
def test_retries_with_split_on_failure(self):
|
|
902
|
+
"""
|
|
903
|
+
Test that _fetch_chunk retries with split on failure.
|
|
904
|
+
|
|
905
|
+
:return:
|
|
906
|
+
:rtype:
|
|
907
|
+
"""
|
|
908
|
+
|
|
909
|
+
ids = [1, 2, 3, 4]
|
|
910
|
+
mock_result_1 = [{"id": 1, "name": "Name1"}, {"id": 2, "name": "Name2"}]
|
|
911
|
+
mock_result_2 = [{"id": 3, "name": "Name3"}, {"id": 4, "name": "Name4"}]
|
|
912
|
+
|
|
913
|
+
mock_client = MagicMock()
|
|
914
|
+
mock_client.Universe.PostUniverseNames.return_value.result.side_effect = [
|
|
915
|
+
Exception(),
|
|
916
|
+
mock_result_1,
|
|
917
|
+
mock_result_2,
|
|
918
|
+
]
|
|
919
|
+
|
|
920
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
921
|
+
result = _fetch_chunk(ids)
|
|
922
|
+
|
|
923
|
+
self.assertEqual(result, mock_result_1 + mock_result_2)
|
|
924
|
+
self.assertEqual(mock_client.Universe.PostUniverseNames.call_count, 3)
|
|
925
|
+
mock_client.Universe.PostUniverseNames.assert_any_call(body=[1, 2])
|
|
926
|
+
mock_client.Universe.PostUniverseNames.assert_any_call(body=[3, 4])
|
|
927
|
+
|
|
928
|
+
def test_handles_empty_id_list(self):
|
|
929
|
+
"""
|
|
930
|
+
Test that _fetch_chunk handles an empty ID list.
|
|
931
|
+
|
|
932
|
+
:return:
|
|
933
|
+
:rtype:
|
|
934
|
+
"""
|
|
935
|
+
|
|
936
|
+
ids = []
|
|
937
|
+
|
|
938
|
+
mock_client = MagicMock()
|
|
939
|
+
mock_client.Universe.PostUniverseNames.return_value.result.return_value = []
|
|
940
|
+
|
|
941
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_client)):
|
|
942
|
+
result = _fetch_chunk(ids)
|
|
943
|
+
|
|
944
|
+
self.assertEqual(result, [])
|
|
945
|
+
|
|
946
|
+
|
|
947
|
+
class TestGetFleetComposition(BaseTestCase):
|
|
948
|
+
"""
|
|
949
|
+
Tests for the get_fleet_composition function.
|
|
950
|
+
"""
|
|
951
|
+
|
|
952
|
+
def test_retrieves_fleet_composition_successfully(self):
|
|
953
|
+
"""
|
|
954
|
+
Test that get_fleet_composition retrieves fleet composition successfully.
|
|
955
|
+
|
|
956
|
+
:return:
|
|
957
|
+
:rtype:
|
|
958
|
+
"""
|
|
959
|
+
|
|
960
|
+
mock_fleet = MagicMock()
|
|
961
|
+
mock_fleet.fleet_commander.character_id = 1
|
|
962
|
+
mock_fleet.fleet_commander.character_name = "Commander"
|
|
963
|
+
mock_fleet.name = "Test Fleet"
|
|
964
|
+
mock_fleet.fleet_id = 67890
|
|
965
|
+
|
|
966
|
+
mock_token = MagicMock()
|
|
967
|
+
|
|
968
|
+
def _make_member(character_id, solar_system_id, ship_type_id, takes_fleet_warp):
|
|
969
|
+
"""
|
|
970
|
+
Helper to create a mock fleet member.
|
|
971
|
+
|
|
972
|
+
:param character_id:
|
|
973
|
+
:type character_id:
|
|
974
|
+
:param solar_system_id:
|
|
975
|
+
:type solar_system_id:
|
|
976
|
+
:param ship_type_id:
|
|
977
|
+
:type ship_type_id:
|
|
978
|
+
:param takes_fleet_warp:
|
|
979
|
+
:type takes_fleet_warp:
|
|
980
|
+
:return:
|
|
981
|
+
:rtype:
|
|
982
|
+
"""
|
|
983
|
+
|
|
984
|
+
m = MagicMock()
|
|
985
|
+
m.character_id = character_id
|
|
986
|
+
m.solar_system_id = solar_system_id
|
|
987
|
+
m.ship_type_id = ship_type_id
|
|
988
|
+
m.takes_fleet_warp = takes_fleet_warp
|
|
989
|
+
m.dict.return_value = {
|
|
990
|
+
"character_id": character_id,
|
|
991
|
+
"solar_system_id": solar_system_id,
|
|
992
|
+
"ship_type_id": ship_type_id,
|
|
993
|
+
}
|
|
994
|
+
return m
|
|
995
|
+
|
|
996
|
+
mock_fleet_infos = [
|
|
997
|
+
_make_member(1, 101, 201, True),
|
|
998
|
+
_make_member(2, 102, 202, False),
|
|
999
|
+
]
|
|
1000
|
+
|
|
1001
|
+
mock_name_lookup = {
|
|
1002
|
+
1: "Character1",
|
|
1003
|
+
2: "Character2",
|
|
1004
|
+
101: "SolarSystem1",
|
|
1005
|
+
102: "SolarSystem2",
|
|
1006
|
+
201: "ShipType1",
|
|
1007
|
+
202: "ShipType2",
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
mock_esi_client = MagicMock()
|
|
1011
|
+
mock_esi_client.Fleets.GetFleetsFleetIdMembers.return_value.result.return_value = (
|
|
1012
|
+
mock_fleet_infos
|
|
1013
|
+
)
|
|
1014
|
+
|
|
1015
|
+
with patch("fleetfinder.tasks.Fleet.objects.get", return_value=mock_fleet):
|
|
1016
|
+
with patch("fleetfinder.tasks.Token.get_token", return_value=mock_token):
|
|
1017
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
1018
|
+
with patch("fleetfinder.tasks._fetch_chunk") as mock_fetch_chunk:
|
|
1019
|
+
mock_fetch_chunk.side_effect = lambda ids: [
|
|
1020
|
+
{"id": id_, "name": mock_name_lookup[id_]} for id_ in ids
|
|
1021
|
+
]
|
|
1022
|
+
|
|
1023
|
+
with patch(
|
|
1024
|
+
"fleetfinder.tasks._get_fleet_aggregate"
|
|
1025
|
+
) as mock_aggregate:
|
|
1026
|
+
mock_aggregate.return_value = {
|
|
1027
|
+
"ShipType1": 1,
|
|
1028
|
+
"ShipType2": 1,
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
result = get_fleet_composition(fleet_id=67890)
|
|
1032
|
+
|
|
1033
|
+
self.assertEqual(
|
|
1034
|
+
result.fleet,
|
|
1035
|
+
[
|
|
1036
|
+
{
|
|
1037
|
+
"character_id": 1,
|
|
1038
|
+
"solar_system_id": 101,
|
|
1039
|
+
"ship_type_id": 201,
|
|
1040
|
+
"takes_fleet_warp": True,
|
|
1041
|
+
"character_name": "Character1",
|
|
1042
|
+
"solar_system_name": "SolarSystem1",
|
|
1043
|
+
"ship_type_name": "ShipType1",
|
|
1044
|
+
"is_fleet_boss": True,
|
|
1045
|
+
},
|
|
1046
|
+
{
|
|
1047
|
+
"character_id": 2,
|
|
1048
|
+
"solar_system_id": 102,
|
|
1049
|
+
"ship_type_id": 202,
|
|
1050
|
+
"takes_fleet_warp": False,
|
|
1051
|
+
"character_name": "Character2",
|
|
1052
|
+
"solar_system_name": "SolarSystem2",
|
|
1053
|
+
"ship_type_name": "ShipType2",
|
|
1054
|
+
"is_fleet_boss": False,
|
|
1055
|
+
},
|
|
1056
|
+
],
|
|
1057
|
+
)
|
|
1058
|
+
self.assertEqual(result.aggregate, {"ShipType1": 1, "ShipType2": 1})
|
|
1059
|
+
|
|
1060
|
+
def test_raises_exception_when_fleet_does_not_exist(self):
|
|
1061
|
+
"""
|
|
1062
|
+
Test that get_fleet_composition raises an exception when the fleet does not exist.
|
|
1063
|
+
|
|
1064
|
+
:return:
|
|
1065
|
+
:rtype:
|
|
1066
|
+
"""
|
|
1067
|
+
|
|
1068
|
+
with patch(
|
|
1069
|
+
"fleetfinder.tasks.Fleet.objects.get", side_effect=Fleet.DoesNotExist
|
|
1070
|
+
):
|
|
1071
|
+
with self.assertRaises(Fleet.DoesNotExist):
|
|
1072
|
+
get_fleet_composition(fleet_id=67890)
|
|
1073
|
+
|
|
1074
|
+
def test_raises_runtime_error_on_esi_failure(self):
|
|
1075
|
+
"""
|
|
1076
|
+
Test that get_fleet_composition raises a RuntimeError on ESI failure.
|
|
1077
|
+
|
|
1078
|
+
:return:
|
|
1079
|
+
:rtype:
|
|
1080
|
+
"""
|
|
1081
|
+
|
|
1082
|
+
mock_fleet = MagicMock()
|
|
1083
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
1084
|
+
mock_fleet.fleet_id = 67890
|
|
1085
|
+
|
|
1086
|
+
with patch("fleetfinder.tasks.Fleet.objects.get", return_value=mock_fleet):
|
|
1087
|
+
with patch("fleetfinder.tasks.Token.get_token", side_effect=Exception):
|
|
1088
|
+
with self.assertRaises(RuntimeError):
|
|
1089
|
+
get_fleet_composition(fleet_id=67890)
|
|
1090
|
+
|
|
1091
|
+
def test_handles_handles_empty_fleet_info_gracefully(self):
|
|
1092
|
+
"""
|
|
1093
|
+
Test that get_fleet_composition handles empty fleet info gracefully.
|
|
1094
|
+
|
|
1095
|
+
:return:
|
|
1096
|
+
:rtype:
|
|
1097
|
+
"""
|
|
1098
|
+
|
|
1099
|
+
mock_fleet = MagicMock()
|
|
1100
|
+
mock_fleet.fleet_commander.character_id = 12345
|
|
1101
|
+
mock_fleet.fleet_id = 67890
|
|
1102
|
+
|
|
1103
|
+
mock_token = MagicMock()
|
|
1104
|
+
|
|
1105
|
+
mock_esi_client = MagicMock()
|
|
1106
|
+
mock_esi_client.Fleets.GetFleetsFleetIdMembers.return_value.result.return_value = (
|
|
1107
|
+
[]
|
|
1108
|
+
)
|
|
1109
|
+
|
|
1110
|
+
with patch("fleetfinder.tasks.Fleet.objects.get", return_value=mock_fleet):
|
|
1111
|
+
with patch("fleetfinder.tasks.Token.get_token", return_value=mock_token):
|
|
1112
|
+
with patch("fleetfinder.tasks.esi", Mock(client=mock_esi_client)):
|
|
1113
|
+
result = get_fleet_composition(fleet_id=67890)
|
|
138
1114
|
|
|
139
|
-
|
|
140
|
-
|
|
1115
|
+
self.assertEqual(result.fleet, [])
|
|
1116
|
+
self.assertEqual(result.aggregate, {})
|