Files
pic/tests/test_services_active_endpoint.py
T
roof a10fe11136
Unit Tests / test (push) Successful in 11m24s
feat: Phase 4 — dynamic nav + service visibility based on installed services
Email, calendar, and files no longer appear in the nav or as usable pages
unless they are installed. The nav refreshes whenever a service is installed
or removed via the new pic-services-changed CustomEvent.

Changes:
- routes/services.py: add GET /api/services/active endpoint
- api.js: add servicesAPI.listActive()
- App.jsx: replace hardcoded coreServiceChildren with dynamic state fetched
  from /api/services/active; SERVICE_META maps ids to nav entry shapes
- ServiceNotInstalledBanner.jsx: new component — admin gets catalog link,
  peer gets "contact admin" message
- EmailPage/CalendarPage/FilesPage: show banner when service not installed
- ServicesIndex.jsx: remove CoreServiceCard + CORE_SERVICES "Built-in"
  section; rename Remove → Uninstall; dispatch pic-services-changed on
  install/uninstall success
- MyServices.jsx: conditionally render service cards based on active list;
  placeholder card when absent; page-level notice when nothing is installed
- tests/test_services_active_endpoint.py: 4 new endpoint tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 12:15:02 -04:00

81 lines
2.1 KiB
Python

"""
Tests for GET /api/services/active endpoint.
"""
import json
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch
import pytest
sys.path.insert(0, str(Path(__file__).parent.parent / 'api'))
from app import app
def _make_registry(active_services):
reg = MagicMock()
reg.list_active = MagicMock(return_value=active_services)
return reg
@pytest.fixture()
def client():
app.config['TESTING'] = True
with app.test_client() as c:
yield c
def test_active_returns_200(client):
import app as app_module
with patch.object(app_module, 'service_registry', _make_registry([])):
resp = client.get('/api/services/active')
assert resp.status_code == 200
def test_active_returns_empty_list_when_nothing_installed(client):
import app as app_module
with patch.object(app_module, 'service_registry', _make_registry([])):
resp = client.get('/api/services/active')
data = json.loads(resp.data)
assert data == []
def test_active_returns_installed_services(client):
email_svc = {
'id': 'email',
'name': 'Email',
'subdomain': 'mail',
'capabilities': {'has_accounts': True},
'config': {},
}
import app as app_module
with patch.object(app_module, 'service_registry', _make_registry([email_svc])):
resp = client.get('/api/services/active')
data = json.loads(resp.data)
assert isinstance(data, list)
assert len(data) == 1
assert data[0]['id'] == 'email'
assert data[0]['name'] == 'Email'
def test_active_response_shape(client):
"""Each entry must have id, name, subdomain, and capabilities keys."""
email_svc = {
'id': 'email',
'name': 'Email',
'subdomain': 'mail',
'capabilities': {'has_accounts': True},
'config': {},
}
import app as app_module
with patch.object(app_module, 'service_registry', _make_registry([email_svc])):
resp = client.get('/api/services/active')
data = json.loads(resp.data)
entry = data[0]
assert 'id' in entry
assert 'name' in entry
assert 'subdomain' in entry
assert 'capabilities' in entry