arthexis 0.1.13__py3-none-any.whl → 0.1.15__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.
- {arthexis-0.1.13.dist-info → arthexis-0.1.15.dist-info}/METADATA +224 -221
- arthexis-0.1.15.dist-info/RECORD +110 -0
- {arthexis-0.1.13.dist-info → arthexis-0.1.15.dist-info}/licenses/LICENSE +674 -674
- config/__init__.py +5 -5
- config/active_app.py +15 -15
- config/asgi.py +43 -43
- config/auth_app.py +7 -7
- config/celery.py +32 -32
- config/context_processors.py +67 -69
- config/horologia_app.py +7 -7
- config/loadenv.py +11 -11
- config/logging.py +59 -48
- config/middleware.py +25 -25
- config/offline.py +49 -49
- config/settings.py +691 -682
- config/settings_helpers.py +109 -109
- config/urls.py +171 -166
- config/wsgi.py +17 -17
- core/admin.py +3795 -2809
- core/admin_history.py +50 -50
- core/admindocs.py +151 -151
- core/apps.py +356 -272
- core/auto_upgrade.py +57 -57
- core/backends.py +265 -236
- core/changelog.py +342 -0
- core/entity.py +149 -133
- core/environment.py +61 -61
- core/fields.py +168 -168
- core/form_fields.py +75 -75
- core/github_helper.py +188 -25
- core/github_issues.py +178 -172
- core/github_repos.py +72 -0
- core/lcd_screen.py +78 -78
- core/liveupdate.py +25 -25
- core/log_paths.py +114 -100
- core/mailer.py +85 -85
- core/middleware.py +91 -91
- core/models.py +3637 -2795
- core/notifications.py +105 -105
- core/public_wifi.py +267 -227
- core/reference_utils.py +108 -108
- core/release.py +840 -368
- core/rfid_import_export.py +113 -0
- core/sigil_builder.py +149 -149
- core/sigil_context.py +20 -20
- core/sigil_resolver.py +315 -315
- core/system.py +952 -493
- core/tasks.py +408 -394
- core/temp_passwords.py +181 -181
- core/test_system_info.py +186 -139
- core/tests.py +2168 -1521
- core/tests_liveupdate.py +17 -17
- core/urls.py +11 -11
- core/user_data.py +641 -633
- core/views.py +2201 -1417
- core/widgets.py +213 -94
- core/workgroup_urls.py +17 -17
- core/workgroup_views.py +94 -94
- nodes/admin.py +1720 -1161
- nodes/apps.py +87 -85
- nodes/backends.py +160 -160
- nodes/dns.py +203 -203
- nodes/feature_checks.py +133 -133
- nodes/lcd.py +165 -165
- nodes/models.py +1764 -1597
- nodes/reports.py +411 -411
- nodes/rfid_sync.py +195 -0
- nodes/signals.py +18 -0
- nodes/tasks.py +46 -46
- nodes/tests.py +3830 -3116
- nodes/urls.py +15 -14
- nodes/utils.py +121 -105
- nodes/views.py +683 -619
- ocpp/admin.py +948 -948
- ocpp/apps.py +25 -25
- ocpp/consumers.py +1565 -1459
- ocpp/evcs.py +844 -844
- ocpp/evcs_discovery.py +158 -158
- ocpp/models.py +917 -917
- ocpp/reference_utils.py +42 -42
- ocpp/routing.py +11 -11
- ocpp/simulator.py +745 -745
- ocpp/status_display.py +26 -26
- ocpp/store.py +601 -541
- ocpp/tasks.py +31 -31
- ocpp/test_export_import.py +130 -130
- ocpp/test_rfid.py +913 -702
- ocpp/tests.py +4445 -4094
- ocpp/transactions_io.py +189 -189
- ocpp/urls.py +50 -50
- ocpp/views.py +1479 -1251
- pages/admin.py +769 -539
- pages/apps.py +10 -10
- pages/checks.py +40 -40
- pages/context_processors.py +127 -119
- pages/defaults.py +13 -13
- pages/forms.py +198 -198
- pages/middleware.py +209 -153
- pages/models.py +643 -426
- pages/tasks.py +74 -0
- pages/tests.py +3025 -2200
- pages/urls.py +26 -25
- pages/utils.py +23 -12
- pages/views.py +1176 -1128
- arthexis-0.1.13.dist-info/RECORD +0 -105
- nodes/actions.py +0 -70
- {arthexis-0.1.13.dist-info → arthexis-0.1.15.dist-info}/WHEEL +0 -0
- {arthexis-0.1.13.dist-info → arthexis-0.1.15.dist-info}/top_level.txt +0 -0
core/test_system_info.py
CHANGED
|
@@ -1,139 +1,186 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import os
|
|
3
|
-
from
|
|
4
|
-
from
|
|
5
|
-
from
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
django
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from
|
|
17
|
-
from
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def
|
|
61
|
-
info = _gather_info()
|
|
62
|
-
self.
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
info
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
"
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
self.
|
|
139
|
-
|
|
1
|
+
import json
|
|
2
|
+
import os
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from subprocess import CompletedProcess
|
|
6
|
+
from tempfile import TemporaryDirectory
|
|
7
|
+
from unittest.mock import Mock, patch
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
|
|
11
|
+
|
|
12
|
+
import django
|
|
13
|
+
|
|
14
|
+
django.setup()
|
|
15
|
+
|
|
16
|
+
from django.conf import settings
|
|
17
|
+
from django.test import SimpleTestCase, override_settings
|
|
18
|
+
from nodes.models import Node, NodeFeature, NodeRole
|
|
19
|
+
from core.system import (
|
|
20
|
+
_gather_info,
|
|
21
|
+
_load_auto_upgrade_log_entries,
|
|
22
|
+
_read_auto_upgrade_mode,
|
|
23
|
+
get_system_sigil_values,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SystemInfoRoleTests(SimpleTestCase):
|
|
28
|
+
@override_settings(NODE_ROLE="Terminal")
|
|
29
|
+
def test_defaults_to_terminal(self):
|
|
30
|
+
info = _gather_info()
|
|
31
|
+
self.assertEqual(info["role"], "Terminal")
|
|
32
|
+
|
|
33
|
+
@override_settings(NODE_ROLE="Satellite")
|
|
34
|
+
def test_uses_settings_role(self):
|
|
35
|
+
info = _gather_info()
|
|
36
|
+
self.assertEqual(info["role"], "Satellite")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class SystemInfoScreenModeTests(SimpleTestCase):
|
|
40
|
+
def test_without_lockfile(self):
|
|
41
|
+
info = _gather_info()
|
|
42
|
+
self.assertEqual(info["screen_mode"], "")
|
|
43
|
+
|
|
44
|
+
def test_with_lockfile(self):
|
|
45
|
+
lock_dir = Path(settings.BASE_DIR) / "locks"
|
|
46
|
+
lock_dir.mkdir(exist_ok=True)
|
|
47
|
+
lock_file = lock_dir / "screen_mode.lck"
|
|
48
|
+
lock_file.write_text("tft")
|
|
49
|
+
try:
|
|
50
|
+
info = _gather_info()
|
|
51
|
+
self.assertEqual(info["screen_mode"], "tft")
|
|
52
|
+
finally:
|
|
53
|
+
lock_file.unlink()
|
|
54
|
+
if not any(lock_dir.iterdir()):
|
|
55
|
+
lock_dir.rmdir()
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SystemInfoRevisionTests(SimpleTestCase):
|
|
59
|
+
@patch("core.system.revision.get_revision", return_value="abcdef1234567890")
|
|
60
|
+
def test_includes_full_revision(self, mock_revision):
|
|
61
|
+
info = _gather_info()
|
|
62
|
+
self.assertEqual(info["revision"], "abcdef1234567890")
|
|
63
|
+
mock_revision.assert_called_once()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class SystemInfoDatabaseTests(SimpleTestCase):
|
|
67
|
+
def test_collects_database_definitions(self):
|
|
68
|
+
info = _gather_info()
|
|
69
|
+
self.assertIn("databases", info)
|
|
70
|
+
aliases = {entry["alias"] for entry in info["databases"]}
|
|
71
|
+
self.assertIn("default", aliases)
|
|
72
|
+
|
|
73
|
+
@override_settings(
|
|
74
|
+
DATABASES={
|
|
75
|
+
"default": {
|
|
76
|
+
"ENGINE": "django.db.backends.sqlite3",
|
|
77
|
+
"NAME": Path("/tmp/db.sqlite3"),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
def test_serializes_path_database_names(self):
|
|
82
|
+
info = _gather_info()
|
|
83
|
+
databases = info["databases"]
|
|
84
|
+
self.assertEqual(databases[0]["name"], "/tmp/db.sqlite3")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class AutoUpgradeModeTests(SimpleTestCase):
|
|
88
|
+
def test_lock_file_read_error_marks_enabled(self):
|
|
89
|
+
mock_path = Mock()
|
|
90
|
+
mock_path.exists.return_value = True
|
|
91
|
+
mock_path.read_text.side_effect = OSError
|
|
92
|
+
|
|
93
|
+
with patch("core.system._auto_upgrade_mode_file", return_value=mock_path):
|
|
94
|
+
info = _read_auto_upgrade_mode(Path("/tmp"))
|
|
95
|
+
|
|
96
|
+
self.assertTrue(info["lock_exists"])
|
|
97
|
+
self.assertTrue(info["enabled"])
|
|
98
|
+
self.assertTrue(info["read_error"])
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class AutoUpgradeLogParsingTests(SimpleTestCase):
|
|
102
|
+
def test_parses_zulu_timestamp_entries(self):
|
|
103
|
+
with TemporaryDirectory() as tmpdir:
|
|
104
|
+
base_dir = Path(tmpdir)
|
|
105
|
+
log_dir = base_dir / "logs"
|
|
106
|
+
log_dir.mkdir()
|
|
107
|
+
log_path = log_dir / "auto-upgrade.log"
|
|
108
|
+
log_path.write_text("2024-01-01T12:34:56Z Started\n", encoding="utf-8")
|
|
109
|
+
|
|
110
|
+
with patch("core.system._format_timestamp", return_value="formatted") as mock_format:
|
|
111
|
+
result = _load_auto_upgrade_log_entries(base_dir)
|
|
112
|
+
|
|
113
|
+
entries = result["entries"]
|
|
114
|
+
self.assertEqual(len(entries), 1)
|
|
115
|
+
entry = entries[0]
|
|
116
|
+
self.assertEqual(entry["message"], "Started")
|
|
117
|
+
self.assertEqual(entry["timestamp"], "formatted")
|
|
118
|
+
|
|
119
|
+
mock_format.assert_called_once()
|
|
120
|
+
parsed_dt = mock_format.call_args[0][0]
|
|
121
|
+
self.assertEqual(parsed_dt.year, 2024)
|
|
122
|
+
self.assertEqual(parsed_dt.month, 1)
|
|
123
|
+
self.assertEqual(parsed_dt.day, 1)
|
|
124
|
+
self.assertEqual(parsed_dt.utcoffset(), timedelta(0))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class SystemInfoRunserverDetectionTests(SimpleTestCase):
|
|
128
|
+
@patch("core.system.subprocess.run")
|
|
129
|
+
def test_detects_runserver_process_port(self, mock_run):
|
|
130
|
+
mock_run.return_value = CompletedProcess(
|
|
131
|
+
args=["pgrep"],
|
|
132
|
+
returncode=0,
|
|
133
|
+
stdout="123 python manage.py runserver 0.0.0.0:8000 --noreload\n",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
info = _gather_info()
|
|
137
|
+
|
|
138
|
+
self.assertTrue(info["running"])
|
|
139
|
+
self.assertEqual(info["port"], 8000)
|
|
140
|
+
|
|
141
|
+
@patch("core.system._probe_ports", return_value=(True, 8000))
|
|
142
|
+
@patch("core.system.subprocess.run", side_effect=FileNotFoundError)
|
|
143
|
+
def test_falls_back_to_port_probe_when_pgrep_missing(self, mock_run, mock_probe):
|
|
144
|
+
info = _gather_info()
|
|
145
|
+
|
|
146
|
+
self.assertTrue(info["running"])
|
|
147
|
+
self.assertEqual(info["port"], 8000)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class SystemSigilValueTests(SimpleTestCase):
|
|
151
|
+
def test_exports_values_for_sigil_resolution(self):
|
|
152
|
+
sample_info = {
|
|
153
|
+
"installed": True,
|
|
154
|
+
"revision": "abcdef",
|
|
155
|
+
"service": "gunicorn",
|
|
156
|
+
"mode": "internal",
|
|
157
|
+
"port": 8888,
|
|
158
|
+
"role": "Terminal",
|
|
159
|
+
"screen_mode": "",
|
|
160
|
+
"features": [
|
|
161
|
+
{"display": "Feature", "expected": True, "actual": False, "slug": "feature"}
|
|
162
|
+
],
|
|
163
|
+
"running": True,
|
|
164
|
+
"service_status": "active",
|
|
165
|
+
"hostname": "example.local",
|
|
166
|
+
"ip_addresses": ["127.0.0.1"],
|
|
167
|
+
"databases": [
|
|
168
|
+
{
|
|
169
|
+
"alias": "default",
|
|
170
|
+
"engine": "django.db.backends.sqlite3",
|
|
171
|
+
"name": "db.sqlite3",
|
|
172
|
+
}
|
|
173
|
+
],
|
|
174
|
+
}
|
|
175
|
+
with patch("core.system._gather_info", return_value=sample_info):
|
|
176
|
+
values = get_system_sigil_values()
|
|
177
|
+
|
|
178
|
+
self.assertEqual(values["REVISION"], "abcdef")
|
|
179
|
+
self.assertEqual(values["RUNNING"], "True")
|
|
180
|
+
self.assertEqual(values["NGINX_MODE"], "internal (8888)")
|
|
181
|
+
self.assertEqual(values["IP_ADDRESSES"], "127.0.0.1")
|
|
182
|
+
features = json.loads(values["FEATURES"])
|
|
183
|
+
self.assertEqual(features[0]["display"], "Feature")
|
|
184
|
+
databases = json.loads(values["DATABASES"])
|
|
185
|
+
self.assertEqual(databases[0]["alias"], "default")
|
|
186
|
+
|