Files
ServerKit/backend/tests/test_email_extraction.py
Juan DenisandClaude Fable 5 43fec8030c cutover(email): serverkit-email leaves the tree (plan 52 Phase 2, D8)
builtin-extensions/serverkit-email + both tracked live copies removed; the
bundled registry entry flips to bundled:false with the local zip's sha256;
the gated auto-install (_email_was_configured) moves from
GATED_BUILTIN_SLUGS to REGISTRY_GATED_SLUGS so mail users still get it back
once the entry is published.

Panel-core tests re-home onto the sibling checkout via the new
conftest.sibling_extension_dir helper (skip cleanly when absent): the
extraction suite installs from the serverkit-email repo, the dovecot-probe
suite loads its module from there. All 39 affected tests green with the
sibling present.

OPERATOR: publish jhd3197/serverkit-email, sign, sync-registry-index.sh.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-29 14:45:22 -04:00

103 lines
4.3 KiB
Python

"""Prove the Email extraction (Phase 4, #35; standalone repo since plan 52 Ph2).
A core panel no longer carries the mail-server API; installing serverkit-email
(now a standalone repo — mounted from the sibling checkout, skip when absent)
registers `/api/v1/email` and its routes respond (which also exercises the
extension's relative-import rewiring). Uninstall removes it again.
"""
import sys
import pytest
import app as app_pkg
from app.models.plugin import InstalledPlugin
from tests.conftest import sibling_extension_dir
from app.services import plugin_service
# Installs plugins, and plugin_service hot-loads their blueprints onto the
# live app. Flask refuses register_blueprint once an app has served a
# request, so these need a private app (plan 64 Phase 1).
pytestmark = pytest.mark.fresh_app
SLUG = 'serverkit-email'
_PKG = f'app.plugins.{SLUG}'
def test_core_has_no_email_routes(app):
"""The mail-server API is gone from core after extraction.
Exception: /api/v1/email/dns-providers* stayed core (moved back after the
extraction broke the Connections DNS tiles on panels without the email
extension) — it keeps the historical path but is not mail-server API.
"""
rules = [r.rule for r in app.url_map.iter_rules()]
email_rules = [r for r in rules if r.startswith('/api/v1/email')]
assert all(r.startswith('/api/v1/email/dns-providers') for r in email_rules)
assert email_rules # the core DNS-provider routes themselves exist
@pytest.fixture
def install_dirs(tmp_path, monkeypatch):
"""Point the install targets at temp dirs AND make app.plugins resolve the
temp backend dir so the hot-loaded blueprint imports from there (not the repo).
"""
backend = tmp_path / 'plugins_backend'
frontend = tmp_path / 'plugins_frontend'
backend.mkdir()
frontend.mkdir()
monkeypatch.setattr(plugin_service, 'BACKEND_PLUGINS_DIR', str(backend))
monkeypatch.setattr(plugin_service, 'FRONTEND_PLUGINS_DIR', str(frontend))
added = str(backend)
import importlib
app_pkg_plugins = importlib.import_module('app.plugins')
if added not in app_pkg_plugins.__path__:
app_pkg_plugins.__path__.append(added)
yield {'backend': backend, 'frontend': frontend}
# Clean the import side effects so other tests see a pristine module graph.
if added in app_pkg_plugins.__path__:
app_pkg_plugins.__path__.remove(added)
for name in list(sys.modules):
if name == _PKG or name.startswith(_PKG + '.'):
del sys.modules[name]
def _install_email_from_sibling(monkeypatch):
src = sibling_extension_dir(SLUG)
if not src:
pytest.skip('serverkit-email checkout not available '
'(set SERVERKIT_EMAIL_DIR to its checkout)')
# The sibling repo pins min_panel_version to the first SDK-1.4.0 release;
# the working tree may still carry the previous version string.
from app.utils import version as version_mod
monkeypatch.setattr(version_mod, 'get_panel_version', lambda: '1.9.11')
monkeypatch.setattr(plugin_service, 'get_panel_version',
lambda: '1.9.11', raising=False)
return plugin_service.install_from_path(src, force=True)
def test_email_is_no_longer_a_builtin(app):
available = {e['slug'] for e in plugin_service.list_builtin_extensions()}
assert SLUG not in available, 'serverkit-email left the tree (plan 52 Ph2)'
def test_install_email_extension_registers_routes(app, client, auth_headers, install_dirs, monkeypatch):
plugin = _install_email_from_sibling(monkeypatch)
assert plugin.status == InstalledPlugin.STATUS_ACTIVE
assert plugin.has_backend is True
assert plugin.url_prefix == '/api/v1/email'
# The blueprint hot-loaded: /api/v1/email/status now exists (not 404) and the
# status guard passes for an active plugin (not 503). Its handler may 200 or
# 500 depending on host mail state — we only assert the route is wired.
resp = client.get('/api/v1/email/status', headers=auth_headers)
assert resp.status_code not in (404, 503), resp.status_code
def test_uninstall_removes_email_plugin(app, install_dirs, monkeypatch):
plugin = _install_email_from_sibling(monkeypatch)
assert plugin_service.uninstall_plugin(plugin.id) is True
assert InstalledPlugin.query.filter_by(slug=SLUG).first() is None