aa-intel-tool 2.10.1__py3-none-any.whl → 2.11.1__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 (92) hide show
  1. aa_intel_tool/__init__.py +1 -1
  2. aa_intel_tool/auth_hooks.py +2 -2
  3. aa_intel_tool/locale/cs_CZ/LC_MESSAGES/django.po +4 -4
  4. aa_intel_tool/locale/de/LC_MESSAGES/django.po +4 -4
  5. aa_intel_tool/locale/django.pot +5 -5
  6. aa_intel_tool/locale/es/LC_MESSAGES/django.po +4 -4
  7. aa_intel_tool/locale/fr_FR/LC_MESSAGES/django.po +4 -4
  8. aa_intel_tool/locale/it_IT/LC_MESSAGES/django.po +4 -4
  9. aa_intel_tool/locale/ja/LC_MESSAGES/django.mo +0 -0
  10. aa_intel_tool/locale/ja/LC_MESSAGES/django.po +35 -35
  11. aa_intel_tool/locale/ko_KR/LC_MESSAGES/django.po +4 -4
  12. aa_intel_tool/locale/nl_NL/LC_MESSAGES/django.po +4 -4
  13. aa_intel_tool/locale/pl_PL/LC_MESSAGES/django.po +4 -4
  14. aa_intel_tool/locale/ru/LC_MESSAGES/django.po +4 -4
  15. aa_intel_tool/locale/sk/LC_MESSAGES/django.po +4 -4
  16. aa_intel_tool/locale/uk/LC_MESSAGES/django.po +4 -4
  17. aa_intel_tool/locale/zh_Hans/LC_MESSAGES/django.po +4 -4
  18. aa_intel_tool/parser/module/dscan.py +4 -5
  19. aa_intel_tool/static/aa_intel_tool/css/aa-intel-tool.css +23 -26
  20. aa_intel_tool/static/aa_intel_tool/css/aa-intel-tool.min.css +1 -1
  21. aa_intel_tool/static/aa_intel_tool/css/aa-intel-tool.min.css.map +1 -1
  22. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-chatscan.js +169 -282
  23. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-chatscan.min.js +1 -1
  24. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-chatscan.min.js.map +1 -1
  25. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-dscan.js +251 -572
  26. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-dscan.min.js +1 -1
  27. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-dscan.min.js.map +1 -1
  28. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-fleetcomposition.js +163 -237
  29. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-fleetcomposition.min.js +1 -1
  30. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-fleetcomposition.min.js.map +1 -1
  31. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-scan-result-common.js +18 -0
  32. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-scan-result-common.min.js +1 -1
  33. aa_intel_tool/static/aa_intel_tool/javascript/aa-intel-tool-scan-result-common.min.js.map +1 -1
  34. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/dataTables.bootstrap5.css +610 -0
  35. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/dataTables.bootstrap5.js +123 -0
  36. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/dataTables.bootstrap5.min.css +8 -0
  37. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/dataTables.bootstrap5.min.css.map +1 -0
  38. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/dataTables.bootstrap5.min.js +6 -0
  39. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/dataTables.bootstrap5.min.js.map +1 -0
  40. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/datatables.min.js +10 -0
  41. aa_intel_tool/static/aa_intel_tool/libs/DataTables/2.3.4/datatables.min.js.map +1 -0
  42. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.css +516 -0
  43. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.min.css +2 -0
  44. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/css/columnControl.bootstrap5.min.css.map +1 -0
  45. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.js +73 -0
  46. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.min.js +6 -0
  47. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/js/columnControl.bootstrap5.min.js.map +1 -0
  48. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.js +3091 -0
  49. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.min.js +10 -0
  50. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/ColumnControl/1.1.1/js/dataTables.columnControl.min.js.map +1 -0
  51. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.css +20 -0
  52. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.min.css +2 -0
  53. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/css/fixedHeader.bootstrap5.min.css.map +1 -0
  54. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.js +1203 -0
  55. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.min.js +6 -0
  56. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/js/dataTables.fixedHeader.min.js.map +1 -0
  57. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.js +59 -0
  58. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.min.js +6 -0
  59. aa_intel_tool/static/aa_intel_tool/libs/DataTables/Extensions/FixedHeader/4.0.4/js/fixedHeader.bootstrap5.min.js.map +1 -0
  60. aa_intel_tool/templates/aa_intel_tool/base.html +30 -0
  61. aa_intel_tool/templates/aa_intel_tool/bundles/datatables-2-css.html +11 -0
  62. aa_intel_tool/templates/aa_intel_tool/bundles/datatables-2-js.html +14 -0
  63. aa_intel_tool/templates/aa_intel_tool/partials/scan/chatlist/alliances.html +1 -1
  64. aa_intel_tool/templates/aa_intel_tool/partials/scan/chatlist/corporations.html +1 -1
  65. aa_intel_tool/templates/aa_intel_tool/partials/scan/chatlist/pilots.html +2 -2
  66. aa_intel_tool/templates/aa_intel_tool/partials/scan/dscan/interesting-on-grid/items.html +1 -1
  67. aa_intel_tool/templates/aa_intel_tool/partials/scan/dscan/ships-breakdown/ship-classes.html +1 -1
  68. aa_intel_tool/templates/aa_intel_tool/partials/scan/dscan/ships-breakdown/ship-types.html +1 -1
  69. aa_intel_tool/templates/aa_intel_tool/partials/scan/fleetcomp/fleet-details/pilots.html +1 -1
  70. aa_intel_tool/templates/aa_intel_tool/views/scan/chatlist.html +2 -2
  71. aa_intel_tool/templates/aa_intel_tool/views/scan/dscan.html +2 -2
  72. aa_intel_tool/templates/aa_intel_tool/views/scan/fleetcomp.html +2 -2
  73. aa_intel_tool/tests/__init__.py +38 -0
  74. aa_intel_tool/tests/test_access.py +2 -2
  75. aa_intel_tool/tests/test_admin.py +4 -3
  76. aa_intel_tool/tests/test_app_settings.py +3 -2
  77. aa_intel_tool/tests/test_auth_hooks.py +2 -2
  78. aa_intel_tool/tests/test_helper_data_structures.py +2 -4
  79. aa_intel_tool/tests/test_helper_eve_character.py +5 -7
  80. aa_intel_tool/tests/test_models.py +3 -3
  81. aa_intel_tool/tests/test_parser_general.py +48 -34
  82. aa_intel_tool/tests/test_parser_helper_db.py +2 -4
  83. aa_intel_tool/tests/test_parser_module_chatlist.py +2 -2
  84. aa_intel_tool/tests/test_parser_module_dscan.py +828 -0
  85. aa_intel_tool/tests/test_parser_module_feetcomp.py +230 -0
  86. aa_intel_tool/tests/test_tasks.py +56 -0
  87. aa_intel_tool/tests/test_views_ajax.py +72 -0
  88. aa_intel_tool/tests/test_views_general.py +240 -0
  89. {aa_intel_tool-2.10.1.dist-info → aa_intel_tool-2.11.1.dist-info}/METADATA +1 -1
  90. {aa_intel_tool-2.10.1.dist-info → aa_intel_tool-2.11.1.dist-info}/RECORD +92 -59
  91. {aa_intel_tool-2.10.1.dist-info → aa_intel_tool-2.11.1.dist-info}/WHEEL +0 -0
  92. {aa_intel_tool-2.10.1.dist-info → aa_intel_tool-2.11.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,230 @@
1
+ # Standard Library
2
+ from unittest.mock import MagicMock, patch
3
+
4
+ # AA Intel Tool
5
+ from aa_intel_tool.exceptions import ParserError
6
+ from aa_intel_tool.parser.module.fleetcomp import get_fleet_composition, parse
7
+ from aa_intel_tool.tests import BaseTestCase
8
+
9
+
10
+ class TestParse(BaseTestCase):
11
+ def test_raises_error_when_fleetcomp_module_is_disabled(self):
12
+ """
13
+ Test that ParserError is raised when the fleet composition module is disabled.
14
+
15
+ :return:
16
+ :rtype:
17
+ """
18
+
19
+ with patch(
20
+ "aa_intel_tool.app_settings.AppSettings.INTELTOOL_ENABLE_MODULE_FLEETCOMP",
21
+ False,
22
+ ):
23
+ with self.assertRaises(ParserError):
24
+ parse(scan_data=[])
25
+
26
+ def test_returns_empty_data_for_empty_scan(self):
27
+ """
28
+ Test that an empty scan returns empty parsed data.
29
+
30
+ :return:
31
+ :rtype:
32
+ """
33
+
34
+ with patch(
35
+ "aa_intel_tool.app_settings.AppSettings.INTELTOOL_ENABLE_MODULE_FLEETCOMP",
36
+ True,
37
+ ):
38
+ with patch(
39
+ "aa_intel_tool.parser.module.fleetcomp.safe_scan_to_db"
40
+ ) as mock_safe_scan:
41
+ mock_safe_scan.return_value = {}
42
+
43
+ result = parse(scan_data=[])
44
+
45
+ self.assertEqual(result, {})
46
+
47
+ def test_parses_valid_scan_data_correctly(self):
48
+ """
49
+ Test that valid scan data is parsed correctly.
50
+
51
+ :return:
52
+ :rtype:
53
+ """
54
+
55
+ scan_data = [
56
+ "Rounon Dax\tJita\tOmen\tCruiser\tFleet Commander (Boss)\t5 - 5 - 5",
57
+ "Arodem Artemis\tPerimeter\tLeopard\tShuttle\tSquad Member\t0 - 0 - 5\tWing 1 / Squad 1",
58
+ ]
59
+ with patch(
60
+ "aa_intel_tool.app_settings.AppSettings.INTELTOOL_ENABLE_MODULE_FLEETCOMP",
61
+ True,
62
+ ):
63
+ with patch(
64
+ "aa_intel_tool.parser.module.fleetcomp.EveEntity.objects.fetch_by_names_esi"
65
+ ) as mock_fetch:
66
+ mock_qs = MagicMock()
67
+ mock_qs.filter.return_value = mock_qs
68
+ mock_qs.__iter__.return_value = iter([])
69
+ mock_fetch.return_value = mock_qs
70
+ with patch(
71
+ "aa_intel_tool.parser.module.fleetcomp.safe_scan_to_db"
72
+ ) as mock_safe_scan:
73
+ mock_safe_scan.return_value = {"parsed": "data"}
74
+ result = parse(scan_data=scan_data)
75
+ self.assertEqual(result, {"parsed": "data"})
76
+
77
+ def tst_handles_duplicate_pilot_entries_in_scan(self):
78
+ scan_data = [
79
+ "Arodem Artemis\tPerimeter\tLeopard\tShuttle\tSquad Member\t0 - 0 - 5\tWing 1 / Squad 1",
80
+ "Arodem Artemis\tPerimeter\tLeopard\tShuttle\tSquad Member\t0 - 0 - 5\tWing 1 / Squad 1",
81
+ ]
82
+ with patch(
83
+ "aa_intel_tool.app_settings.AppSettings.INTELTOOL_ENABLE_MODULE_FLEETCOMP",
84
+ True,
85
+ ):
86
+ with patch(
87
+ "aa_intel_tool.parser.module.fleetcomp.safe_scan_to_db"
88
+ ) as mock_safe_scan:
89
+ mock_safe_scan.return_value = {"parsed": "data"}
90
+ result = parse(scan_data=scan_data)
91
+ self.assertEqual(result, {"parsed": "data"})
92
+
93
+
94
+ class TestGetFleetComposition(BaseTestCase):
95
+ """
96
+ Testing the get_fleet_composition function
97
+ """
98
+
99
+ def test_returns_correct_fleet_composition_for_valid_data(self):
100
+ """
101
+ Test that the fleet composition is returned correctly for valid data.
102
+
103
+ :return:
104
+ :rtype:
105
+ """
106
+
107
+ pilots = {
108
+ "Pilot1": {"ship": "Omen"},
109
+ "Pilot2": {"ship": "Zealot"},
110
+ }
111
+ ships = {
112
+ "class": {"Omen": {"count": 1}, "Zealot": {"count": 1}},
113
+ "type": {"Cruiser": {"count": 1}, "Heavy Assault Cruiser": {"count": 1}},
114
+ }
115
+
116
+ ship1 = MagicMock()
117
+ ship1.id = 1
118
+ ship1.name = "Omen"
119
+ ship1.eve_group__id = 10
120
+ ship1.eve_group__name = "Cruiser"
121
+ ship1.mass = 1000
122
+
123
+ ship2 = MagicMock()
124
+ ship2.id = 2
125
+ ship2.name = "Zealot"
126
+ ship2.eve_group__id = 20
127
+ ship2.eve_group__name = "Heavy Assault Cruiser"
128
+ ship2.mass = 500
129
+
130
+ with (
131
+ patch(
132
+ "aa_intel_tool.parser.module.fleetcomp.EveEntity.objects.fetch_by_names_esi"
133
+ ) as mock_fetch,
134
+ patch(
135
+ "aa_intel_tool.parser.module.fleetcomp.EveType.objects.bulk_get_or_create_esi"
136
+ ) as mock_bulk,
137
+ patch(
138
+ "aa_intel_tool.parser.module.fleetcomp._get_character_info"
139
+ ) as mock_get_character_info,
140
+ ):
141
+ mock_fetch.return_value.filter.return_value.values_list.return_value = [
142
+ 1,
143
+ 2,
144
+ ]
145
+ mock_bulk.return_value.values_list.return_value = [ship1, ship2]
146
+ mock_get_character_info.return_value = [
147
+ MagicMock(
148
+ character_name="Pilot1",
149
+ character_id=1001,
150
+ portrait_url_32="portrait1.png",
151
+ ),
152
+ MagicMock(
153
+ character_name="Pilot2",
154
+ character_id=1002,
155
+ portrait_url_32="portrait2.png",
156
+ ),
157
+ ]
158
+
159
+ result = get_fleet_composition(pilots=pilots, ships=ships)
160
+
161
+ self.assertEqual(len(result["classes"]), 2)
162
+ self.assertEqual(len(result["types"]), 2)
163
+ self.assertEqual(len(result["pilots"]), 2)
164
+
165
+ def test_handles_empty_pilots_and_ships(self):
166
+ """
167
+ Test that empty pilots and ships return empty fleet composition.
168
+ :return:
169
+ :rtype:
170
+ """
171
+
172
+ pilots = {}
173
+ ships = {"class": {}, "type": {}}
174
+
175
+ with (
176
+ patch(
177
+ "aa_intel_tool.parser.module.fleetcomp.EveEntity.objects.fetch_by_names_esi"
178
+ ) as mock_fetch,
179
+ patch(
180
+ "aa_intel_tool.parser.module.fleetcomp.EveType.objects.bulk_get_or_create_esi"
181
+ ) as mock_bulk,
182
+ patch(
183
+ "aa_intel_tool.parser.module.fleetcomp._get_character_info"
184
+ ) as mock_get_character_info,
185
+ ):
186
+ mock_fetch.return_value.filter.return_value.values_list.return_value = []
187
+ mock_bulk.return_value.values_list.return_value = []
188
+ mock_get_character_info.return_value = []
189
+
190
+ result = get_fleet_composition(pilots=pilots, ships=ships)
191
+
192
+ self.assertEqual(result["classes"], [])
193
+ self.assertEqual(result["types"], [])
194
+ self.assertEqual(result["pilots"], [])
195
+
196
+ def test_raises_error_for_missing_ship_class_in_pilots(self):
197
+ """
198
+ Test that an error is raised when a ship class in pilots is missing.
199
+
200
+ :return:
201
+ :rtype:
202
+ """
203
+
204
+ pilots = {"Pilot1": {"ship": "Unknown"}}
205
+ ships = {"class": {}, "type": {}}
206
+ mock_ship_class_details = []
207
+
208
+ with (
209
+ patch(
210
+ "aa_intel_tool.parser.module.fleetcomp.EveEntity.objects.fetch_by_names_esi"
211
+ ) as mock_fetch,
212
+ patch(
213
+ "aa_intel_tool.parser.module.fleetcomp.EveType.objects.bulk_get_or_create_esi"
214
+ ) as mock_bulk,
215
+ patch(
216
+ "aa_intel_tool.parser.module.fleetcomp._get_character_info"
217
+ ) as mock_get_character_info,
218
+ ):
219
+ mock_fetch.return_value.filter.return_value.values_list.return_value = []
220
+ mock_bulk.return_value.values_list.return_value = mock_ship_class_details
221
+ mock_get_character_info.return_value = [
222
+ MagicMock(
223
+ character_name="Pilot1",
224
+ character_id=1001,
225
+ portrait_url_32="portrait1.png",
226
+ )
227
+ ]
228
+
229
+ with self.assertRaises(StopIteration):
230
+ get_fleet_composition(pilots=pilots, ships=ships)
@@ -0,0 +1,56 @@
1
+ """
2
+ Unit tests for the tasks in aa_intel_tool.
3
+ """
4
+
5
+ # Standard Library
6
+ from datetime import timedelta
7
+ from unittest.mock import patch
8
+
9
+ # Django
10
+ from django.utils.timezone import now
11
+
12
+ # AA Intel Tool
13
+ from aa_intel_tool.tasks import housekeeping
14
+ from aa_intel_tool.tests import BaseTestCase
15
+
16
+
17
+ class TestHousekeeping(BaseTestCase):
18
+ """
19
+ Tests for the housekeeping task
20
+ """
21
+
22
+ def test_removes_scans_older_than_retention_time(self):
23
+ """
24
+ Test that scans older than the retention time are removed.
25
+
26
+ :return:
27
+ :rtype:
28
+ """
29
+
30
+ with (
31
+ patch("aa_intel_tool.tasks.AppSettings.INTELTOOL_SCAN_RETENTION_TIME", 7),
32
+ patch("aa_intel_tool.tasks.Scan.objects.filter") as mock_filter,
33
+ ):
34
+ housekeeping()
35
+ mock_filter.assert_called_once()
36
+ called_kwargs = mock_filter.call_args.kwargs
37
+ created_lte = called_kwargs.get("created__lte")
38
+ expected = now() - timedelta(days=7)
39
+
40
+ self.assertTrue(abs(created_lte - expected) < timedelta(seconds=1))
41
+ mock_filter.return_value.delete.assert_called_once()
42
+
43
+ def test_does_not_remove_scans_when_retention_time_is_zero(self):
44
+ """
45
+ Test that no scans are removed when retention time is set to zero.
46
+
47
+ :return:
48
+ :rtype:
49
+ """
50
+
51
+ with (
52
+ patch("aa_intel_tool.tasks.AppSettings.INTELTOOL_SCAN_RETENTION_TIME", 0),
53
+ patch("aa_intel_tool.tasks.Scan.objects.filter") as mock_filter,
54
+ ):
55
+ housekeeping()
56
+ mock_filter.assert_not_called()
@@ -0,0 +1,72 @@
1
+ """
2
+ Tests for the AJAX views in aa_intel_tool.views.ajax.
3
+ """
4
+
5
+ # Standard Library
6
+ from http import HTTPStatus
7
+ from unittest.mock import Mock, patch
8
+
9
+ # Django
10
+ from django.test import RequestFactory
11
+ from django.urls import reverse
12
+
13
+ # AA Intel Tool
14
+ from aa_intel_tool.models import ScanData
15
+ from aa_intel_tool.tests import BaseTestCase
16
+ from aa_intel_tool.views.ajax import get_scan_data
17
+
18
+
19
+ class TestGetScanData(BaseTestCase):
20
+ """
21
+ Tests for the get_scan_data AJAX view
22
+ """
23
+
24
+ def test_returns_processed_data_for_valid_scan_hash_and_section(self):
25
+ """
26
+ Testing return of processed data for valid scan hash and section
27
+
28
+ :return:
29
+ :rtype:
30
+ """
31
+
32
+ rf = RequestFactory()
33
+ request = rf.get(
34
+ reverse(
35
+ "aa_intel_tool:ajax_get_scan_data",
36
+ kwargs={"scan_hash": "valid-hash", "scan_section": "valid-section"},
37
+ )
38
+ )
39
+
40
+ with patch("aa_intel_tool.views.ajax.ScanData.objects.filter") as mock_filter:
41
+ mock_scan_data = Mock()
42
+ mock_scan_data.processed_data = {"key": "value"}
43
+ mock_filter.return_value.get.return_value = mock_scan_data
44
+
45
+ response = get_scan_data(request, "valid-hash", "valid-section")
46
+
47
+ self.assertEqual(response.status_code, HTTPStatus.OK)
48
+ self.assertJSONEqual(response.content, {"key": "value"})
49
+
50
+ def test_returns_empty_dict_when_scan_data_does_not_exist(self):
51
+ """
52
+ Testing return of empty dict when scan data does not exist
53
+
54
+ :return:
55
+ :rtype:
56
+ """
57
+
58
+ rf = RequestFactory()
59
+ request = rf.get(
60
+ reverse(
61
+ "aa_intel_tool:ajax_get_scan_data",
62
+ kwargs={"scan_hash": "invalid-hash", "scan_section": "invalid-section"},
63
+ )
64
+ )
65
+
66
+ with patch("aa_intel_tool.views.ajax.ScanData.objects.filter") as mock_filter:
67
+ mock_filter.return_value.get.side_effect = ScanData.DoesNotExist
68
+
69
+ response = get_scan_data(request, "invalid-hash", "invalid-section")
70
+
71
+ self.assertEqual(response.status_code, HTTPStatus.OK)
72
+ self.assertJSONEqual(response.content, {})
@@ -0,0 +1,240 @@
1
+ """
2
+ Tests for the general views of the AA Intel Tool application.
3
+ """
4
+
5
+ # Standard Library
6
+ from http import HTTPStatus
7
+ from unittest.mock import Mock, patch
8
+
9
+ # Django
10
+ from django.contrib.messages import get_messages
11
+ from django.contrib.messages.storage.fallback import FallbackStorage
12
+ from django.contrib.sessions.middleware import SessionMiddleware
13
+ from django.test import RequestFactory
14
+
15
+ # AA Intel Tool
16
+ from aa_intel_tool.exceptions import ParserError
17
+ from aa_intel_tool.models import Scan
18
+ from aa_intel_tool.tests import BaseTestCase
19
+ from aa_intel_tool.views import general as general_view
20
+
21
+
22
+ class TestViewIndex(BaseTestCase):
23
+ """
24
+ Tests for the index view
25
+ """
26
+
27
+ def test_redirects_to_scan_view_on_valid_form_submission_direct_call(self):
28
+ """
29
+ Testing redirection to scan view on valid form submission
30
+
31
+ :return:
32
+ :rtype:
33
+ """
34
+
35
+ form_data = {"eve_intel": "valid intel data"}
36
+ rf = RequestFactory()
37
+ request = rf.post("/intel/", data=form_data)
38
+
39
+ with (
40
+ patch("aa_intel_tool.views.general.IntelForm") as mock_form,
41
+ patch("aa_intel_tool.views.general.parse_intel") as mock_parse,
42
+ ):
43
+ mock_form.return_value.is_valid.return_value = True
44
+ mock_form.return_value.cleaned_data = form_data
45
+ mock_parse.return_value = "valid_scan_hash"
46
+
47
+ response = general_view.index(request)
48
+
49
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
50
+ self.assertEqual(response["Location"], "/intel/scan/valid_scan_hash/")
51
+
52
+ def test_shows_error_message_on_parser_error_direct_call(self):
53
+ """
54
+ Testing error message on parser error during form submission
55
+
56
+ :return:
57
+ :rtype:
58
+ """
59
+
60
+ form_data = {"eve_intel": "invalid intel data"}
61
+ rf = RequestFactory()
62
+ request = rf.post("/intel/", data=form_data)
63
+
64
+ middleware = SessionMiddleware(lambda req: None)
65
+ middleware.process_request(request)
66
+ request.session.save()
67
+ request._messages = FallbackStorage(request)
68
+
69
+ with (
70
+ patch("aa_intel_tool.views.general.IntelForm") as mock_form,
71
+ patch("aa_intel_tool.views.general.parse_intel") as mock_parse,
72
+ ):
73
+ mock_form.return_value.is_valid.return_value = True
74
+ mock_form.return_value.cleaned_data = form_data
75
+ mock_parse.side_effect = ParserError("Parsing failed")
76
+
77
+ response = general_view.index(request)
78
+
79
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
80
+
81
+ messages = [str(m) for m in get_messages(request)]
82
+
83
+ # ensure the general parse-failure message is present
84
+ self.assertTrue(
85
+ any("The provided data could not be parsed." in m for m in messages),
86
+ f"messages were: {messages}",
87
+ )
88
+ # ensure the exception detail is included somewhere
89
+ self.assertTrue(
90
+ any("Parsing failed" in m for m in messages),
91
+ f"messages were: {messages}",
92
+ )
93
+
94
+ def test_shows_error_message_on_unexpected_exception_direct_call(self):
95
+ """
96
+ Testing error message on unexpected exception during form submission
97
+
98
+ :return:
99
+ :rtype:
100
+ """
101
+
102
+ form_data = {"eve_intel": "unexpected error data"}
103
+ rf = RequestFactory()
104
+ request = rf.post("/intel/", data=form_data)
105
+
106
+ middleware = SessionMiddleware(lambda req: None)
107
+ middleware.process_request(request)
108
+ request.session.save()
109
+ request._messages = FallbackStorage(request)
110
+
111
+ with (
112
+ patch("aa_intel_tool.views.general.IntelForm") as mock_form,
113
+ patch("aa_intel_tool.views.general.parse_intel") as mock_parse,
114
+ ):
115
+ mock_form.return_value.is_valid.return_value = True
116
+ mock_form.return_value.cleaned_data = form_data
117
+ mock_parse.side_effect = Exception("Unexpected error")
118
+
119
+ response = general_view.index(request)
120
+
121
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
122
+
123
+ messages = [str(m) for m in get_messages(request)]
124
+
125
+ self.assertTrue(
126
+ any(
127
+ "Something unexpected happened" in m or "(System Error)" in m
128
+ for m in messages
129
+ ),
130
+ f"messages were: {messages}",
131
+ )
132
+ self.assertTrue(
133
+ any("Unexpected error" in m for m in messages),
134
+ f"messages were: {messages}",
135
+ )
136
+
137
+ def test_renders_blank_form_on_get_request_direct_call(self):
138
+ """
139
+ Testing rendering of blank form on GET request
140
+
141
+ :return:
142
+ :rtype:
143
+ """
144
+
145
+ rf = RequestFactory()
146
+ request = rf.get("/intel/")
147
+
148
+ response = general_view.index(request)
149
+
150
+ self.assertEqual(response.status_code, HTTPStatus.OK)
151
+ self.assertIn("<form", response.content.decode("utf-8"))
152
+
153
+
154
+ class TestViewScan(BaseTestCase):
155
+ """
156
+ Tests for the scan view
157
+ """
158
+
159
+ def test_shows_error_message_when_scan_does_not_exist(self):
160
+ """
161
+ Testing error message when scan does not exist
162
+
163
+ :return:
164
+ :rtype:
165
+ """
166
+
167
+ rf = RequestFactory()
168
+ request = rf.get("/intel/scan/nonexistent-scan-hash/")
169
+
170
+ middleware = SessionMiddleware(lambda req: None)
171
+ middleware.process_request(request)
172
+ request.session.save()
173
+
174
+ request._messages = FallbackStorage(request)
175
+ with patch("aa_intel_tool.views.general.Scan.objects.exclude") as mock_exclude:
176
+ mock_exclude.return_value.get.side_effect = Scan.DoesNotExist
177
+
178
+ response = general_view.scan(request, scan_hash="nonexistent-scan-hash")
179
+
180
+ self.assertEqual(response.status_code, HTTPStatus.FOUND)
181
+
182
+ messages = [str(m) for m in get_messages(request)]
183
+
184
+ self.assertTrue(
185
+ any(
186
+ "The scan you were looking for could not be found." in m
187
+ for m in messages
188
+ ),
189
+ f"messages were: {messages}",
190
+ )
191
+
192
+ def test_renders_correct_template_for_supported_scan_type(self):
193
+ """
194
+ Testing rendering of correct template for supported scan type
195
+
196
+ :return:
197
+ :rtype:
198
+ """
199
+
200
+ rf = RequestFactory()
201
+ request = rf.get("/intel/scan/supported-scan-hash/")
202
+
203
+ middleware = SessionMiddleware(lambda req: None)
204
+ middleware.process_request(request)
205
+ request.session.save()
206
+ request._messages = FallbackStorage(request)
207
+
208
+ with patch("aa_intel_tool.views.general.Scan.objects.exclude") as mock_exclude:
209
+ mock_scan = Mock()
210
+ mock_scan.scan_type = "supported_type"
211
+ mock_scan.created = "2023-01-01"
212
+ mock_scan.raw_data = "raw intel data"
213
+ mock_exclude.return_value.get.return_value = mock_scan
214
+
215
+ # dummy response that mimics a rendered template response
216
+ dummy_response = Mock()
217
+ dummy_response.status_code = HTTPStatus.OK
218
+ dummy_response.templates = [
219
+ type("T", (), {"name": "supported_template.html"})()
220
+ ]
221
+
222
+ with (
223
+ patch(
224
+ "aa_intel_tool.views.general.SUPPORTED_INTEL_TYPES",
225
+ {
226
+ "supported_type": {
227
+ "name": "Supported Type",
228
+ "template": "supported_template.html",
229
+ }
230
+ },
231
+ ),
232
+ patch(
233
+ "aa_intel_tool.views.general.render", return_value=dummy_response
234
+ ) as mock_render,
235
+ ):
236
+ response = general_view.scan(request, scan_hash="supported-scan-hash")
237
+
238
+ self.assertEqual(response.status_code, HTTPStatus.OK)
239
+ self.assertTemplateUsed(response, "supported_template.html")
240
+ mock_render.assert_called_once()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aa-intel-tool
3
- Version: 2.10.1
3
+ Version: 2.11.1
4
4
  Summary: A simple parser for D-Scans and more for Alliance Auth
5
5
  Project-URL: Changelog, https://github.com/ppfeufer/aa-intel-tool/blob/master/CHANGELOG.md
6
6
  Project-URL: Codecov, https://codecov.io/gh/ppfeufer/aa-intel-tool