feat: Phase 6 — require_active_service decorator + wizard install wiring

Email/calendar/files routes now return 404 when the service is not
installed, using a require_active_service decorator that checks
ServiceRegistry. Status endpoints are exempt so health checks always work.

SetupManager.complete_setup() now accepts a service_store_manager and
installs any wizard-selected services in a background daemon thread after
setup completes. Failures are logged but do not fail the wizard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 16:58:57 -04:00
parent a69ca1e402
commit 44d7e96f29
12 changed files with 556 additions and 5 deletions
+9 -3
View File
@@ -393,8 +393,10 @@ class TestAPIEndpoints(unittest.TestCase):
self.assertEqual(response.status_code, 500)
mock_peers.update_peer_ip.side_effect = None
@patch('app.service_registry')
@patch('app.email_manager')
def test_email_endpoints(self, mock_email):
def test_email_endpoints(self, mock_email, mock_sr):
mock_sr.get.return_value = {'id': 'email', 'installed': True}
# Ensure all relevant mock methods return JSON-serializable values
mock_email.get_users.return_value = [{'username': 'user1', 'domain': 'cell', 'email': 'user1@cell'}]
mock_email.create_email_user.return_value = True
@@ -454,8 +456,10 @@ class TestAPIEndpoints(unittest.TestCase):
self.assertEqual(response.status_code, 500)
mock_email.get_mailbox_info.side_effect = None
@patch('app.service_registry')
@patch('app.calendar_manager')
def test_calendar_endpoints(self, mock_calendar):
def test_calendar_endpoints(self, mock_calendar, mock_sr):
mock_sr.get.return_value = {'id': 'calendar', 'installed': True}
# Mock return values for all relevant calendar_manager methods
mock_calendar.get_users.return_value = [{'username': 'user1', 'collections': {'calendars': ['cal1'], 'contacts': ['c1']}}]
mock_calendar.create_calendar_user.return_value = True
@@ -523,8 +527,10 @@ class TestAPIEndpoints(unittest.TestCase):
self.assertEqual(response.status_code, 500)
mock_calendar.test_connectivity.side_effect = None
@patch('app.service_registry')
@patch('app.file_manager')
def test_file_endpoints(self, mock_file):
def test_file_endpoints(self, mock_file, mock_sr):
mock_sr.get.return_value = {'id': 'files', 'installed': True}
# Mock return values for all relevant file_manager methods
mock_file.get_users.return_value = [{'username': 'user1', 'storage_info': {'total_files': 1, 'total_size_bytes': 1000}}]
mock_file.create_user.return_value = True