aa-fleetfinder 0.1.0a12__py3-none-any.whl → 3.0.0b2__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.
Files changed (89) hide show
  1. aa_fleetfinder-3.0.0b2.dist-info/METADATA +820 -0
  2. aa_fleetfinder-3.0.0b2.dist-info/RECORD +86 -0
  3. {aa_fleetfinder-0.1.0a12.dist-info → aa_fleetfinder-3.0.0b2.dist-info}/WHEEL +1 -2
  4. fleetfinder/__init__.py +19 -0
  5. fleetfinder/app_settings.py +20 -0
  6. fleetfinder/apps.py +22 -0
  7. fleetfinder/auth_hooks.py +58 -0
  8. fleetfinder/locale/cs_CZ/LC_MESSAGES/django.mo +0 -0
  9. fleetfinder/locale/cs_CZ/LC_MESSAGES/django.po +296 -0
  10. fleetfinder/locale/de/LC_MESSAGES/django.mo +0 -0
  11. fleetfinder/locale/de/LC_MESSAGES/django.po +306 -0
  12. fleetfinder/locale/django.pot +303 -0
  13. fleetfinder/locale/es/LC_MESSAGES/django.mo +0 -0
  14. fleetfinder/locale/es/LC_MESSAGES/django.po +319 -0
  15. fleetfinder/locale/fr_FR/LC_MESSAGES/django.mo +0 -0
  16. fleetfinder/locale/fr_FR/LC_MESSAGES/django.po +314 -0
  17. fleetfinder/locale/it_IT/LC_MESSAGES/django.mo +0 -0
  18. fleetfinder/locale/it_IT/LC_MESSAGES/django.po +294 -0
  19. fleetfinder/locale/ja/LC_MESSAGES/django.mo +0 -0
  20. fleetfinder/locale/ja/LC_MESSAGES/django.po +303 -0
  21. fleetfinder/locale/ko_KR/LC_MESSAGES/django.mo +0 -0
  22. fleetfinder/locale/ko_KR/LC_MESSAGES/django.po +337 -0
  23. fleetfinder/locale/nl_NL/LC_MESSAGES/django.mo +0 -0
  24. fleetfinder/locale/nl_NL/LC_MESSAGES/django.po +294 -0
  25. fleetfinder/locale/pl_PL/LC_MESSAGES/django.mo +0 -0
  26. fleetfinder/locale/pl_PL/LC_MESSAGES/django.po +298 -0
  27. fleetfinder/locale/ru/LC_MESSAGES/django.mo +0 -0
  28. fleetfinder/locale/ru/LC_MESSAGES/django.po +319 -0
  29. fleetfinder/locale/sk/LC_MESSAGES/django.mo +0 -0
  30. fleetfinder/locale/sk/LC_MESSAGES/django.po +294 -0
  31. fleetfinder/locale/uk/LC_MESSAGES/django.mo +0 -0
  32. fleetfinder/locale/uk/LC_MESSAGES/django.po +310 -0
  33. fleetfinder/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  34. fleetfinder/locale/zh_Hans/LC_MESSAGES/django.po +319 -0
  35. fleetfinder/migrations/0001_initial.py +72 -0
  36. fleetfinder/migrations/0002_esi_error_handling_and_verbose_names.py +92 -0
  37. fleetfinder/migrations/0003_alter_fleet_fleet_commander_alter_fleet_groups_and_more.py +46 -0
  38. fleetfinder/migrations/__init__.py +0 -0
  39. fleetfinder/models.py +95 -0
  40. fleetfinder/providers.py +32 -0
  41. fleetfinder/static/fleetfinder/css/fleetfinder.css +31 -0
  42. fleetfinder/static/fleetfinder/css/fleetfinder.min.css +2 -0
  43. fleetfinder/static/fleetfinder/css/fleetfinder.min.css.map +1 -0
  44. fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.js +86 -0
  45. fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.min.js +2 -0
  46. fleetfinder/static/fleetfinder/js/fleetfinder-dashboard.min.js.map +1 -0
  47. fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.js +154 -0
  48. fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.min.js +2 -0
  49. fleetfinder/static/fleetfinder/js/fleetfinder-fleet-details.min.js.map +1 -0
  50. fleetfinder/static/fleetfinder/js/fleetfinder.js +23 -0
  51. fleetfinder/static/fleetfinder/js/fleetfinder.min.js +2 -0
  52. fleetfinder/static/fleetfinder/js/fleetfinder.min.js.map +1 -0
  53. fleetfinder/static/fleetfinder/libs/slim-select/2.6.0/css/slimselect.css +477 -0
  54. fleetfinder/static/fleetfinder/libs/slim-select/2.6.0/css/slimselect.min.css +2 -0
  55. fleetfinder/static/fleetfinder/libs/slim-select/2.6.0/css/slimselect.min.css.map +1 -0
  56. fleetfinder/static/fleetfinder/libs/slim-select/2.6.0/js/slimselect.min.js +1 -0
  57. fleetfinder/tasks.py +554 -0
  58. fleetfinder/templates/fleetfinder/base.html +43 -0
  59. fleetfinder/templates/fleetfinder/bundles/css/fleetfinder-css.html +3 -0
  60. fleetfinder/templates/fleetfinder/bundles/css/slim-select-css.html +3 -0
  61. fleetfinder/templates/fleetfinder/bundles/js/fleetfinder-js.html +9 -0
  62. fleetfinder/templates/fleetfinder/bundles/js/slim-select-js.html +3 -0
  63. fleetfinder/templates/fleetfinder/create-fleet.html +42 -0
  64. fleetfinder/templates/fleetfinder/dashboard.html +53 -0
  65. fleetfinder/templates/fleetfinder/edit-fleet.html +42 -0
  66. fleetfinder/templates/fleetfinder/fleet-details.html +102 -0
  67. fleetfinder/templates/fleetfinder/join-fleet.html +68 -0
  68. fleetfinder/templates/fleetfinder/modals/kick-fleet-member.html +46 -0
  69. fleetfinder/templates/fleetfinder/partials/body/form-fleet-details.html +50 -0
  70. fleetfinder/templates/fleetfinder/partials/footer/app-translation-footer.html +11 -0
  71. fleetfinder/templates/fleetfinder/partials/header/header-nav-left.html +9 -0
  72. fleetfinder/templates/fleetfinder/partials/header/header-nav-right.html +18 -0
  73. fleetfinder/templatetags/__init__.py +3 -0
  74. fleetfinder/templatetags/fleetfinder.py +33 -0
  75. fleetfinder/tests/__init__.py +41 -0
  76. fleetfinder/tests/test_access.py +74 -0
  77. fleetfinder/tests/test_auth_hooks.py +79 -0
  78. fleetfinder/tests/test_settings.py +38 -0
  79. fleetfinder/tests/test_tasks.py +1116 -0
  80. fleetfinder/tests/test_templatetags.py +65 -0
  81. fleetfinder/tests/test_user_agent.py +88 -0
  82. fleetfinder/tests/test_views.py +1184 -0
  83. fleetfinder/tests/utils.py +58 -0
  84. fleetfinder/urls.py +45 -0
  85. fleetfinder/views.py +631 -0
  86. aa_fleetfinder-0.1.0a12.dist-info/METADATA +0 -50
  87. aa_fleetfinder-0.1.0a12.dist-info/RECORD +0 -5
  88. aa_fleetfinder-0.1.0a12.dist-info/top_level.txt +0 -1
  89. {aa_fleetfinder-0.1.0a12.dist-info → aa_fleetfinder-3.0.0b2.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,1184 @@
1
+ """
2
+ Test the views for the Fleet Finder application.
3
+ """
4
+
5
+ # Standard Library
6
+ import json
7
+ from http import HTTPStatus
8
+ from types import SimpleNamespace
9
+ from unittest.mock import MagicMock, Mock, patch
10
+
11
+ # Django
12
+ from django.contrib.auth.models import Group
13
+ from django.urls import reverse
14
+ from django.utils.datetime_safe import datetime
15
+ from django.utils.timezone import now
16
+
17
+ # Alliance Auth
18
+ from allianceauth.groupmanagement.models import AuthGroup
19
+ from esi.exceptions import HTTPClientError
20
+
21
+ # Alliance Auth (External Libs)
22
+ from app_utils.testing import create_fake_user
23
+
24
+ # AA Fleet Finder
25
+ from fleetfinder.models import Fleet
26
+ from fleetfinder.tests import BaseTestCase
27
+ from fleetfinder.views import (
28
+ _get_and_validate_fleet,
29
+ ajax_fleet_kick_member,
30
+ create_fleet,
31
+ join_fleet,
32
+ save_fleet,
33
+ )
34
+
35
+
36
+ def _dt_to_iso(dt: datetime) -> str:
37
+ """
38
+ Helper :: Convert a datetime object to ISO 8601 format.
39
+
40
+ @see https://github.com/django/django/blob/main/django/core/serializers/json.py#L92-L98
41
+
42
+ :param dt:
43
+ :type dt:
44
+ :return:
45
+ :rtype:
46
+ """
47
+
48
+ r = dt.isoformat()
49
+
50
+ if dt.microsecond:
51
+ r = r[:23] + r[26:]
52
+
53
+ if r.endswith("+00:00"):
54
+ r = r.removesuffix("+00:00") + "Z"
55
+
56
+ return r
57
+
58
+
59
+ class FleetfinderTestViews(BaseTestCase):
60
+ """
61
+ Base test case for Fleet Finder views.
62
+ This class sets up the necessary users and fleet ID for testing.
63
+ It includes a user with the `fleetfinder.manage_fleets` permission
64
+ and a user with `fleetfinder.access_fleetfinder` access permissions.
65
+ The fleet ID is set to a predefined value for testing purposes.
66
+ """
67
+
68
+ @classmethod
69
+ def setUp(cls):
70
+ """
71
+ Set up the test case.
72
+
73
+ :return:
74
+ :rtype:
75
+ """
76
+
77
+ cls.user_with_manage_perms = create_fake_user(
78
+ character_id=1000,
79
+ character_name="Jean Luc Picard",
80
+ permissions=["fleetfinder.access_fleetfinder", "fleetfinder.manage_fleets"],
81
+ )
82
+ cls.user_with_basic_acces_perms = create_fake_user(
83
+ character_id=1001,
84
+ character_name="William Riker",
85
+ permissions=["fleetfinder.access_fleetfinder"],
86
+ )
87
+
88
+ cls.fleet_created_at = now()
89
+
90
+ cls.fleet = Fleet(
91
+ fleet_id=12345,
92
+ name="Starfleet",
93
+ fleet_commander=cls.user_with_manage_perms.profile.main_character,
94
+ created_at=cls.fleet_created_at,
95
+ is_free_move=False,
96
+ )
97
+ cls.fleet.save()
98
+
99
+ cls.fleet_id = 12345
100
+
101
+
102
+ class TestHelperGetAndValidateFleet(FleetfinderTestViews):
103
+ """
104
+ Tests for the _get_and_validate_fleet helper function.
105
+ """
106
+
107
+ def test_retrieves_fleet_information_successfully(self):
108
+ """
109
+ Test that the _get_and_validate_fleet function retrieves fleet information successfully.
110
+
111
+ :return:
112
+ :rtype:
113
+ """
114
+
115
+ mock_token = MagicMock(character_id=12345)
116
+ mock_fleet_result = MagicMock(fleet_id=67890, fleet_boss_id=12345)
117
+ esi_stub = SimpleNamespace(
118
+ client=SimpleNamespace(
119
+ Fleets=SimpleNamespace(GetCharactersCharacterIdFleet=MagicMock())
120
+ )
121
+ )
122
+
123
+ with patch("fleetfinder.views.esi", new=esi_stub):
124
+ esi_stub.client.Fleets.GetCharactersCharacterIdFleet.return_value.result.return_value = (
125
+ mock_fleet_result
126
+ )
127
+ result = _get_and_validate_fleet(mock_token, 12345)
128
+
129
+ self.assertEqual(result, mock_fleet_result)
130
+
131
+ def test_raises_value_error_when_fleet_not_found(self):
132
+ """
133
+ Test that the _get_and_validate_fleet function raises a ValueError when fleet is not found.
134
+
135
+ :return:
136
+ :rtype:
137
+ """
138
+
139
+ mock_token = MagicMock(character_id=12345)
140
+ esi_stub = SimpleNamespace(
141
+ client=SimpleNamespace(
142
+ Fleets=SimpleNamespace(GetCharactersCharacterIdFleet=MagicMock())
143
+ )
144
+ )
145
+
146
+ with patch("fleetfinder.views.esi", new=esi_stub):
147
+ esi_stub.client.Fleets.GetCharactersCharacterIdFleet.side_effect = (
148
+ HTTPClientError(404, {}, {})
149
+ )
150
+
151
+ with self.assertRaises(ValueError) as context:
152
+ _get_and_validate_fleet(mock_token, 12345)
153
+
154
+ self.assertEqual(str(context.exception), "Fleet not found")
155
+
156
+ def test_raises_runtime_error_on_unexpected_exception(self):
157
+ """
158
+ Test that the _get_and_validate_fleet function raises a RuntimeError on unexpected exceptions.
159
+
160
+ :return:
161
+ :rtype:
162
+ """
163
+
164
+ mock_token = MagicMock(character_id=12345)
165
+ esi_stub = SimpleNamespace(
166
+ client=SimpleNamespace(
167
+ Fleets=SimpleNamespace(GetCharactersCharacterIdFleet=MagicMock())
168
+ )
169
+ )
170
+
171
+ with patch("fleetfinder.views.esi", new=esi_stub):
172
+ esi_stub.client.Fleets.GetCharactersCharacterIdFleet.side_effect = (
173
+ Exception("Unexpected error")
174
+ )
175
+
176
+ with self.assertRaises(RuntimeError) as context:
177
+ _get_and_validate_fleet(mock_token, 12345)
178
+
179
+ self.assertIn("Error retrieving fleet from ESI", str(context.exception))
180
+
181
+ def test_raises_value_error_when_fleet_id_is_missing(self):
182
+ """
183
+ Test that the _get_and_validate_fleet function raises a ValueError when fleet ID is missing.
184
+
185
+ :return:
186
+ :rtype:
187
+ """
188
+
189
+ mock_token = MagicMock(character_id=12345)
190
+ mock_fleet_result = MagicMock(fleet_id=None)
191
+ esi_stub = SimpleNamespace(
192
+ client=SimpleNamespace(
193
+ Fleets=SimpleNamespace(GetCharactersCharacterIdFleet=MagicMock())
194
+ )
195
+ )
196
+ with patch("fleetfinder.views.esi", new=esi_stub):
197
+ esi_stub.client.Fleets.GetCharactersCharacterIdFleet.return_value.result.return_value = (
198
+ mock_fleet_result
199
+ )
200
+ with patch(
201
+ "fleetfinder.views.EveCharacter.objects.get"
202
+ ) as mock_get_character:
203
+ mock_get_character.return_value.character_name = "Commander"
204
+ with self.assertRaises(ValueError) as context:
205
+ _get_and_validate_fleet(mock_token, 12345)
206
+ self.assertIn("No fleet found for Commander", str(context.exception))
207
+
208
+ def test_raises_value_error_when_not_fleet_boss(self):
209
+ """
210
+ Test that the _get_and_validate_fleet function raises a ValueError when the user is not the fleet boss.
211
+
212
+ :return:
213
+ :rtype:
214
+ """
215
+
216
+ mock_token = MagicMock(character_id=12345)
217
+ mock_fleet_result = MagicMock(fleet_id=67890, fleet_boss_id=54321)
218
+
219
+ esi_stub = SimpleNamespace(
220
+ client=SimpleNamespace(
221
+ Fleets=SimpleNamespace(GetCharactersCharacterIdFleet=MagicMock())
222
+ )
223
+ )
224
+
225
+ with patch("fleetfinder.views.esi", new=esi_stub):
226
+ esi_stub.client.Fleets.GetCharactersCharacterIdFleet.return_value.result.return_value = (
227
+ mock_fleet_result
228
+ )
229
+
230
+ with patch(
231
+ "fleetfinder.views.EveCharacter.objects.get"
232
+ ) as mock_get_character:
233
+ mock_get_character.return_value.character_name = "Commander"
234
+
235
+ with self.assertRaises(ValueError) as context:
236
+ _get_and_validate_fleet(mock_token, 12345)
237
+
238
+ self.assertIn("Commander is not the fleet boss", str(context.exception))
239
+
240
+
241
+ class TestAjaxDashboardView(FleetfinderTestViews):
242
+ """
243
+ Test the ajax_dashboard view in the Fleet Finder application.
244
+ This view is responsible for rendering the dashboard with fleet data.
245
+ It should return a JSON response containing fleet information,
246
+ including fleet names, commanders, and group associations.
247
+ If no fleets are available, it should return an empty list.
248
+ It should also filter fleets based on the user's groups.
249
+ """
250
+
251
+ @patch("fleetfinder.views.get_all_characters_from_user")
252
+ def test_renders_dashboard_with_fleet_data_with_basic_access(
253
+ self, mock_get_characters
254
+ ):
255
+ """
256
+ Test that the ajax_dashboard view renders the dashboard with fleet data
257
+ when the user has basic access permissions.
258
+
259
+ :param mock_get_characters:
260
+ :type mock_get_characters:
261
+ :return:
262
+ :rtype:
263
+ """
264
+
265
+ mock_get_characters.return_value = [
266
+ self.user_with_manage_perms.profile.main_character
267
+ ]
268
+
269
+ fleet = self.fleet
270
+ fleet.groups.set([])
271
+
272
+ self.client.force_login(self.user_with_basic_acces_perms)
273
+ url = reverse("fleetfinder:ajax_dashboard")
274
+ join_url = reverse("fleetfinder:join_fleet", args=[self.fleet_id])
275
+ response = self.client.get(url)
276
+
277
+ expected_response = [
278
+ {
279
+ "fleet_commander": {
280
+ "html": '<img class="rounded eve-character-portrait" src="https://images.evetech.net/characters/1000/portrait?size=32" alt="Jean Luc Picard" loading="lazy">Jean Luc Picard',
281
+ "sort": "Jean Luc Picard",
282
+ },
283
+ "fleet_name": "Starfleet",
284
+ "created_at": _dt_to_iso(self.fleet_created_at),
285
+ "actions": f'<a href="{join_url}" class="btn btn-sm btn-success ms-1" data-bs-tooltip="aa-fleetfinder" title="Join fleet"><i class="fa-solid fa-right-to-bracket"></i></a>',
286
+ }
287
+ ]
288
+
289
+ self.assertEqual(response.status_code, HTTPStatus.OK)
290
+ self.assertIn("Starfleet", response.json()[0]["fleet_name"])
291
+ self.assertIn("Jean Luc Picard", response.json()[0]["fleet_commander"]["html"])
292
+ self.assertEqual(response.json(), expected_response)
293
+
294
+ @patch("fleetfinder.views.get_all_characters_from_user")
295
+ def test_renders_dashboard_with_fleet_data_with_manage_access(
296
+ self, mock_get_characters
297
+ ):
298
+ """
299
+ Test that the ajax_dashboard view renders the dashboard with fleet data
300
+ when the user has manage access permissions.
301
+
302
+ :param mock_get_characters:
303
+ :type mock_get_characters:
304
+ :return:
305
+ :rtype:
306
+ """
307
+
308
+ mock_get_characters.return_value = [
309
+ self.user_with_manage_perms.profile.main_character
310
+ ]
311
+
312
+ fleet = self.fleet
313
+ fleet.groups.set([])
314
+
315
+ self.client.force_login(self.user_with_manage_perms)
316
+ url = reverse("fleetfinder:ajax_dashboard")
317
+ join_url = reverse("fleetfinder:join_fleet", args=[self.fleet_id])
318
+ details_url = reverse("fleetfinder:fleet_details", args=[self.fleet_id])
319
+ edit_url = reverse("fleetfinder:edit_fleet", args=[self.fleet_id])
320
+ response = self.client.get(url)
321
+
322
+ expected_response = [
323
+ {
324
+ "fleet_commander": {
325
+ "html": '<img class="rounded eve-character-portrait" src="https://images.evetech.net/characters/1000/portrait?size=32" alt="Jean Luc Picard" loading="lazy">Jean Luc Picard',
326
+ "sort": "Jean Luc Picard",
327
+ },
328
+ "fleet_name": "Starfleet",
329
+ "created_at": _dt_to_iso(self.fleet_created_at),
330
+ "actions": (
331
+ f'<a href="{join_url}" class="btn btn-sm btn-success ms-1" data-bs-tooltip="aa-fleetfinder" title="Join fleet"><i class="fa-solid fa-right-to-bracket"></i></a>'
332
+ f'<a href="{details_url}" class="btn btn-sm btn-info ms-1" data-bs-tooltip="aa-fleetfinder" title="View fleet details"><i class="fa-solid fa-eye"></i></a>'
333
+ f'<a href="{edit_url}" class="btn btn-sm btn-warning ms-1" data-bs-tooltip="aa-fleetfinder" title="Edit fleet advert"><i class="fa-solid fa-pen-to-square"></i></a>'
334
+ ),
335
+ }
336
+ ]
337
+
338
+ self.assertEqual(response.status_code, HTTPStatus.OK)
339
+ self.assertIn("Starfleet", response.json()[0]["fleet_name"])
340
+ self.assertIn("Jean Luc Picard", response.json()[0]["fleet_commander"]["html"])
341
+ self.assertEqual(response.json(), expected_response)
342
+
343
+ @patch("fleetfinder.views.get_all_characters_from_user")
344
+ def test_returns_empty_data_when_no_fleets_available(self, mock_get_characters):
345
+ """
346
+ Test that the ajax_dashboard view returns an empty list when no fleets are available.
347
+
348
+ :param mock_get_characters:
349
+ :type mock_get_characters:
350
+ :return:
351
+ :rtype:
352
+ """
353
+
354
+ mock_get_characters.return_value = [
355
+ self.user_with_manage_perms.profile.main_character
356
+ ]
357
+
358
+ Fleet.objects.all().delete() # Remove all fleets
359
+
360
+ self.client.force_login(self.user_with_basic_acces_perms)
361
+ url = reverse("fleetfinder:ajax_dashboard")
362
+ response = self.client.get(url)
363
+
364
+ self.assertEqual(response.status_code, HTTPStatus.OK)
365
+ self.assertEqual(response.json(), [])
366
+
367
+ @patch("fleetfinder.views.get_all_characters_from_user")
368
+ def test_filters_fleets_by_user_groups(self, mock_get_characters):
369
+ """
370
+ Test that the ajax_dashboard view filters fleets based on the user's groups.
371
+
372
+ :param mock_get_characters:
373
+ :type mock_get_characters:
374
+ :return:
375
+ :rtype:
376
+ """
377
+
378
+ mock_get_characters.return_value = [
379
+ self.user_with_manage_perms.profile.main_character
380
+ ]
381
+
382
+ group_obj = Group.objects.create(name="Starfleet Officers")
383
+ auth_group, _ = AuthGroup.objects.get_or_create(group=group_obj)
384
+ fleet = self.fleet
385
+ fleet.groups.set([auth_group])
386
+
387
+ self.client.force_login(self.user_with_basic_acces_perms)
388
+ self.user_with_basic_acces_perms.groups.add(group_obj)
389
+ url = reverse("fleetfinder:ajax_dashboard")
390
+ response = self.client.get(url)
391
+
392
+ self.assertEqual(response.status_code, HTTPStatus.OK)
393
+ self.assertIn("Starfleet", response.json()[0]["fleet_name"])
394
+
395
+
396
+ class TestCreateFleetView(FleetfinderTestViews):
397
+ """
398
+ Test the create_fleet view in the Fleet Finder application.
399
+ This view is responsible for creating new fleet adverts.
400
+ """
401
+
402
+ def test_redirects_to_dashboard_when_token_validation_fails(self):
403
+ """
404
+ Test that the create_fleet view redirects to the dashboard when token validation fails.
405
+
406
+ :return:
407
+ :rtype:
408
+ """
409
+
410
+ mock_request = MagicMock(method="POST", user=MagicMock())
411
+ mock_request.session = MagicMock(
412
+ session_key="session123", exists=MagicMock(return_value=True)
413
+ )
414
+ mock_token = MagicMock(character_id=12345)
415
+
416
+ with patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet:
417
+ mock_validate_fleet.side_effect = ValueError("Invalid token")
418
+ view_func = create_fleet
419
+
420
+ while hasattr(view_func, "__wrapped__"):
421
+ view_func = view_func.__wrapped__
422
+
423
+ response = view_func(mock_request, mock_token)
424
+
425
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
426
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
427
+
428
+ def test_redirects_to_dashboard_on_get_request(self):
429
+ """
430
+ Test that the create_fleet view redirects to the dashboard on a GET request.
431
+
432
+ :return:
433
+ :rtype:
434
+ """
435
+
436
+ mock_request = MagicMock(method="GET", user=self.user_with_manage_perms)
437
+ mock_request.session = MagicMock(
438
+ session_key="session123", exists=MagicMock(return_value=True)
439
+ )
440
+ mock_token = MagicMock(character_id=12345)
441
+ with (
442
+ patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet,
443
+ patch("fleetfinder.views.AuthGroup.objects.filter") as mock_filter,
444
+ ):
445
+ mock_validate_fleet.return_value = MagicMock()
446
+ mock_filter.return_value = []
447
+ view_func = create_fleet
448
+ while hasattr(view_func, "__wrapped__"):
449
+ view_func = view_func.__wrapped__
450
+ response = view_func(mock_request, mock_token)
451
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
452
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
453
+
454
+ def test_renders_create_fleet_template_on_valid_post_request(self):
455
+ """
456
+ Test that the create_fleet view renders the create fleet template on a valid POST request.
457
+
458
+ :return:
459
+ :rtype:
460
+ """
461
+ mock_request = MagicMock(method="POST", user=MagicMock())
462
+ mock_request.session = MagicMock(
463
+ session_key="session123", exists=MagicMock(return_value=True)
464
+ )
465
+ mock_token = MagicMock(character_id=12345)
466
+ with (
467
+ patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet,
468
+ patch("fleetfinder.views.AuthGroup.objects.filter") as mock_filter,
469
+ patch("fleetfinder.views.render") as mock_render,
470
+ ):
471
+ mock_validate_fleet.return_value = MagicMock()
472
+ mock_filter.return_value = []
473
+ view_func = create_fleet
474
+ while hasattr(view_func, "__wrapped__"):
475
+ view_func = view_func.__wrapped__
476
+ view_func(mock_request, mock_token)
477
+ mock_render.assert_called_once_with(
478
+ request=mock_request,
479
+ template_name="fleetfinder/create-fleet.html",
480
+ context={"character_id": mock_token.character_id, "auth_groups": []},
481
+ )
482
+
483
+
484
+ class TestJoinFleetView(FleetfinderTestViews):
485
+ """
486
+ Test the join_fleet view in the Fleet Finder application.
487
+ This view is responsible for allowing users to join a fleet.
488
+ It should redirect to the fleet details page after joining.
489
+ If the fleet does not exist, it should return a 404 status code.
490
+ """
491
+
492
+ def test_redirects_to_dashboard_if_fleet_not_found(self):
493
+ """
494
+ Test that the join_fleet view redirects to the dashboard if the fleet is not found.
495
+
496
+ :return:
497
+ :rtype:
498
+ """
499
+
500
+ mock_request = MagicMock(method="GET", user=MagicMock())
501
+ mock_request.user.groups.all.return_value = []
502
+
503
+ with patch("fleetfinder.views.Fleet.objects.filter") as mock_filter:
504
+ mock_filter.return_value.count.return_value = 0
505
+ response = join_fleet(mock_request, fleet_id=1)
506
+
507
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
508
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
509
+
510
+ def test_redirects_to_dashboard_on_post_and_sends_invitations(self):
511
+ """
512
+ Test that the join_fleet view redirects to the dashboard on a POST request and sends fleet invitations.
513
+
514
+ :return:
515
+ :rtype:
516
+ """
517
+
518
+ mock_request = MagicMock(method="POST", user=MagicMock())
519
+ mock_request.user.groups.all.return_value = []
520
+ mock_request.POST.getlist.return_value = [1001, 1002]
521
+
522
+ with (
523
+ patch("fleetfinder.views.Fleet.objects.filter") as mock_filter,
524
+ patch(
525
+ "fleetfinder.views.send_fleet_invitation.delay"
526
+ ) as mock_send_invitation,
527
+ ):
528
+ mock_filter.return_value.count.return_value = 1
529
+ response = join_fleet(mock_request, fleet_id=1)
530
+
531
+ mock_send_invitation.assert_called_once_with(
532
+ character_ids=[1001, 1002], fleet_id=1
533
+ )
534
+
535
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
536
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
537
+
538
+ def test_renders_join_fleet_template_with_characters_on_get(self):
539
+ """
540
+ Test that the join_fleet view renders the join fleet template with characters on a GET request.
541
+
542
+ :return:
543
+ :rtype:
544
+ """
545
+
546
+ mock_request = MagicMock(method="GET", user=MagicMock())
547
+ mock_request.user.groups.all.return_value = []
548
+ mock_request.user.__str__.return_value = "test_user"
549
+ mock_characters = [
550
+ MagicMock(character_name="Char1"),
551
+ MagicMock(character_name="Char2"),
552
+ ]
553
+
554
+ with (
555
+ patch("fleetfinder.views.Fleet.objects.filter") as mock_filter,
556
+ patch(
557
+ "fleetfinder.views.EveCharacter.objects.filter"
558
+ ) as mock_character_filter,
559
+ patch("fleetfinder.views.render") as mock_render,
560
+ ):
561
+ mock_filter.return_value.count.return_value = 1
562
+ mock_character_filter.return_value.select_related.return_value.order_by.return_value = (
563
+ mock_characters
564
+ )
565
+
566
+ join_fleet(mock_request, fleet_id=1)
567
+
568
+ mock_render.assert_called_once_with(
569
+ request=mock_request,
570
+ template_name="fleetfinder/join-fleet.html",
571
+ context={"characters": mock_characters},
572
+ )
573
+
574
+
575
+ class TestSaveFleetView(FleetfinderTestViews):
576
+ """
577
+ Test the save_fleet view in the Fleet Finder application.
578
+ This view is responsible for saving fleet details.
579
+ It should redirect to the dashboard after saving.
580
+ """
581
+
582
+ def test_redirects_to_dashboard_if_request_method_is_not_post(self):
583
+ """
584
+ Test that the save_fleet view redirects to the dashboard if the request method is not POST.
585
+
586
+ :return:
587
+ :rtype:
588
+ """
589
+
590
+ mock_request = MagicMock(method="GET", user=MagicMock())
591
+ response = save_fleet(mock_request)
592
+
593
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
594
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
595
+
596
+ def test_creates_new_fleet_with_valid_data(self):
597
+ """
598
+ Test that the save_fleet view creates a new fleet with valid data.
599
+
600
+ :return:
601
+ :rtype:
602
+ """
603
+
604
+ mock_request = MagicMock(method="POST", user=MagicMock())
605
+ post_data = {
606
+ "character_id": "12345",
607
+ "free_move": "on",
608
+ "name": "Test Fleet",
609
+ "groups": ["1", "2"],
610
+ }
611
+ mock_request.POST = MagicMock()
612
+ mock_request.POST.get.side_effect = lambda k, default=None: post_data.get(
613
+ k, default
614
+ )
615
+ mock_request.POST.getlist.side_effect = lambda k, default=None: post_data.get(
616
+ k, default
617
+ )
618
+
619
+ esi_stub = SimpleNamespace(
620
+ client=SimpleNamespace(Fleets=SimpleNamespace(PutFleetsFleetId=MagicMock()))
621
+ )
622
+
623
+ with (
624
+ patch(
625
+ "fleetfinder.views.Fleet.objects.get_or_create"
626
+ ) as mock_get_or_create,
627
+ patch("fleetfinder.views.Token.get_token") as mock_get_token,
628
+ patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet,
629
+ patch("fleetfinder.views.EveCharacter.objects.get") as mock_eve_get,
630
+ patch("fleetfinder.views.esi", new=esi_stub),
631
+ ):
632
+ mock_get_or_create.return_value = (MagicMock(), True)
633
+ mock_get_token.return_value = MagicMock()
634
+ mock_validate_fleet.return_value = MagicMock(fleet_id=1)
635
+ mock_eve_get.return_value = MagicMock(
636
+ character_id=int(post_data["character_id"])
637
+ )
638
+ response = save_fleet(mock_request)
639
+
640
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
641
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
642
+
643
+ def test_updates_existing_fleet_with_valid_data(self):
644
+ """
645
+ Test that the save_fleet view updates an existing fleet with valid data.
646
+
647
+ :return:
648
+ :rtype:
649
+ """
650
+
651
+ mock_request = MagicMock(method="POST", user=MagicMock())
652
+ post_data = {
653
+ "character_id": "12345",
654
+ "free_move": "on",
655
+ "name": "Updated Fleet",
656
+ "groups": ["1", "2"],
657
+ }
658
+ mock_request.POST = MagicMock()
659
+ mock_request.POST.get.side_effect = lambda k, default=None: post_data.get(
660
+ k, default
661
+ )
662
+ mock_request.POST.getlist.side_effect = lambda k, default=None: post_data.get(
663
+ k, default
664
+ )
665
+
666
+ mock_fleet = MagicMock()
667
+ esi_stub = SimpleNamespace(
668
+ client=SimpleNamespace(Fleets=SimpleNamespace(PutFleetsFleetId=MagicMock()))
669
+ )
670
+
671
+ with (
672
+ patch(
673
+ "fleetfinder.views.Fleet.objects.get_or_create"
674
+ ) as mock_get_or_create,
675
+ patch("fleetfinder.views.Token.get_token") as mock_get_token,
676
+ patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet,
677
+ patch("fleetfinder.views.EveCharacter.objects.get") as mock_eve_get,
678
+ patch("fleetfinder.views.esi", new=esi_stub),
679
+ ):
680
+ mock_get_or_create.return_value = (mock_fleet, False)
681
+ mock_get_token.return_value = MagicMock()
682
+ mock_validate_fleet.return_value = MagicMock(fleet_id=1)
683
+ mock_eve_get.return_value = MagicMock(
684
+ character_id=int(post_data["character_id"])
685
+ )
686
+ response = save_fleet(mock_request)
687
+ mock_fleet.save.assert_called_once()
688
+
689
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
690
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
691
+
692
+ def test_handles_http_client_error_during_fleet_creation(self):
693
+ """
694
+ Test that the save_fleet view handles HTTPClientError during fleet creation.
695
+
696
+ :return:
697
+ :rtype:
698
+ """
699
+
700
+ mock_request = MagicMock(method="POST", user=MagicMock())
701
+ post_data = {
702
+ "character_id": "12345",
703
+ "free_move": "on",
704
+ "name": "Test Fleet",
705
+ "groups": ["1", "2"],
706
+ }
707
+ mock_request.POST = MagicMock()
708
+ mock_request.POST.get.side_effect = lambda k, default=None: post_data.get(
709
+ k, default
710
+ )
711
+ mock_request.POST.getlist.side_effect = lambda k, default=None: post_data.get(
712
+ k, default
713
+ )
714
+
715
+ esi_stub = SimpleNamespace(
716
+ client=SimpleNamespace(Fleets=SimpleNamespace(PutFleetsFleetId=MagicMock()))
717
+ )
718
+
719
+ with (
720
+ patch("fleetfinder.views.Token.get_token") as mock_get_token,
721
+ patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet,
722
+ patch("fleetfinder.views.messages.error") as mock_messages,
723
+ patch("fleetfinder.views.EveCharacter.objects.get") as mock_eve_get,
724
+ patch("fleetfinder.views.esi", new=esi_stub),
725
+ ):
726
+ mock_get_token.return_value = MagicMock()
727
+ mock_validate_fleet.side_effect = HTTPClientError(400, {}, {})
728
+ mock_eve_get.return_value = MagicMock(
729
+ character_id=int(post_data["character_id"])
730
+ )
731
+ response = save_fleet(mock_request)
732
+ mock_messages.assert_called_once()
733
+
734
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
735
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
736
+
737
+ def test_handles_value_error_during_fleet_creation(self):
738
+ """
739
+ Test that the save_fleet view handles ValueError during fleet creation.
740
+
741
+ :return:
742
+ :rtype:
743
+ """
744
+
745
+ mock_request = MagicMock(method="POST", user=MagicMock())
746
+ post_data = {
747
+ "character_id": "12345",
748
+ "free_move": "on",
749
+ "name": "Test Fleet",
750
+ "groups": ["1", "2"],
751
+ }
752
+ mock_request.POST = MagicMock()
753
+ mock_request.POST.get.side_effect = lambda k, default=None: post_data.get(
754
+ k, default
755
+ )
756
+ mock_request.POST.getlist.side_effect = lambda k, default=None: post_data.get(
757
+ k, default
758
+ )
759
+
760
+ esi_stub = SimpleNamespace(
761
+ client=SimpleNamespace(Fleets=SimpleNamespace(PutFleetsFleetId=MagicMock()))
762
+ )
763
+
764
+ with (
765
+ patch("fleetfinder.views.Token.get_token") as mock_get_token,
766
+ patch("fleetfinder.views._get_and_validate_fleet") as mock_validate_fleet,
767
+ patch("fleetfinder.views.messages.error") as mock_messages,
768
+ patch("fleetfinder.views.EveCharacter.objects.get") as mock_eve_get,
769
+ patch("fleetfinder.views.esi", new=esi_stub),
770
+ ):
771
+ mock_get_token.return_value = MagicMock()
772
+ mock_validate_fleet.side_effect = ValueError("Invalid fleet")
773
+ mock_eve_get.return_value = MagicMock(
774
+ character_id=int(post_data["character_id"])
775
+ )
776
+ response = save_fleet(mock_request)
777
+ mock_messages.assert_called_once()
778
+
779
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
780
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
781
+
782
+
783
+ class TestFleetEditView(FleetfinderTestViews):
784
+ """
785
+ Test the edit_fleet view in the Fleet Finder application.
786
+ This view is responsible for editing fleet details.
787
+ """
788
+
789
+ @patch("fleetfinder.views.Fleet.objects.get")
790
+ @patch("fleetfinder.views.AuthGroup.objects.filter")
791
+ def test_renders_edit_fleet_template_with_correct_context(
792
+ self, mock_filter_groups, mock_get_fleet
793
+ ):
794
+ """
795
+ Test that the edit_fleet view renders the correct template and context.
796
+
797
+ :param mock_filter_groups:
798
+ :type mock_filter_groups:
799
+ :param mock_get_fleet:
800
+ :type mock_get_fleet:
801
+ :return:
802
+ :rtype:
803
+ """
804
+
805
+ mock_get_fleet.return_value = self.fleet
806
+ group1 = Mock(spec=AuthGroup)
807
+ group1.name = "Group1"
808
+ group2 = Mock(spec=AuthGroup)
809
+ group2.name = "Group2"
810
+ mock_filter_groups.return_value = [group1, group2]
811
+
812
+ self.client.force_login(self.user_with_manage_perms)
813
+ url = reverse("fleetfinder:edit_fleet", args=[self.fleet_id])
814
+ response = self.client.get(url)
815
+
816
+ self.assertTemplateUsed(response, "fleetfinder/edit-fleet.html")
817
+ self.assertEqual(response.context["fleet"].name, "Starfleet")
818
+ self.assertEqual(response.context["character_id"], 1000)
819
+ self.assertEqual(len(response.context["auth_groups"]), 2)
820
+
821
+ @patch("fleetfinder.views.Fleet.objects.get")
822
+ def test_redirects_to_dashboard_if_fleet_does_not_exist(self, mock_get_fleet):
823
+ """
824
+ Test that the edit_fleet view redirects to the dashboard if the fleet does not exist.
825
+
826
+ :param mock_get_fleet:
827
+ :type mock_get_fleet:
828
+ :return:
829
+ :rtype:
830
+ """
831
+
832
+ mock_get_fleet.side_effect = Fleet.DoesNotExist
833
+
834
+ self.client.force_login(self.user_with_manage_perms)
835
+ url = reverse("fleetfinder:edit_fleet", args=[99999])
836
+ response = self.client.get(url)
837
+
838
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
839
+ self.assertRedirects(response, reverse("fleetfinder:dashboard"))
840
+
841
+
842
+ class TestFleetDetailsView(FleetfinderTestViews):
843
+ """
844
+ Test the fleet_details view in the Fleet Finder application.
845
+ This view is responsible for rendering the fleet details page.
846
+ It should render the correct template and require the user to have
847
+ the 'fleetfinder.manage_fleets' permission to access it.
848
+ If the fleet does not exist, it should return a 404 status code.
849
+ """
850
+
851
+ @patch("fleetfinder.views.Fleet.objects.get")
852
+ def test_fleet_details_renders_correct_template(self, mock_get_fleet):
853
+ """
854
+ Test that the fleet_details view renders the correct template.
855
+
856
+ :param mock_get_fleet:
857
+ :type mock_get_fleet:
858
+ :return:
859
+ :rtype:
860
+ """
861
+
862
+ mock_get_fleet.return_value = Fleet(fleet_id=self.fleet_id)
863
+
864
+ self.client.force_login(self.user_with_manage_perms)
865
+ url = reverse("fleetfinder:fleet_details", args=[self.fleet_id])
866
+ response = self.client.get(url)
867
+
868
+ self.assertEqual(response.status_code, HTTPStatus.OK)
869
+ self.assertTemplateUsed(response, "fleetfinder/fleet-details.html")
870
+
871
+ @patch("fleetfinder.views.Fleet.objects.get")
872
+ def test_fleet_details_requires_manage_permission(self, mock_get_fleet):
873
+ """
874
+ Test that the fleet_details view requires the user to have the 'fleetfinder.manage_fleets' permission.
875
+
876
+ :param mock_get_fleet:
877
+ :type mock_get_fleet:
878
+ :return:
879
+ :rtype:
880
+ """
881
+
882
+ mock_get_fleet.return_value = Fleet(fleet_id=self.fleet_id)
883
+
884
+ self.client.force_login(self.user_with_basic_acces_perms)
885
+ url = reverse("fleetfinder:fleet_details", args=[self.fleet_id])
886
+ response = self.client.get(url)
887
+
888
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
889
+
890
+ def test_fleet_redirects_to_dashboard_if_not_found(self):
891
+ """
892
+ Test that the fleet_details view redirects to the dashboard if the fleet does not exist.
893
+
894
+ :return:
895
+ :rtype:
896
+ """
897
+
898
+ self.client.force_login(self.user_with_manage_perms)
899
+
900
+ response = self.client.get(reverse("fleetfinder:fleet_details", args=[123]))
901
+
902
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
903
+ self.assertEqual(response.url, reverse("fleetfinder:dashboard"))
904
+
905
+
906
+ class TestAjaxFleetDetailsView(FleetfinderTestViews):
907
+ """
908
+ Test the ajax_fleet_details view in the Fleet Finder application.
909
+ This view is responsible for returning fleet details in JSON format.
910
+ It should return the fleet members and their ship types, or an empty list if the fleet is empty.
911
+ """
912
+
913
+ @patch("fleetfinder.views.get_fleet_composition")
914
+ def test_returns_correct_fleet_details(self, mock_get_fleet_composition):
915
+ """
916
+ Test that the ajax_fleet_details view returns the correct fleet details.
917
+
918
+ :param mock_get_fleet_composition:
919
+ :type mock_get_fleet_composition:
920
+ :return:
921
+ :rtype:
922
+ """
923
+
924
+ mock_get_fleet_composition.return_value = SimpleNamespace(
925
+ fleet=[{"name": "Pilot1"}, {"name": "Pilot2"}, {"name": "Pilot3"}],
926
+ aggregate={"Frigate": 2, "Cruiser": 1},
927
+ )
928
+
929
+ self.client.force_login(user=self.user_with_manage_perms)
930
+
931
+ url = reverse("fleetfinder:ajax_fleet_details", args=[self.fleet_id])
932
+ response = self.client.get(url)
933
+
934
+ expected_fleet_composition = {
935
+ "fleet_member": [
936
+ {"name": "Pilot1"},
937
+ {"name": "Pilot2"},
938
+ {"name": "Pilot3"},
939
+ ],
940
+ "fleet_composition": [
941
+ {"ship_type_name": "Frigate", "number": 2},
942
+ {"ship_type_name": "Cruiser", "number": 1},
943
+ ],
944
+ }
945
+
946
+ self.assertEqual(response.status_code, HTTPStatus.OK)
947
+ self.assertEqual(json.loads(response.content), expected_fleet_composition)
948
+
949
+ @patch("fleetfinder.views.get_fleet_composition")
950
+ def test_handles_empty_fleet(self, mock_get_fleet_composition):
951
+ """
952
+ Test that the ajax_fleet_details view handles an empty fleet correctly.
953
+
954
+ :param mock_get_fleet_composition:
955
+ :type mock_get_fleet_composition:
956
+ :return:
957
+ :rtype:
958
+ """
959
+
960
+ mock_get_fleet_composition.return_value = SimpleNamespace(
961
+ fleet=[],
962
+ aggregate={},
963
+ )
964
+
965
+ self.client.force_login(user=self.user_with_manage_perms)
966
+
967
+ url = reverse("fleetfinder:ajax_fleet_details", args=[self.fleet_id])
968
+ response = self.client.get(url)
969
+
970
+ expected_fleet_composition = {"fleet_member": [], "fleet_composition": []}
971
+
972
+ self.assertEqual(response.status_code, HTTPStatus.OK)
973
+ self.assertEqual(json.loads(response.content), expected_fleet_composition)
974
+
975
+ @patch("fleetfinder.views.get_fleet_composition")
976
+ def test_returns_error_when_fleet_does_not_exist(self, mock_get_fleet_composition):
977
+ """
978
+ Test that the ajax_fleet_details view returns an error when the fleet does not exist.
979
+
980
+ :param mock_get_fleet_composition:
981
+ :type mock_get_fleet_composition:
982
+ :return:
983
+ :rtype:
984
+ """
985
+
986
+ mock_get_fleet_composition.side_effect = Fleet.DoesNotExist
987
+
988
+ self.client.force_login(user=self.user_with_manage_perms)
989
+
990
+ url = reverse("fleetfinder:ajax_fleet_details", args=[123])
991
+ response = self.client.get(url)
992
+
993
+ self.assertEqual(response.status_code, HTTPStatus.OK)
994
+ self.assertJSONEqual(
995
+ response.content,
996
+ {"error": "Fleet with ID 123 does not exist."},
997
+ )
998
+
999
+ @patch("fleetfinder.views.get_fleet_composition")
1000
+ def test_returns_error_when_runtime_error_occurs(self, mock_get_fleet_composition):
1001
+ """
1002
+ Test that the ajax_fleet_details view returns an error when a runtime error occurs.
1003
+
1004
+ :param mock_get_fleet_composition:
1005
+ :type mock_get_fleet_composition:
1006
+ :return:
1007
+ :rtype:
1008
+ """
1009
+
1010
+ mock_get_fleet_composition.side_effect = RuntimeError("Unexpected error")
1011
+
1012
+ self.client.force_login(user=self.user_with_manage_perms)
1013
+
1014
+ url = reverse("fleetfinder:ajax_fleet_details", args=[123])
1015
+ response = self.client.get(url)
1016
+
1017
+ self.assertEqual(response.status_code, HTTPStatus.OK)
1018
+ self.assertJSONEqual(
1019
+ response.content,
1020
+ {"error": "Error retrieving fleet composition: Unexpected error"},
1021
+ )
1022
+
1023
+
1024
+ class TestAjaxFleetKickMemberView(FleetfinderTestViews):
1025
+ """
1026
+ Test the ajax_fleet_kick_member view in the Fleet Finder application.
1027
+ This view is responsible for kicking a member from a fleet.
1028
+ It should return a JSON response indicating success or failure.
1029
+ """
1030
+
1031
+ def test_returns_405_if_request_method_is_not_post(self):
1032
+ """
1033
+ Test that the ajax_fleet_kick_member view returns a 405 status code if the request method is not POST.
1034
+
1035
+ :return:
1036
+ :rtype:
1037
+ """
1038
+
1039
+ mock_request = MagicMock(method="GET", user=MagicMock())
1040
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1041
+
1042
+ self.assertEqual(response.status_code, HTTPStatus.METHOD_NOT_ALLOWED)
1043
+ self.assertEqual(
1044
+ json.loads(response.content),
1045
+ {"success": False, "error": "Method not allowed"},
1046
+ )
1047
+
1048
+ def test_returns_404_if_fleet_does_not_exist(self):
1049
+ """
1050
+ Test that the ajax_fleet_kick_member view returns a 404 status code if the fleet does not exist.
1051
+
1052
+ :return:
1053
+ :rtype:
1054
+ """
1055
+
1056
+ mock_request = MagicMock(method="POST", body=json.dumps({"memberId": 123}))
1057
+
1058
+ with patch(
1059
+ "fleetfinder.views.Fleet.objects.get", side_effect=Fleet.DoesNotExist
1060
+ ):
1061
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1062
+
1063
+ self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
1064
+ self.assertEqual(
1065
+ json.loads(response.content), {"success": False, "error": "Fleet not found"}
1066
+ )
1067
+
1068
+ def test_returns_400_if_member_id_is_missing(self):
1069
+ """
1070
+ Test that the ajax_fleet_kick_member view returns a 400 status code if the member ID is missing.
1071
+
1072
+ :return:
1073
+ :rtype:
1074
+ """
1075
+
1076
+ mock_request = MagicMock(method="POST", body=json.dumps({}))
1077
+
1078
+ with patch("fleetfinder.views.Fleet.objects.get") as mock_get:
1079
+ mock_get.return_value = MagicMock()
1080
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1081
+
1082
+ self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
1083
+ self.assertEqual(
1084
+ json.loads(response.content),
1085
+ {"success": False, "error": "Member ID required"},
1086
+ )
1087
+
1088
+ def test_returns_400_if_request_body_is_invalid(self):
1089
+ """
1090
+ Test that the ajax_fleet_kick_member view returns a 400 status code if the request body is invalid.
1091
+
1092
+ :return:
1093
+ :rtype:
1094
+ """
1095
+
1096
+ mock_request = MagicMock(method="POST", body="invalid_json")
1097
+
1098
+ with patch("fleetfinder.views.Fleet.objects.get") as mock_get:
1099
+ mock_get.return_value = MagicMock()
1100
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1101
+
1102
+ self.assertEqual(response.status_code, HTTPStatus.BAD_REQUEST)
1103
+ self.assertEqual(
1104
+ json.loads(response.content),
1105
+ {"success": False, "error": "Invalid request data"},
1106
+ )
1107
+
1108
+ def test_returns_404_if_member_not_found_in_fleet(self):
1109
+ """
1110
+ Test that the ajax_fleet_kick_member view returns a 404 status code if the member is not found in the fleet.
1111
+
1112
+ :return:
1113
+ :rtype:
1114
+ """
1115
+
1116
+ mock_request = MagicMock(method="POST", body=json.dumps({"memberId": 123}))
1117
+ esi_stub = SimpleNamespace(
1118
+ client=SimpleNamespace(
1119
+ Fleets=SimpleNamespace(
1120
+ DeleteFleetsFleetIdMembersMemberId=MagicMock(
1121
+ side_effect=HTTPClientError(404, {}, {})
1122
+ )
1123
+ )
1124
+ )
1125
+ )
1126
+
1127
+ with (
1128
+ patch("fleetfinder.views.Fleet.objects.get") as mock_get,
1129
+ patch("fleetfinder.views.esi", new=esi_stub),
1130
+ ):
1131
+ mock_get.return_value = MagicMock(fleet_commander=MagicMock(character_id=1))
1132
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1133
+
1134
+ self.assertEqual(response.status_code, HTTPStatus.NOT_FOUND)
1135
+ self.assertEqual(
1136
+ json.loads(response.content),
1137
+ {"success": False, "error": "Member not found in fleet"},
1138
+ )
1139
+
1140
+ def test_returns_esi_error_when_unexpected_http_error_occurs(self):
1141
+ mock_request = MagicMock(method="POST", body=json.dumps({"memberId": 123}))
1142
+ esi_stub = SimpleNamespace(
1143
+ client=SimpleNamespace(
1144
+ Fleets=SimpleNamespace(
1145
+ DeleteFleetsFleetIdMembersMemberId=MagicMock(
1146
+ side_effect=HTTPClientError(500, {}, {})
1147
+ )
1148
+ )
1149
+ )
1150
+ )
1151
+ with (
1152
+ patch("fleetfinder.views.Fleet.objects.get") as mock_get,
1153
+ patch("fleetfinder.views.esi", new=esi_stub),
1154
+ ):
1155
+ mock_get.return_value = MagicMock(fleet_commander=MagicMock(character_id=1))
1156
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1157
+ self.assertEqual(response.status_code, 500)
1158
+ self.assertEqual(
1159
+ json.loads(response.content),
1160
+ {
1161
+ "success": False,
1162
+ "error": "An ESI error occurred: <HTTPClientError 500 {} {}>",
1163
+ },
1164
+ )
1165
+
1166
+ def test_successfully_kicks_member_from_fleet(self):
1167
+ mock_request = MagicMock(method="POST", body=json.dumps({"memberId": 123}))
1168
+ esi_stub = SimpleNamespace(
1169
+ client=SimpleNamespace(
1170
+ Fleets=SimpleNamespace(
1171
+ DeleteFleetsFleetIdMembersMemberId=MagicMock(
1172
+ return_value=MagicMock()
1173
+ )
1174
+ )
1175
+ )
1176
+ )
1177
+ with (
1178
+ patch("fleetfinder.views.Fleet.objects.get") as mock_get,
1179
+ patch("fleetfinder.views.esi", new=esi_stub),
1180
+ ):
1181
+ mock_get.return_value = MagicMock(fleet_commander=MagicMock(character_id=1))
1182
+ response = ajax_fleet_kick_member(mock_request, fleet_id=1)
1183
+ self.assertEqual(response.status_code, HTTPStatus.OK)
1184
+ self.assertEqual(json.loads(response.content), {"success": True})