arthexis 0.1.26__py3-none-any.whl → 0.1.28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of arthexis might be problematic. Click here for more details.
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/METADATA +16 -11
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/RECORD +39 -38
- config/settings.py +7 -1
- config/settings_helpers.py +176 -1
- config/urls.py +18 -2
- core/admin.py +265 -23
- core/apps.py +6 -2
- core/celery_utils.py +73 -0
- core/models.py +307 -63
- core/system.py +17 -2
- core/tasks.py +304 -129
- core/test_system_info.py +43 -5
- core/tests.py +202 -2
- core/user_data.py +52 -19
- core/views.py +70 -3
- nodes/admin.py +348 -3
- nodes/apps.py +1 -1
- nodes/feature_checks.py +30 -0
- nodes/models.py +146 -18
- nodes/tasks.py +1 -1
- nodes/tests.py +181 -48
- nodes/views.py +148 -3
- ocpp/admin.py +1001 -10
- ocpp/consumers.py +572 -7
- ocpp/models.py +499 -33
- ocpp/store.py +406 -40
- ocpp/tasks.py +109 -145
- ocpp/test_rfid.py +73 -2
- ocpp/tests.py +982 -90
- ocpp/urls.py +5 -0
- ocpp/views.py +172 -70
- pages/context_processors.py +2 -0
- pages/models.py +9 -0
- pages/tests.py +166 -18
- pages/urls.py +1 -0
- pages/views.py +66 -3
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/WHEEL +0 -0
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/licenses/LICENSE +0 -0
- {arthexis-0.1.26.dist-info → arthexis-0.1.28.dist-info}/top_level.txt +0 -0
nodes/tests.py
CHANGED
|
@@ -43,9 +43,14 @@ from django.test import (
|
|
|
43
43
|
from django.urls import reverse
|
|
44
44
|
from django.contrib.auth import get_user_model
|
|
45
45
|
from django.contrib import admin
|
|
46
|
+
from django.contrib import messages
|
|
46
47
|
from django.contrib.admin import helpers
|
|
47
48
|
from django.contrib.auth.models import Permission
|
|
48
49
|
from django_celery_beat.models import IntervalSchedule, PeriodicTask
|
|
50
|
+
from core.celery_utils import (
|
|
51
|
+
periodic_task_name_variants,
|
|
52
|
+
slugify_task_name,
|
|
53
|
+
)
|
|
49
54
|
from django.conf import settings
|
|
50
55
|
from django.utils import timezone
|
|
51
56
|
from urllib.parse import urlparse
|
|
@@ -54,6 +59,7 @@ from . import dns as dns_utils
|
|
|
54
59
|
from selenium.common.exceptions import WebDriverException
|
|
55
60
|
from .classifiers import run_default_classifiers
|
|
56
61
|
from .utils import capture_rpi_snapshot, capture_screenshot, save_screenshot
|
|
62
|
+
from .feature_checks import feature_checks
|
|
57
63
|
from django.db.utils import DatabaseError
|
|
58
64
|
|
|
59
65
|
from .admin import NodeAdmin
|
|
@@ -90,7 +96,7 @@ class NodeBadgeColorTests(TestCase):
|
|
|
90
96
|
node = Node.objects.create(
|
|
91
97
|
hostname="watchtower",
|
|
92
98
|
address="10.1.0.1",
|
|
93
|
-
port=
|
|
99
|
+
port=8888,
|
|
94
100
|
mac_address="00:aa:bb:cc:dd:01",
|
|
95
101
|
role=self.watchtower,
|
|
96
102
|
)
|
|
@@ -127,6 +133,23 @@ class NodeTests(TestCase):
|
|
|
127
133
|
NodeRole.objects.get_or_create(name="Terminal")
|
|
128
134
|
NodeRole.objects.get_or_create(name="Interface")
|
|
129
135
|
|
|
136
|
+
def test_terminal_role_enables_clipboard_feature_by_default(self):
|
|
137
|
+
role = NodeRole.objects.get(name="Terminal")
|
|
138
|
+
feature, _ = NodeFeature.objects.get_or_create(
|
|
139
|
+
slug="clipboard-poll", defaults={"display": "Clipboard Poll"}
|
|
140
|
+
)
|
|
141
|
+
feature.roles.add(role)
|
|
142
|
+
|
|
143
|
+
node = Node.objects.create(
|
|
144
|
+
hostname="terminal-node",
|
|
145
|
+
address="10.0.0.5",
|
|
146
|
+
port=8888,
|
|
147
|
+
mac_address="aa:bb:cc:dd:ee:ff",
|
|
148
|
+
role=role,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
self.assertTrue(node.has_feature("clipboard-poll"))
|
|
152
|
+
|
|
130
153
|
|
|
131
154
|
class NodeGetLocalDatabaseUnavailableTests(SimpleTestCase):
|
|
132
155
|
def test_get_local_handles_database_errors(self):
|
|
@@ -317,7 +340,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
317
340
|
data={
|
|
318
341
|
"hostname": "local",
|
|
319
342
|
"address": "127.0.0.1",
|
|
320
|
-
"port":
|
|
343
|
+
"port": 8888,
|
|
321
344
|
"mac_address": "00:11:22:33:44:55",
|
|
322
345
|
},
|
|
323
346
|
content_type="application/json",
|
|
@@ -437,7 +460,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
437
460
|
hostname="best-ip",
|
|
438
461
|
address="gateway.local",
|
|
439
462
|
ipv4_address="198.51.100.5",
|
|
440
|
-
port=
|
|
463
|
+
port=8888,
|
|
441
464
|
mac_address="00:11:22:33:44:77",
|
|
442
465
|
)
|
|
443
466
|
self.assertEqual(node.get_best_ip(), "198.51.100.5")
|
|
@@ -502,7 +525,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
502
525
|
data={
|
|
503
526
|
"hostname": "lcd",
|
|
504
527
|
"address": "127.0.0.1",
|
|
505
|
-
"port":
|
|
528
|
+
"port": 8888,
|
|
506
529
|
"mac_address": "00:aa:bb:cc:dd:ee",
|
|
507
530
|
"features": ["clipboard-poll"],
|
|
508
531
|
},
|
|
@@ -517,7 +540,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
517
540
|
data={
|
|
518
541
|
"hostname": "lcd",
|
|
519
542
|
"address": "127.0.0.1",
|
|
520
|
-
"port":
|
|
543
|
+
"port": 8888,
|
|
521
544
|
"mac_address": "00:aa:bb:cc:dd:ee",
|
|
522
545
|
"features": [],
|
|
523
546
|
},
|
|
@@ -684,7 +707,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
684
707
|
payload = {
|
|
685
708
|
"hostname": "cors",
|
|
686
709
|
"address": "127.0.0.1",
|
|
687
|
-
"port":
|
|
710
|
+
"port": 8888,
|
|
688
711
|
"mac_address": "10:20:30:40:50:60",
|
|
689
712
|
}
|
|
690
713
|
response = self.client.post(
|
|
@@ -702,7 +725,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
702
725
|
payload = {
|
|
703
726
|
"hostname": "visitor",
|
|
704
727
|
"address": "127.0.0.1",
|
|
705
|
-
"port":
|
|
728
|
+
"port": 8888,
|
|
706
729
|
"mac_address": "aa:bb:cc:dd:ee:00",
|
|
707
730
|
}
|
|
708
731
|
response = self.client.post(
|
|
@@ -746,7 +769,7 @@ class NodeGetLocalTests(TestCase):
|
|
|
746
769
|
payload = {
|
|
747
770
|
"hostname": "visitor",
|
|
748
771
|
"address": "127.0.0.1",
|
|
749
|
-
"port":
|
|
772
|
+
"port": 8888,
|
|
750
773
|
"mac_address": "aa:bb:cc:dd:ee:11",
|
|
751
774
|
"public_key": public_bytes,
|
|
752
775
|
"token": token,
|
|
@@ -832,7 +855,7 @@ class NodeEnsureKeysTests(TestCase):
|
|
|
832
855
|
self.node = Node.objects.create(
|
|
833
856
|
hostname="ensure-host",
|
|
834
857
|
address="127.0.0.1",
|
|
835
|
-
port=
|
|
858
|
+
port=8888,
|
|
836
859
|
mac_address="00:11:22:33:44:55",
|
|
837
860
|
)
|
|
838
861
|
|
|
@@ -888,7 +911,7 @@ class NodeInfoViewTests(TestCase):
|
|
|
888
911
|
address="10.0.0.10",
|
|
889
912
|
ipv4_address="10.0.0.10",
|
|
890
913
|
ipv6_address="2001:db8::10",
|
|
891
|
-
port=
|
|
914
|
+
port=8888,
|
|
892
915
|
mac_address=self.mac,
|
|
893
916
|
public_endpoint="local",
|
|
894
917
|
current_relation=Node.Relation.SELF,
|
|
@@ -952,7 +975,7 @@ class RegisterVisitorNodeMessageTests(TestCase):
|
|
|
952
975
|
self.visitor = Node.objects.create(
|
|
953
976
|
hostname="visitor-node",
|
|
954
977
|
address="10.0.0.100",
|
|
955
|
-
port=
|
|
978
|
+
port=8888,
|
|
956
979
|
mac_address="00:10:20:30:40:50",
|
|
957
980
|
role=self.role,
|
|
958
981
|
)
|
|
@@ -1134,7 +1157,7 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1134
1157
|
Node.objects.create(
|
|
1135
1158
|
hostname="localnode",
|
|
1136
1159
|
address="192.168.1.5",
|
|
1137
|
-
port=
|
|
1160
|
+
port=8888,
|
|
1138
1161
|
mac_address="00:ff:ee:dd:cc:bb",
|
|
1139
1162
|
installed_version="1.9.0",
|
|
1140
1163
|
installed_revision="oldrev",
|
|
@@ -1192,7 +1215,7 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1192
1215
|
Node.objects.create(
|
|
1193
1216
|
hostname="samever",
|
|
1194
1217
|
address="192.168.1.6",
|
|
1195
|
-
port=
|
|
1218
|
+
port=8888,
|
|
1196
1219
|
mac_address="00:ff:ee:dd:cc:cc",
|
|
1197
1220
|
installed_version="1.0.0",
|
|
1198
1221
|
installed_revision="rev1",
|
|
@@ -1372,7 +1395,7 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1372
1395
|
sender = Node.objects.create(
|
|
1373
1396
|
hostname="sender",
|
|
1374
1397
|
address="10.0.0.1",
|
|
1375
|
-
port=
|
|
1398
|
+
port=8888,
|
|
1376
1399
|
mac_address="00:11:22:33:44:cc",
|
|
1377
1400
|
public_key=public_key,
|
|
1378
1401
|
)
|
|
@@ -1614,8 +1637,11 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1614
1637
|
port=9000,
|
|
1615
1638
|
mac_address="00:11:22:33:44:99",
|
|
1616
1639
|
)
|
|
1617
|
-
|
|
1618
|
-
|
|
1640
|
+
raw_name = f"poll_clipboard_node_{node.pk}"
|
|
1641
|
+
task_name = slugify_task_name(raw_name)
|
|
1642
|
+
PeriodicTask.objects.filter(
|
|
1643
|
+
name__in=periodic_task_name_variants(raw_name)
|
|
1644
|
+
).delete()
|
|
1619
1645
|
NodeFeatureAssignment.objects.create(node=node, feature=feature)
|
|
1620
1646
|
self.assertTrue(PeriodicTask.objects.filter(name=task_name).exists())
|
|
1621
1647
|
NodeFeatureAssignment.objects.filter(node=node, feature=feature).delete()
|
|
@@ -1632,8 +1658,11 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1632
1658
|
port=9100,
|
|
1633
1659
|
mac_address="00:11:22:33:44:aa",
|
|
1634
1660
|
)
|
|
1635
|
-
|
|
1636
|
-
|
|
1661
|
+
raw_name = f"capture_screenshot_node_{node.pk}"
|
|
1662
|
+
task_name = slugify_task_name(raw_name)
|
|
1663
|
+
PeriodicTask.objects.filter(
|
|
1664
|
+
name__in=periodic_task_name_variants(raw_name)
|
|
1665
|
+
).delete()
|
|
1637
1666
|
NodeFeatureAssignment.objects.create(node=node, feature=feature)
|
|
1638
1667
|
self.assertTrue(PeriodicTask.objects.filter(name=task_name).exists())
|
|
1639
1668
|
NodeFeatureAssignment.objects.filter(node=node, feature=feature).delete()
|
|
@@ -1652,14 +1681,18 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1652
1681
|
"base_path": settings.BASE_DIR,
|
|
1653
1682
|
},
|
|
1654
1683
|
)
|
|
1655
|
-
|
|
1684
|
+
raw_name = "pages_purge_landing_leads"
|
|
1685
|
+
task_name = slugify_task_name(raw_name)
|
|
1686
|
+
PeriodicTask.objects.filter(
|
|
1687
|
+
name__in=periodic_task_name_variants(raw_name)
|
|
1688
|
+
).delete()
|
|
1656
1689
|
NodeFeatureAssignment.objects.get_or_create(node=node, feature=feature)
|
|
1657
1690
|
self.assertTrue(
|
|
1658
|
-
PeriodicTask.objects.filter(name=
|
|
1691
|
+
PeriodicTask.objects.filter(name=task_name).exists()
|
|
1659
1692
|
)
|
|
1660
1693
|
NodeFeatureAssignment.objects.filter(node=node, feature=feature).delete()
|
|
1661
1694
|
self.assertFalse(
|
|
1662
|
-
PeriodicTask.objects.filter(name=
|
|
1695
|
+
PeriodicTask.objects.filter(name=task_name).exists()
|
|
1663
1696
|
)
|
|
1664
1697
|
|
|
1665
1698
|
def test_ocpp_session_report_task_syncs_with_feature(self):
|
|
@@ -1675,8 +1708,11 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1675
1708
|
"base_path": settings.BASE_DIR,
|
|
1676
1709
|
},
|
|
1677
1710
|
)
|
|
1678
|
-
|
|
1679
|
-
|
|
1711
|
+
raw_name = "ocpp_send_daily_session_report"
|
|
1712
|
+
task_name = slugify_task_name(raw_name)
|
|
1713
|
+
PeriodicTask.objects.filter(
|
|
1714
|
+
name__in=periodic_task_name_variants(raw_name)
|
|
1715
|
+
).delete()
|
|
1680
1716
|
|
|
1681
1717
|
with patch("nodes.models.mailer.can_send_email", return_value=True):
|
|
1682
1718
|
NodeFeatureAssignment.objects.get_or_create(node=node, feature=feature)
|
|
@@ -1699,8 +1735,11 @@ class NodeRegisterCurrentTests(TestCase):
|
|
|
1699
1735
|
"base_path": settings.BASE_DIR,
|
|
1700
1736
|
},
|
|
1701
1737
|
)
|
|
1702
|
-
|
|
1703
|
-
|
|
1738
|
+
raw_name = "ocpp_send_daily_session_report"
|
|
1739
|
+
task_name = slugify_task_name(raw_name)
|
|
1740
|
+
PeriodicTask.objects.filter(
|
|
1741
|
+
name__in=periodic_task_name_variants(raw_name)
|
|
1742
|
+
).delete()
|
|
1704
1743
|
|
|
1705
1744
|
with patch("nodes.models.mailer.can_send_email", return_value=False):
|
|
1706
1745
|
NodeFeatureAssignment.objects.get_or_create(node=node, feature=feature)
|
|
@@ -1736,7 +1775,7 @@ class NodeAdminTests(TestCase):
|
|
|
1736
1775
|
return Node.objects.create(
|
|
1737
1776
|
hostname="localnode",
|
|
1738
1777
|
address="127.0.0.1",
|
|
1739
|
-
port=
|
|
1778
|
+
port=8888,
|
|
1740
1779
|
mac_address=Node.get_current_mac(),
|
|
1741
1780
|
)
|
|
1742
1781
|
|
|
@@ -1811,7 +1850,7 @@ class NodeAdminTests(TestCase):
|
|
|
1811
1850
|
Node.objects.create(
|
|
1812
1851
|
hostname=hostname,
|
|
1813
1852
|
address="127.0.0.1",
|
|
1814
|
-
port=
|
|
1853
|
+
port=8888,
|
|
1815
1854
|
mac_address=None,
|
|
1816
1855
|
)
|
|
1817
1856
|
|
|
@@ -1942,7 +1981,7 @@ class NodeAdminTests(TestCase):
|
|
|
1942
1981
|
local_node = Node.objects.create(
|
|
1943
1982
|
hostname="local",
|
|
1944
1983
|
address="127.0.0.1",
|
|
1945
|
-
port=
|
|
1984
|
+
port=8888,
|
|
1946
1985
|
mac_address="00:11:22:33:44:aa",
|
|
1947
1986
|
public_key="LOCAL-PUB",
|
|
1948
1987
|
)
|
|
@@ -2189,6 +2228,34 @@ class NodeAdminTests(TestCase):
|
|
|
2189
2228
|
response, "Completed 0 of 1 feature check(s) successfully.", html=False
|
|
2190
2229
|
)
|
|
2191
2230
|
|
|
2231
|
+
@pytest.mark.feature("audio-capture")
|
|
2232
|
+
@patch("nodes.models.Node._has_audio_capture_device", return_value=False)
|
|
2233
|
+
def test_check_features_for_eligibility_audio_capture_requires_device(
|
|
2234
|
+
self, mock_device
|
|
2235
|
+
):
|
|
2236
|
+
self._create_local_node()
|
|
2237
|
+
feature, _ = NodeFeature.objects.get_or_create(
|
|
2238
|
+
slug="audio-capture", defaults={"display": "Audio Capture"}
|
|
2239
|
+
)
|
|
2240
|
+
changelist_url = reverse("admin:nodes_nodefeature_changelist")
|
|
2241
|
+
response = self.client.post(
|
|
2242
|
+
changelist_url,
|
|
2243
|
+
{
|
|
2244
|
+
"action": "check_features_for_eligibility",
|
|
2245
|
+
"_selected_action": [str(feature.pk)],
|
|
2246
|
+
},
|
|
2247
|
+
follow=True,
|
|
2248
|
+
)
|
|
2249
|
+
self.assertEqual(response.status_code, 200)
|
|
2250
|
+
self.assertContains(
|
|
2251
|
+
response,
|
|
2252
|
+
"No audio recording device detected on localnode for Audio Capture. This feature can be enabled manually.",
|
|
2253
|
+
html=False,
|
|
2254
|
+
)
|
|
2255
|
+
self.assertContains(
|
|
2256
|
+
response, "Completed 0 of 1 feature check(s) successfully.", html=False
|
|
2257
|
+
)
|
|
2258
|
+
|
|
2192
2259
|
@pytest.mark.feature("screenshot-poll")
|
|
2193
2260
|
def test_enable_selected_features_enables_manual_feature(self):
|
|
2194
2261
|
node = self._create_local_node()
|
|
@@ -2756,7 +2823,7 @@ class NodeProxyGatewayTests(TestCase):
|
|
|
2756
2823
|
self.node = Node.objects.create(
|
|
2757
2824
|
hostname="requester",
|
|
2758
2825
|
address="127.0.0.1",
|
|
2759
|
-
port=
|
|
2826
|
+
port=8888,
|
|
2760
2827
|
mac_address="aa:bb:cc:dd:ee:aa",
|
|
2761
2828
|
public_key=public_key,
|
|
2762
2829
|
)
|
|
@@ -2981,7 +3048,7 @@ class RFIDExportViewTests(TestCase):
|
|
|
2981
3048
|
self.local_node = Node.objects.create(
|
|
2982
3049
|
hostname="local",
|
|
2983
3050
|
address="127.0.0.1",
|
|
2984
|
-
port=
|
|
3051
|
+
port=8888,
|
|
2985
3052
|
mac_address=Node.get_current_mac(),
|
|
2986
3053
|
)
|
|
2987
3054
|
self.remote_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
|
|
@@ -3107,7 +3174,7 @@ class NetMessageReachTests(TestCase):
|
|
|
3107
3174
|
self.nodes[name] = Node.objects.create(
|
|
3108
3175
|
hostname=name.lower(),
|
|
3109
3176
|
address=f"10.0.0.{idx}",
|
|
3110
|
-
port=
|
|
3177
|
+
port=8888 + idx,
|
|
3111
3178
|
mac_address=f"00:11:22:33:44:{idx:02x}",
|
|
3112
3179
|
role=self.roles[name],
|
|
3113
3180
|
)
|
|
@@ -3318,7 +3385,7 @@ class NetMessagePropagationTests(TestCase):
|
|
|
3318
3385
|
Node.objects.create(
|
|
3319
3386
|
hostname=f"n{idx}",
|
|
3320
3387
|
address=f"10.0.0.{idx}",
|
|
3321
|
-
port=
|
|
3388
|
+
port=8888 + idx,
|
|
3322
3389
|
mac_address=f"00:11:22:33:44:{idx:02x}",
|
|
3323
3390
|
role=self.role,
|
|
3324
3391
|
public_endpoint=f"n{idx}",
|
|
@@ -3365,7 +3432,7 @@ class NetMessagePropagationTests(TestCase):
|
|
|
3365
3432
|
Node.objects.create(
|
|
3366
3433
|
hostname=f"n{idx}",
|
|
3367
3434
|
address=f"10.0.0.{idx}",
|
|
3368
|
-
port=
|
|
3435
|
+
port=8888 + idx,
|
|
3369
3436
|
mac_address=f"00:11:22:33:44:{idx:02x}",
|
|
3370
3437
|
role=self.role,
|
|
3371
3438
|
public_endpoint=f"n{idx}",
|
|
@@ -3454,7 +3521,7 @@ class NetMessageQueueTests(TestCase):
|
|
|
3454
3521
|
local = Node.objects.create(
|
|
3455
3522
|
hostname="local",
|
|
3456
3523
|
address="10.0.0.1",
|
|
3457
|
-
port=
|
|
3524
|
+
port=8888,
|
|
3458
3525
|
mac_address="00:11:22:33:44:10",
|
|
3459
3526
|
role=self.role,
|
|
3460
3527
|
public_endpoint="local",
|
|
@@ -3753,7 +3820,7 @@ class NetworkChargerActionSecurityTests(TestCase):
|
|
|
3753
3820
|
self.local_node = Node.objects.create(
|
|
3754
3821
|
hostname="local-node",
|
|
3755
3822
|
address="127.0.0.1",
|
|
3756
|
-
port=
|
|
3823
|
+
port=8888,
|
|
3757
3824
|
mac_address="00:aa:bb:cc:dd:10",
|
|
3758
3825
|
public_endpoint="local-endpoint",
|
|
3759
3826
|
)
|
|
@@ -4026,7 +4093,7 @@ class ContentSampleAdminTests(TestCase):
|
|
|
4026
4093
|
Node.objects.create(
|
|
4027
4094
|
hostname="host",
|
|
4028
4095
|
address="127.0.0.1",
|
|
4029
|
-
port=
|
|
4096
|
+
port=8888,
|
|
4030
4097
|
mac_address=Node.get_current_mac(),
|
|
4031
4098
|
)
|
|
4032
4099
|
url = reverse("admin:nodes_contentsample_from_clipboard")
|
|
@@ -4052,7 +4119,7 @@ class EmailOutboxTests(TestCase):
|
|
|
4052
4119
|
node = Node.objects.create(
|
|
4053
4120
|
hostname="outboxhost",
|
|
4054
4121
|
address="127.0.0.1",
|
|
4055
|
-
port=
|
|
4122
|
+
port=8888,
|
|
4056
4123
|
mac_address="00:11:22:33:aa:bb",
|
|
4057
4124
|
)
|
|
4058
4125
|
outbox = EmailOutbox.objects.create(
|
|
@@ -4068,7 +4135,7 @@ class EmailOutboxTests(TestCase):
|
|
|
4068
4135
|
node = Node.objects.create(
|
|
4069
4136
|
hostname="host",
|
|
4070
4137
|
address="127.0.0.1",
|
|
4071
|
-
port=
|
|
4138
|
+
port=8888,
|
|
4072
4139
|
mac_address="00:11:22:33:cc:dd",
|
|
4073
4140
|
)
|
|
4074
4141
|
node.send_mail("sub", "msg", ["to@example.com"])
|
|
@@ -4181,7 +4248,7 @@ class ClipboardTaskTests(TestCase):
|
|
|
4181
4248
|
Node.objects.create(
|
|
4182
4249
|
hostname="host",
|
|
4183
4250
|
address="127.0.0.1",
|
|
4184
|
-
port=
|
|
4251
|
+
port=8888,
|
|
4185
4252
|
mac_address=Node.get_current_mac(),
|
|
4186
4253
|
)
|
|
4187
4254
|
sample_clipboard()
|
|
@@ -4205,7 +4272,7 @@ class ClipboardTaskTests(TestCase):
|
|
|
4205
4272
|
node = Node.objects.create(
|
|
4206
4273
|
hostname="host",
|
|
4207
4274
|
address="127.0.0.1",
|
|
4208
|
-
port=
|
|
4275
|
+
port=8888,
|
|
4209
4276
|
mac_address=Node.get_current_mac(),
|
|
4210
4277
|
)
|
|
4211
4278
|
screenshot_dir = settings.LOG_DIR / "screenshots"
|
|
@@ -4228,7 +4295,7 @@ class ClipboardTaskTests(TestCase):
|
|
|
4228
4295
|
Node.objects.create(
|
|
4229
4296
|
hostname="host",
|
|
4230
4297
|
address="127.0.0.1",
|
|
4231
|
-
port=
|
|
4298
|
+
port=8888,
|
|
4232
4299
|
mac_address=Node.get_current_mac(),
|
|
4233
4300
|
)
|
|
4234
4301
|
mock_capture.side_effect = RuntimeError("boom")
|
|
@@ -4290,14 +4357,14 @@ class NodeRoleAdminTests(TestCase):
|
|
|
4290
4357
|
node1 = Node.objects.create(
|
|
4291
4358
|
hostname="n1",
|
|
4292
4359
|
address="127.0.0.1",
|
|
4293
|
-
port=
|
|
4360
|
+
port=8888,
|
|
4294
4361
|
mac_address="00:11:22:33:44:55",
|
|
4295
4362
|
role=role,
|
|
4296
4363
|
)
|
|
4297
4364
|
node2 = Node.objects.create(
|
|
4298
4365
|
hostname="n2",
|
|
4299
4366
|
address="127.0.0.2",
|
|
4300
|
-
port=
|
|
4367
|
+
port=8888,
|
|
4301
4368
|
mac_address="00:11:22:33:44:66",
|
|
4302
4369
|
)
|
|
4303
4370
|
url = reverse("admin:nodes_noderole_change", args=[role.pk])
|
|
@@ -4316,7 +4383,7 @@ class NodeRoleAdminTests(TestCase):
|
|
|
4316
4383
|
Node.objects.create(
|
|
4317
4384
|
hostname="n1",
|
|
4318
4385
|
address="127.0.0.1",
|
|
4319
|
-
port=
|
|
4386
|
+
port=8888,
|
|
4320
4387
|
mac_address="00:11:22:33:44:77",
|
|
4321
4388
|
role=role,
|
|
4322
4389
|
)
|
|
@@ -4379,7 +4446,7 @@ class NodeFeatureTests(TestCase):
|
|
|
4379
4446
|
self.node = Node.objects.create(
|
|
4380
4447
|
hostname="local",
|
|
4381
4448
|
address="127.0.0.1",
|
|
4382
|
-
port=
|
|
4449
|
+
port=8888,
|
|
4383
4450
|
mac_address="00:11:22:33:44:55",
|
|
4384
4451
|
role=self.role,
|
|
4385
4452
|
)
|
|
@@ -4552,7 +4619,7 @@ class NodeFeatureTests(TestCase):
|
|
|
4552
4619
|
node = Node.objects.create(
|
|
4553
4620
|
hostname="control",
|
|
4554
4621
|
address="127.0.0.1",
|
|
4555
|
-
port=
|
|
4622
|
+
port=8888,
|
|
4556
4623
|
mac_address=mac,
|
|
4557
4624
|
role=control_role,
|
|
4558
4625
|
)
|
|
@@ -4576,7 +4643,7 @@ class NodeFeatureTests(TestCase):
|
|
|
4576
4643
|
node = Node.objects.create(
|
|
4577
4644
|
hostname="control",
|
|
4578
4645
|
address="127.0.0.1",
|
|
4579
|
-
port=
|
|
4646
|
+
port=8888,
|
|
4580
4647
|
mac_address=mac,
|
|
4581
4648
|
role=control_role,
|
|
4582
4649
|
base_path=str(Path(tmp)),
|
|
@@ -4597,7 +4664,7 @@ class NodeFeatureTests(TestCase):
|
|
|
4597
4664
|
node = Node.objects.create(
|
|
4598
4665
|
hostname="control",
|
|
4599
4666
|
address="127.0.0.1",
|
|
4600
|
-
port=
|
|
4667
|
+
port=8888,
|
|
4601
4668
|
mac_address=mac,
|
|
4602
4669
|
role=control_role,
|
|
4603
4670
|
)
|
|
@@ -4612,6 +4679,72 @@ class NodeFeatureTests(TestCase):
|
|
|
4612
4679
|
)
|
|
4613
4680
|
|
|
4614
4681
|
|
|
4682
|
+
class AudioCaptureDetectionTests(TestCase):
|
|
4683
|
+
def test_has_audio_capture_device_true(self):
|
|
4684
|
+
with TemporaryDirectory() as tmp:
|
|
4685
|
+
pcm_path = Path(tmp) / "pcm"
|
|
4686
|
+
pcm_path.write_text(
|
|
4687
|
+
"00-00: USB Audio : USB Audio : playback 1 : capture 1\n",
|
|
4688
|
+
encoding="utf-8",
|
|
4689
|
+
)
|
|
4690
|
+
with patch.object(Node, "AUDIO_CAPTURE_PCM_PATH", pcm_path):
|
|
4691
|
+
self.assertTrue(Node._has_audio_capture_device())
|
|
4692
|
+
|
|
4693
|
+
def test_has_audio_capture_device_false_without_capture(self):
|
|
4694
|
+
with TemporaryDirectory() as tmp:
|
|
4695
|
+
pcm_path = Path(tmp) / "pcm"
|
|
4696
|
+
pcm_path.write_text(
|
|
4697
|
+
"00-00: USB Audio : USB Audio : playback 1\n",
|
|
4698
|
+
encoding="utf-8",
|
|
4699
|
+
)
|
|
4700
|
+
with patch.object(Node, "AUDIO_CAPTURE_PCM_PATH", pcm_path):
|
|
4701
|
+
self.assertFalse(Node._has_audio_capture_device())
|
|
4702
|
+
|
|
4703
|
+
def test_has_audio_capture_device_false_when_file_missing(self):
|
|
4704
|
+
with TemporaryDirectory() as tmp:
|
|
4705
|
+
pcm_path = Path(tmp) / "pcm"
|
|
4706
|
+
with patch.object(Node, "AUDIO_CAPTURE_PCM_PATH", pcm_path):
|
|
4707
|
+
self.assertFalse(Node._has_audio_capture_device())
|
|
4708
|
+
|
|
4709
|
+
|
|
4710
|
+
class AudioCaptureFeatureCheckTests(TestCase):
|
|
4711
|
+
def setUp(self):
|
|
4712
|
+
self.node = Node.objects.create(
|
|
4713
|
+
hostname="localnode",
|
|
4714
|
+
address="127.0.0.1",
|
|
4715
|
+
port=8888,
|
|
4716
|
+
mac_address=Node.get_current_mac(),
|
|
4717
|
+
)
|
|
4718
|
+
self.feature, _ = NodeFeature.objects.get_or_create(
|
|
4719
|
+
slug="audio-capture", defaults={"display": "Audio Capture"}
|
|
4720
|
+
)
|
|
4721
|
+
|
|
4722
|
+
@pytest.mark.feature("audio-capture")
|
|
4723
|
+
@patch("nodes.models.Node._has_audio_capture_device", return_value=False)
|
|
4724
|
+
def test_feature_check_warns_without_device(self, mock_device):
|
|
4725
|
+
result = feature_checks.run(self.feature, node=self.node)
|
|
4726
|
+
self.assertFalse(result.success)
|
|
4727
|
+
self.assertIn("No audio recording device detected", result.message)
|
|
4728
|
+
self.assertEqual(result.level, messages.WARNING)
|
|
4729
|
+
|
|
4730
|
+
@pytest.mark.feature("audio-capture")
|
|
4731
|
+
@patch("nodes.models.Node._has_audio_capture_device", return_value=True)
|
|
4732
|
+
def test_feature_check_warns_when_feature_disabled(self, mock_device):
|
|
4733
|
+
result = feature_checks.run(self.feature, node=self.node)
|
|
4734
|
+
self.assertFalse(result.success)
|
|
4735
|
+
self.assertIn("is not enabled", result.message)
|
|
4736
|
+
self.assertEqual(result.level, messages.WARNING)
|
|
4737
|
+
|
|
4738
|
+
@pytest.mark.feature("audio-capture")
|
|
4739
|
+
@patch("nodes.models.Node._has_audio_capture_device", return_value=True)
|
|
4740
|
+
def test_feature_check_passes_when_enabled(self, mock_device):
|
|
4741
|
+
NodeFeatureAssignment.objects.get_or_create(node=self.node, feature=self.feature)
|
|
4742
|
+
result = feature_checks.run(self.feature, node=self.node)
|
|
4743
|
+
self.assertTrue(result.success)
|
|
4744
|
+
self.assertIn("recording device is available", result.message)
|
|
4745
|
+
self.assertEqual(result.level, messages.SUCCESS)
|
|
4746
|
+
|
|
4747
|
+
|
|
4615
4748
|
class CeleryReportAdminViewTests(TestCase):
|
|
4616
4749
|
def setUp(self):
|
|
4617
4750
|
User = get_user_model()
|