wire: AccountManager HTTP dispatch + EgressManager startup + egress API routes
Unit Tests / test (push) Successful in 11m15s
Unit Tests / test (push) Successful in 11m15s
- add_peer() now calls account_manager.provision() for any installed store service whose manifest declares accounts.manager == 'http', enabling per-peer credential provisioning to third-party HTTP services - reapply_on_startup() calls egress_manager.apply_all() so fwmark rules survive container restarts without manual intervention - add GET /api/egress/status and PUT /api/egress/services/<id>/exit routes so the UI can read and override per-service egress policy - tests: HTTP provision wiring (happy path + non-fatal failure), egress apply_all at startup (wired/unwired/failure cases) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -372,6 +372,104 @@ def test_delete_nonexistent_peer_returns_gracefully(admin_client, mock_peer_regi
|
||||
assert r.status_code in (200, 404)
|
||||
|
||||
|
||||
# ── POST /api/peers — HTTP store service provisioning ────────────────────────
|
||||
|
||||
def test_create_peer_provisions_http_store_services(
|
||||
auth_mgr, mock_email_mgr, mock_calendar_mgr,
|
||||
mock_file_mgr, mock_wg_mgr, mock_peer_registry):
|
||||
"""When an installed store service has accounts.manager='http',
|
||||
account_manager.provision() must be called for the new peer."""
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'test-secret'
|
||||
|
||||
mock_am = MagicMock()
|
||||
mock_am.provision.return_value = {'password': 'generated'}
|
||||
mock_am.store_credentials = MagicMock()
|
||||
|
||||
mock_cfg = MagicMock()
|
||||
mock_cfg.get_installed_services.return_value = {'my-store-app': {}}
|
||||
|
||||
mock_sreg = MagicMock()
|
||||
mock_sreg.get.return_value = {'id': 'my-store-app', 'accounts': {'manager': 'http'}, 'backend': 'cell-my-store-app:8080'}
|
||||
|
||||
patches = [
|
||||
patch('app.auth_manager', auth_mgr),
|
||||
patch('app.email_manager', mock_email_mgr),
|
||||
patch('app.calendar_manager', mock_calendar_mgr),
|
||||
patch('app.file_manager', mock_file_mgr),
|
||||
patch('app.wireguard_manager', mock_wg_mgr),
|
||||
patch('app.peer_registry', mock_peer_registry),
|
||||
patch('app.firewall_manager'),
|
||||
patch('app.account_manager', mock_am),
|
||||
patch('app.config_manager', mock_cfg),
|
||||
patch('app.service_registry', mock_sreg),
|
||||
]
|
||||
try:
|
||||
import auth_routes
|
||||
patches.append(patch.object(auth_routes, 'auth_manager', auth_mgr, create=True))
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
started = [p.start() for p in patches]
|
||||
try:
|
||||
with app.test_client() as client:
|
||||
r = _login(client)
|
||||
assert r.status_code == 200
|
||||
resp = _post_peer(client)
|
||||
assert resp.status_code == 201
|
||||
mock_am.provision.assert_called_once_with('my-store-app', 'alice')
|
||||
finally:
|
||||
for p in patches:
|
||||
p.stop()
|
||||
|
||||
|
||||
def test_create_peer_http_provision_failure_is_nonfatal(
|
||||
auth_mgr, mock_email_mgr, mock_calendar_mgr,
|
||||
mock_file_mgr, mock_wg_mgr, mock_peer_registry):
|
||||
"""HTTP account provisioning failure must not block peer creation."""
|
||||
app.config['TESTING'] = True
|
||||
app.config['SECRET_KEY'] = 'test-secret'
|
||||
|
||||
mock_am = MagicMock()
|
||||
mock_am.provision.side_effect = RuntimeError('service unavailable')
|
||||
mock_am.store_credentials = MagicMock()
|
||||
|
||||
mock_cfg = MagicMock()
|
||||
mock_cfg.get_installed_services.return_value = {'my-store-app': {}}
|
||||
|
||||
mock_sreg = MagicMock()
|
||||
mock_sreg.get.return_value = {'id': 'my-store-app', 'accounts': {'manager': 'http'}, 'backend': 'cell-my-store-app:8080'}
|
||||
|
||||
patches = [
|
||||
patch('app.auth_manager', auth_mgr),
|
||||
patch('app.email_manager', mock_email_mgr),
|
||||
patch('app.calendar_manager', mock_calendar_mgr),
|
||||
patch('app.file_manager', mock_file_mgr),
|
||||
patch('app.wireguard_manager', mock_wg_mgr),
|
||||
patch('app.peer_registry', mock_peer_registry),
|
||||
patch('app.firewall_manager'),
|
||||
patch('app.account_manager', mock_am),
|
||||
patch('app.config_manager', mock_cfg),
|
||||
patch('app.service_registry', mock_sreg),
|
||||
]
|
||||
try:
|
||||
import auth_routes
|
||||
patches.append(patch.object(auth_routes, 'auth_manager', auth_mgr, create=True))
|
||||
except (ImportError, AttributeError):
|
||||
pass
|
||||
|
||||
started = [p.start() for p in patches]
|
||||
try:
|
||||
with app.test_client() as client:
|
||||
r = _login(client)
|
||||
assert r.status_code == 200
|
||||
resp = _post_peer(client)
|
||||
assert resp.status_code == 201, 'HTTP provision failure must not block peer creation'
|
||||
finally:
|
||||
for p in patches:
|
||||
p.stop()
|
||||
|
||||
|
||||
# ── POST /api/peers — firewall rollback (A3) ──────────────────────────────────
|
||||
|
||||
def test_create_peer_rolls_back_firewall_on_dns_failure(
|
||||
|
||||
Reference in New Issue
Block a user