fix: clean-install bugs — Tor false-installed, WG port-check honesty, encrypted backup upload
Unit Tests / test (push) Successful in 13m7s

Three independent bugs surfaced during pic1 clean-install testing:

1. Tor _exit_status hardcoded configured=True regardless of whether Tor was
   actually installed.  Status now flows through the same store-installed /
   container-running bridge used by every other optional service, so Tor only
   reports installed when the container is present and running.

2. check_port_open compared the port from wg0.conf against the kernel-reported
   listening port, causing false "port closed" results whenever the conf and the
   running container were momentarily out of sync.  The function is now an honest
   liveness check: any wg0 interface that is up and has a "listening port:" line
   in `wg show` is considered open.  The check-port API endpoint now also returns
   the actual kernel listening_port and a port_mismatch flag so the UI can inform
   the user when a container recreate is needed.  (The recreate machinery already
   exists via the port-change pending-restart path; this fix makes the mismatch
   visible rather than silently lying about reachability.)

3. upload_backup only handled .zip archives; encrypted .age blobs were rejected
   with a generic error.  The endpoint now calls backup_crypto.is_encrypted() to
   detect Age-encrypted blobs and stores them verbatim as <id>.tar.gz.age with
   mode 0600 so they can be uploaded and then restored with a passphrase.  The
   plaintext zip path is unchanged.

Tests added/updated: test_connectivity_manager.py (Tor status bridge),
test_wireguard_manager.py + test_wireguard_endpoints.py (port-check liveness
and mismatch flag), test_config_backup_restore_http.py (encrypted upload
round-trip).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-11 01:52:26 -04:00
parent 743b026b01
commit 8d904b1b8f
9 changed files with 207 additions and 26 deletions
+21
View File
@@ -36,6 +36,7 @@ class TestWireGuardEndpoints(unittest.TestCase):
def test_check_port_returns_port_open_true(self, mock_wg):
mock_wg.check_port_open.return_value = True
mock_wg._get_configured_port.return_value = 51820
mock_wg._kernel_listening_port.return_value = 51820
r = self.client.post('/api/wireguard/check-port')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
@@ -43,15 +44,35 @@ class TestWireGuardEndpoints(unittest.TestCase):
self.assertIn('port', data)
self.assertTrue(data['port_open'])
self.assertEqual(data['port'], 51820)
self.assertEqual(data['listening_port'], 51820)
self.assertFalse(data['port_mismatch'])
@patch('app.wireguard_manager')
def test_check_port_reports_actual_listening_port_on_mismatch(self, mock_wg):
# Configured 51821 but kernel bound to 51820 — endpoint surfaces the real
# bound port and flags the mismatch without reporting the port closed.
mock_wg.check_port_open.return_value = True
mock_wg._get_configured_port.return_value = 51821
mock_wg._kernel_listening_port.return_value = 51820
r = self.client.post('/api/wireguard/check-port')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertTrue(data['port_open'])
self.assertEqual(data['port'], 51821)
self.assertEqual(data['listening_port'], 51820)
self.assertTrue(data['port_mismatch'])
@patch('app.wireguard_manager')
def test_check_port_returns_port_open_false(self, mock_wg):
mock_wg.check_port_open.return_value = False
mock_wg._get_configured_port.return_value = 51820
mock_wg._kernel_listening_port.return_value = None
r = self.client.post('/api/wireguard/check-port')
self.assertEqual(r.status_code, 200)
data = json.loads(r.data)
self.assertFalse(data['port_open'])
self.assertIsNone(data['listening_port'])
self.assertFalse(data['port_mismatch'])
@patch('app.wireguard_manager')
def test_check_port_returns_500_on_exception(self, mock_wg):