aa-structures 2.9.1__py3-none-any.whl → 2.11.0__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.
@@ -318,20 +318,21 @@ class TestOwnerFetchToken(NoSocketsTestCase):
318
318
  self.assertTrue(mock_notify.called)
319
319
  self.assertEqual(owner.characters.count(), 0)
320
320
 
321
- def test_raise_error_when_token_not_found_and_delete_character(
321
+ def test_raise_error_when_no_valid_token_found_and_disable_character(
322
322
  self, mock_notify_admins, mock_notify
323
323
  ):
324
324
  # given
325
- character = EveCharacterFactory()
326
- user = UserMainDefaultOwnerFactory(main_character__character=character)
327
- owner = OwnerFactory(user=user, characters=[character])
328
- user.token_set.first().scopes.clear()
325
+ eve_character = EveCharacterFactory()
326
+ user = UserMainDefaultOwnerFactory(main_character__character=eve_character)
327
+ owner = OwnerFactory(user=user, characters=[eve_character])
328
+ user.token_set.first().scopes.clear() # token no longer valid
329
329
  # when/then
330
330
  with self.assertRaises(TokenError):
331
331
  owner.fetch_token()
332
+ character = owner.characters.first()
333
+ self.assertFalse(character.is_enabled)
332
334
  self.assertTrue(mock_notify_admins.called)
333
335
  self.assertTrue(mock_notify.called)
334
- self.assertEqual(owner.characters.count(), 0)
335
336
 
336
337
  def test_raise_error_when_character_no_longer_a_corporation_member_and_delete_it(
337
338
  self, mock_notify_admins, mock_notify
@@ -351,37 +352,7 @@ class TestOwnerFetchToken(NoSocketsTestCase):
351
352
  self.assertTrue(mock_notify.called)
352
353
  self.assertEqual(owner.characters.count(), 0)
353
354
 
354
- def test_should_delete_invalid_characters_and_return_token_from_valid_char(
355
- self, mock_notify_admins, mock_notify
356
- ):
357
- # given
358
- character_1 = EveCharacterFactory()
359
- user = UserMainDefaultOwnerFactory(main_character__character=character_1)
360
- owner = OwnerFactory(
361
- user=user,
362
- characters=[character_1],
363
- characters__notifications_last_used_at=dt.datetime(
364
- 2021, 1, 1, 1, 2, tzinfo=utc
365
- ),
366
- )
367
- character_2 = EveCharacterFactory()
368
- OwnerCharacterFactory(
369
- owner=owner,
370
- eve_character=character_2,
371
- notifications_last_used_at=dt.datetime(2021, 1, 1, 1, 1, tzinfo=utc),
372
- )
373
-
374
- # when
375
- token = owner.fetch_token()
376
-
377
- # then
378
- self.assertIsInstance(token, Token)
379
- self.assertEqual(token.user, user)
380
- self.assertTrue(mock_notify_admins.called)
381
- self.assertTrue(mock_notify.called)
382
- self.assertEqual(owner.characters.count(), 1)
383
-
384
- def test_should_rotate_through_characters_for_notification(
355
+ def test_should_rotate_through_enabled_characters_for_notification(
385
356
  self, mock_notify_admins, mock_notify
386
357
  ):
387
358
  # given
@@ -398,6 +369,9 @@ class TestOwnerFetchToken(NoSocketsTestCase):
398
369
  owner=owner,
399
370
  notifications_last_used_at=dt.datetime(2021, 1, 1, 3, 0, tzinfo=utc),
400
371
  )
372
+ OwnerCharacterFactory(
373
+ owner=owner, is_enabled=False
374
+ ) # this one should be ignore
401
375
  tokens_received = []
402
376
 
403
377
  # when
@@ -493,6 +467,36 @@ class TestOwnerFetchToken(NoSocketsTestCase):
493
467
  ],
494
468
  )
495
469
 
470
+ def test_should_delete_invalid_characters_and_return_token_from_valid_char(
471
+ self, mock_notify_admins, mock_notify
472
+ ):
473
+ # given
474
+ character_1 = EveCharacterFactory()
475
+ user = UserMainDefaultOwnerFactory(main_character__character=character_1)
476
+ owner = OwnerFactory(
477
+ user=user,
478
+ characters=[character_1],
479
+ characters__notifications_last_used_at=dt.datetime(
480
+ 2021, 1, 1, 1, 2, tzinfo=utc
481
+ ),
482
+ )
483
+ character_2 = EveCharacterFactory() # invalid, because of different corporation
484
+ OwnerCharacterFactory(
485
+ owner=owner,
486
+ eve_character=character_2,
487
+ notifications_last_used_at=dt.datetime(2021, 1, 1, 1, 1, tzinfo=utc),
488
+ )
489
+
490
+ # when
491
+ token = owner.fetch_token()
492
+
493
+ # then
494
+ self.assertIsInstance(token, Token)
495
+ self.assertEqual(token.user, user)
496
+ self.assertTrue(mock_notify_admins.called)
497
+ self.assertTrue(mock_notify.called)
498
+ self.assertEqual(owner.characters.count(), 1)
499
+
496
500
 
497
501
  class TestOwnerCharacters(NoSocketsTestCase):
498
502
  @classmethod
@@ -500,12 +504,6 @@ class TestOwnerCharacters(NoSocketsTestCase):
500
504
  super().setUpClass()
501
505
  cls.owner = OwnerFactory()
502
506
 
503
- def test_should_return_str(self):
504
- # given
505
- character = OwnerCharacterFactory(owner=self.owner)
506
- # when/then
507
- self.assertTrue(str(character))
508
-
509
507
  def test_should_add_new_character(self):
510
508
  # given
511
509
  character = EveCharacterFactory(corporation=self.owner.corporation)
@@ -546,22 +544,35 @@ class TestOwnerCharacters(NoSocketsTestCase):
546
544
  with self.assertRaises(ValueError):
547
545
  self.owner.add_character(character_ownership)
548
546
 
549
- def test_should_count_characters(self):
547
+ def test_should_count_enabled_characters_only(self):
550
548
  # given
551
- OwnerCharacterFactory(owner=self.owner)
549
+ OwnerCharacterFactory(owner=self.owner, is_enabled=False)
552
550
  # when
553
- result = self.owner.characters_count()
551
+ result = self.owner.valid_characters_count()
554
552
  # then
555
- self.assertEqual(result, 2)
553
+ self.assertEqual(result, 1)
556
554
 
557
555
  def test_should_count_characters_when_empty(self):
558
556
  # given
559
557
  owner = OwnerFactory(characters=False)
560
558
  # when
561
- result = owner.characters_count()
559
+ result = owner.valid_characters_count()
562
560
  # then
563
561
  self.assertEqual(result, 0)
564
562
 
563
+ def test_should_reset_character_when_re_adding(self):
564
+ # given
565
+ character: OwnerCharacter = self.owner.characters.first()
566
+ character.is_enabled = False
567
+ character.disabled_reason = "some reason"
568
+ character.save()
569
+ # when
570
+ self.owner.add_character(character.character_ownership)
571
+ # then
572
+ character.refresh_from_db()
573
+ self.assertTrue(character.is_enabled)
574
+ self.assertFalse(character.disabled_reason)
575
+
565
576
 
566
577
  @patch(MODULE_PATH + ".notify", spec=True)
567
578
  @patch(MODULE_PATH + ".notify_admins", spec=True)
@@ -577,7 +588,7 @@ class TestOwnerDeleteCharacter(NoSocketsTestCase):
577
588
  user = character.character_ownership.user
578
589
 
579
590
  # when
580
- self.owner.delete_character(character=character, error="dummy error")
591
+ self.owner.delete_character(character=character, reason="dummy error")
581
592
 
582
593
  # then
583
594
  self.assertEqual(self.owner.characters.count(), 0)
@@ -591,18 +602,50 @@ class TestOwnerDeleteCharacter(NoSocketsTestCase):
591
602
  self.assertEqual(kwargs["user"], user)
592
603
  self.assertEqual(kwargs["level"], "warning")
593
604
 
594
- def test_should_not_delete_when_errors_are_allowed(
605
+
606
+ @patch(MODULE_PATH + ".notify", spec=True)
607
+ @patch(MODULE_PATH + ".notify_admins", spec=True)
608
+ class TestOwnerDisableCharacters(NoSocketsTestCase):
609
+ @classmethod
610
+ def setUpClass(cls):
611
+ super().setUpClass()
612
+ cls.owner = OwnerFactory(characters=False)
613
+
614
+ def test_should_disable_character_and_notify(self, mock_notify_admins, mock_notify):
615
+ # given
616
+ character = OwnerCharacterFactory(owner=self.owner)
617
+ user = character.character_ownership.user
618
+
619
+ # when
620
+ self.owner.disable_character(character=character, reason="dummy error")
621
+
622
+ # then
623
+ character.refresh_from_db()
624
+ self.assertFalse(character.is_enabled)
625
+ self.assertTrue(character.disabled_reason)
626
+ self.assertTrue(mock_notify_admins.called)
627
+ _, kwargs = mock_notify_admins.call_args
628
+ self.assertIn("dummy error", kwargs["message"])
629
+ self.assertEqual(kwargs["level"], "danger")
630
+ self.assertTrue(mock_notify.called)
631
+ _, kwargs = mock_notify.call_args
632
+ self.assertIn("dummy error", kwargs["message"])
633
+ self.assertEqual(kwargs["user"], user)
634
+ self.assertEqual(kwargs["level"], "warning")
635
+
636
+ def test_should_not_disable_when_error_counter_above_zero(
595
637
  self, mock_notify_admins, mock_notify
596
638
  ):
597
639
  # given
598
640
  character = OwnerCharacterFactory(owner=self.owner)
599
641
 
600
642
  # when
601
- self.owner.delete_character(
602
- character=character, error="dummy error", max_allowed_errors=1
643
+ self.owner.disable_character(
644
+ character=character, reason="dummy error", max_allowed_errors=1
603
645
  )
604
646
  # then
605
647
  character.refresh_from_db()
648
+ self.assertTrue(character.is_enabled)
606
649
  self.assertEqual(character.error_count, 1)
607
650
  self.assertFalse(mock_notify_admins.called)
608
651
  self.assertFalse(mock_notify.called)
@@ -7,7 +7,7 @@ from app_utils.esi_testing import EsiClientStub, EsiEndpoint
7
7
  from app_utils.testing import NoSocketsTestCase
8
8
 
9
9
  from structures.constants import EveCorporationId
10
- from structures.models import StarbaseDetail, Structure
10
+ from structures.models import OwnerCharacter, StarbaseDetail, Structure
11
11
  from structures.tests.testdata.factories import (
12
12
  EveEntityCorporationFactory,
13
13
  OwnerFactory,
@@ -353,12 +353,13 @@ class TestUpdateStarbasesEsi(NoSocketsTestCase):
353
353
  owner.refresh_from_db()
354
354
  self.assertFalse(owner.is_structure_sync_fresh)
355
355
  self.assertTrue(mock_notify)
356
- character = owner.characters.first()
356
+ character: OwnerCharacter = owner.characters.first()
357
357
  self.assertEqual(character.error_count, 1)
358
+ self.assertTrue(character.is_enabled)
358
359
 
359
360
  @patch(MODULE_PATH + ".STRUCTURES_ESI_DIRECTOR_ERROR_MAX_RETRIES", 3)
360
361
  @patch(MODULE_PATH + ".notify", spec=True)
361
- def test_should_remove_character_when_not_director_while_updating_starbases(
362
+ def test_should_disable_character_when_not_director_while_updating_starbases(
362
363
  self, mock_notify, mock_esi
363
364
  ):
364
365
  # given
@@ -369,7 +370,7 @@ class TestUpdateStarbasesEsi(NoSocketsTestCase):
369
370
  )
370
371
  mock_esi.client = self.esi_client_stub.replace_endpoints([new_endpoint])
371
372
  owner = OwnerFactory(user=self.user, structures_last_update_at=None)
372
- character = owner.characters.first()
373
+ character: OwnerCharacter = owner.characters.first()
373
374
  character.error_count = 3
374
375
  character.save()
375
376
  # when
@@ -378,7 +379,22 @@ class TestUpdateStarbasesEsi(NoSocketsTestCase):
378
379
  owner.refresh_from_db()
379
380
  self.assertFalse(owner.is_structure_sync_fresh)
380
381
  self.assertTrue(mock_notify)
381
- self.assertNotIn(character, owner.characters.all())
382
+ character.refresh_from_db()
383
+ self.assertFalse(character.is_enabled)
384
+
385
+ def test_should_reset_error_count_for_character_when_successful(self, mock_esi):
386
+ # given
387
+ mock_esi.client = self.esi_client_stub
388
+ owner = OwnerFactory(user=self.user, structures_last_update_at=None)
389
+ character: OwnerCharacter = owner.characters.first()
390
+ character.error_count = 3
391
+ character.save()
392
+ # when
393
+ owner.update_structures_esi()
394
+ # then
395
+ character.refresh_from_db()
396
+ self.assertTrue(character.is_enabled)
397
+ self.assertEqual(character.error_count, 0)
382
398
 
383
399
  def test_should_remove_old_starbases(self, mock_esi):
384
400
  # given
@@ -0,0 +1,31 @@
1
+ from app_utils.testing import NoSocketsTestCase
2
+
3
+ from structures.tests.testdata.factories import OwnerCharacterFactory, OwnerFactory
4
+
5
+ MODULE_PATH = "structures.models.owners"
6
+
7
+
8
+ class TestOwnerCharacter(NoSocketsTestCase):
9
+ @classmethod
10
+ def setUpClass(cls):
11
+ super().setUpClass()
12
+ cls.owner = OwnerFactory()
13
+
14
+ def test_should_return_str(self):
15
+ # given
16
+ character = OwnerCharacterFactory(owner=self.owner)
17
+ # when/then
18
+ self.assertTrue(str(character))
19
+
20
+ def test_can_reset_character(self):
21
+ # given
22
+ character = OwnerCharacterFactory(
23
+ owner=self.owner, is_enabled=False, disabled_reason="reason", error_count=42
24
+ )
25
+ # when
26
+ character.reset()
27
+ # then
28
+ character.refresh_from_db()
29
+ self.assertTrue(character.is_enabled)
30
+ self.assertFalse(character.disabled_reason)
31
+ self.assertFalse(character.error_count)
@@ -32,6 +32,7 @@ from .testdata.factories import (
32
32
  EveCorporationInfoFactory,
33
33
  FuelAlertConfigFactory,
34
34
  NotificationFactory,
35
+ OwnerCharacterFactory,
35
36
  OwnerFactory,
36
37
  PocoFactory,
37
38
  StarbaseFactory,
@@ -261,6 +262,7 @@ class TestNotificationAdmin(TestCase):
261
262
  )
262
263
  # when
263
264
  result = self.modeladmin._structures(obj)
265
+ # then
264
266
  self.assertIsNone(result)
265
267
 
266
268
  # FIXME: Does not seam to work with special prefetch list
@@ -356,6 +358,52 @@ class TestNotificationAdmin(TestCase):
356
358
  self.assertSetEqual(set(queryset), set(expected))
357
359
 
358
360
 
361
+ class TestNotificationAdminWebhooks(TestCase):
362
+ @classmethod
363
+ def setUpClass(cls):
364
+ super().setUpClass()
365
+ cls.factory = RequestFactory()
366
+ load_eveuniverse()
367
+ cls.modeladmin = NotificationAdmin(model=Notification, admin_site=AdminSite())
368
+ cls.user = SuperuserFactory()
369
+
370
+ def test_should_return_name_of_owner_webhook(self):
371
+ # given
372
+ owner = OwnerFactory(webhooks__name="Alpha")
373
+ obj = NotificationFactory(
374
+ owner=owner, notif_type=NotificationType.STRUCTURE_LOST_SHIELD
375
+ )
376
+ obj2 = self.modeladmin.get_queryset(MockRequest(user=self.user)).get(pk=obj.pk)
377
+ # when
378
+ result = self.modeladmin._webhooks(obj2)
379
+ # then
380
+ self.assertEqual("Alpha", result)
381
+
382
+ def test_should_report_missing_webhook(self):
383
+ # given
384
+ owner = OwnerFactory(webhooks=False)
385
+ obj = NotificationFactory(
386
+ owner=owner, notif_type=NotificationType.STRUCTURE_LOST_SHIELD
387
+ )
388
+ obj2 = self.modeladmin.get_queryset(MockRequest(user=self.user)).get(pk=obj.pk)
389
+ # when
390
+ result = self.modeladmin._webhooks(obj2)
391
+ # then
392
+ self.assertIn("Not configured", result)
393
+
394
+ def test_should_report_when_webhooks_not_configured_for_this_notif_type(self):
395
+ # given
396
+ owner = OwnerFactory()
397
+ obj = NotificationFactory(
398
+ owner=owner, notif_type=NotificationType.SOV_ENTOSIS_CAPTURE_STARTED
399
+ )
400
+ obj2 = self.modeladmin.get_queryset(MockRequest(user=self.user)).get(pk=obj.pk)
401
+ # when
402
+ result = self.modeladmin._webhooks(obj2)
403
+ # then
404
+ self.assertIn("Not configured", result)
405
+
406
+
359
407
  class TestOwnerAdmin(TestCase):
360
408
  @classmethod
361
409
  def setUpClass(cls):
@@ -420,6 +468,23 @@ class TestOwnerAdmin(TestCase):
420
468
  self.assertEqual(mock_task.delay.call_count, 1)
421
469
  self.assertTrue(mock_message_user.called)
422
470
 
471
+ @patch(MODULE_PATH + ".OwnerAdmin.message_user", spec=True)
472
+ def test_action_reset_characters(self, mock_message_user):
473
+ # given
474
+ owner_1 = OwnerFactory(characters=False)
475
+ character_1 = OwnerCharacterFactory(owner=owner_1, is_enabled=False)
476
+ owner_2 = OwnerFactory(characters=False)
477
+ character_2 = OwnerCharacterFactory(owner=owner_2, is_enabled=False)
478
+ # when
479
+ queryset = Owner.objects.filter(pk=owner_1.pk)
480
+ self.modeladmin.reset_characters(MockRequest(self.user), queryset)
481
+ # then
482
+ self.assertTrue(mock_message_user.called)
483
+ character_1.refresh_from_db()
484
+ self.assertTrue(character_1.is_enabled)
485
+ character_2.refresh_from_db()
486
+ self.assertFalse(character_2.is_enabled)
487
+
423
488
  def test_should_return_empty_turnaround_times(self):
424
489
  # given
425
490
  obj = OwnerFactory()
@@ -60,12 +60,13 @@ class TestOwnerManager(NoSocketsTestCase):
60
60
  def test_should_annotate_characters_count(self):
61
61
  # given
62
62
  owner = OwnerFactory() # 1st character automatically created
63
- OwnerCharacterFactory(owner=owner) # 2nd character added
63
+ OwnerCharacterFactory(owner=owner, is_enabled=False) # 2nd character added
64
64
  # when
65
65
  result = Owner.objects.annotate_characters_count()
66
66
  # then
67
67
  obj = result.get(pk=owner.pk)
68
- self.assertEqual(obj.characters_count_2, 2)
68
+ self.assertEqual(obj.characters_enabled_count, 1)
69
+ self.assertEqual(obj.characters_disabled_count, 1)
69
70
 
70
71
  def test_should_return_when_structures_where_last_updated_for_several_owners(self):
71
72
  # given
@@ -203,9 +203,7 @@ class OwnerFactory(factory.django.DjangoModelFactory, metaclass=BaseMetaFactory[
203
203
  def characters(
204
204
  obj, create: bool, extracted: Optional[List[EveCharacter]], **kwargs
205
205
  ):
206
- """
207
- Set extracted to False to skip creating characters.
208
- """
206
+ # Set characters=False to skip creating characters.
209
207
  if not create or extracted is False:
210
208
  return
211
209
 
@@ -222,7 +220,8 @@ class OwnerFactory(factory.django.DjangoModelFactory, metaclass=BaseMetaFactory[
222
220
 
223
221
  @factory.post_generation
224
222
  def webhooks(obj, create, extracted, **kwargs):
225
- if not create:
223
+ # Set webhooks=False to skip creating characters.
224
+ if not create or extracted is False:
226
225
  return
227
226
 
228
227
  if extracted:
@@ -0,0 +1,113 @@
1
+ import datetime as dt
2
+ from unittest.mock import patch
3
+
4
+ from django.test import TestCase
5
+ from django.urls import reverse
6
+ from django.utils.timezone import now
7
+
8
+ from app_utils.testing import response_text
9
+
10
+ from structures.tests.testdata.factories import OwnerFactory
11
+ from structures.views import status
12
+
13
+ OWNERS_PATH = "structures.models.owners"
14
+
15
+
16
+ @patch(OWNERS_PATH + ".STRUCTURES_STRUCTURE_SYNC_GRACE_MINUTES", 30)
17
+ @patch(OWNERS_PATH + ".STRUCTURES_NOTIFICATION_SYNC_GRACE_MINUTES", 30)
18
+ class TestServiceStatus(TestCase):
19
+ def test_should_report_ok(self):
20
+ # given
21
+ OwnerFactory(
22
+ structures_last_update_at=now(),
23
+ notifications_last_update_at=now(),
24
+ forwarding_last_update_at=now(),
25
+ assets_last_update_at=now(),
26
+ )
27
+ request = self.client.get(reverse("structures:service_status"))
28
+ # when
29
+ response = status.service_status(request)
30
+ # then
31
+ self.assertEqual(response.status_code, 200)
32
+ self.assertEqual(response_text(response), "service is up")
33
+
34
+ def test_should_report_ok_when_downed_owner_is_inactive(self):
35
+ # given
36
+ OwnerFactory(
37
+ structures_last_update_at=now(),
38
+ notifications_last_update_at=now(),
39
+ forwarding_last_update_at=now(),
40
+ assets_last_update_at=now(),
41
+ )
42
+ OwnerFactory(
43
+ structures_last_update_at=now() - dt.timedelta(minutes=31),
44
+ notifications_last_update_at=now(),
45
+ forwarding_last_update_at=now(),
46
+ assets_last_update_at=now(),
47
+ is_active=False,
48
+ )
49
+ request = self.client.get(reverse("structures:service_status"))
50
+ # when
51
+ response = status.service_status(request)
52
+ # then
53
+ self.assertEqual(response.status_code, 200)
54
+
55
+ def test_should_report_fail_when_issue_with_structures(self):
56
+ # given
57
+ OwnerFactory(
58
+ structures_last_update_at=now() - dt.timedelta(minutes=31),
59
+ notifications_last_update_at=now(),
60
+ forwarding_last_update_at=now(),
61
+ assets_last_update_at=now(),
62
+ )
63
+ request = self.client.get(reverse("structures:service_status"))
64
+ # when
65
+ response = status.service_status(request)
66
+ # then
67
+ self.assertEqual(response.status_code, 500)
68
+ self.assertEqual(response_text(response), "service is down")
69
+
70
+ def test_should_report_fail_when_issue_with_notifications(self):
71
+ # given
72
+ OwnerFactory(
73
+ structures_last_update_at=now(),
74
+ notifications_last_update_at=now() - dt.timedelta(minutes=31),
75
+ forwarding_last_update_at=now(),
76
+ assets_last_update_at=now(),
77
+ )
78
+ request = self.client.get(reverse("structures:service_status"))
79
+ # when
80
+ response = status.service_status(request)
81
+ # then
82
+ self.assertEqual(response.status_code, 500)
83
+ self.assertEqual(response_text(response), "service is down")
84
+
85
+ def test_should_report_fail_when_issue_with_forwarding(self):
86
+ # given
87
+ OwnerFactory(
88
+ structures_last_update_at=now(),
89
+ notifications_last_update_at=now(),
90
+ forwarding_last_update_at=now() - dt.timedelta(minutes=31),
91
+ assets_last_update_at=now(),
92
+ )
93
+ request = self.client.get(reverse("structures:service_status"))
94
+ # when
95
+ response = status.service_status(request)
96
+ # then
97
+ self.assertEqual(response.status_code, 500)
98
+ self.assertEqual(response_text(response), "service is down")
99
+
100
+ def test_should_report_fail_when_issue_with_assets(self):
101
+ # given
102
+ OwnerFactory(
103
+ structures_last_update_at=now(),
104
+ notifications_last_update_at=now(),
105
+ forwarding_last_update_at=now(),
106
+ assets_last_update_at=now() - dt.timedelta(minutes=31),
107
+ )
108
+ request = self.client.get(reverse("structures:service_status"))
109
+ # when
110
+ response = status.service_status(request)
111
+ # then
112
+ self.assertEqual(response.status_code, 500)
113
+ self.assertEqual(response_text(response), "service is down")