intermittent commit

This commit is contained in:
2026-07-14 13:22:10 +02:00
parent 8aa1943581
commit e6f7c45f0a
76 changed files with 6039 additions and 2188 deletions

View File

@@ -14,7 +14,6 @@ import tempfile
import textwrap
import tomllib
import unittest
import urllib.error
from dataclasses import replace
from pathlib import Path
from types import SimpleNamespace
@@ -35,6 +34,7 @@ from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
from govoplan_core.core.events import event_context
from govoplan_core.core import module_installer as module_installer_module
from govoplan_core.core.migrations import migration_metadata_plan
from govoplan_core.core.module_management import (
ModuleInstallPlan,
@@ -227,7 +227,7 @@ class ModuleSystemTests(unittest.TestCase):
self.assertTrue({"access", "admin", "tenancy", "policy", "audit", "dashboard", "files", "mail", "campaigns", "docs", "ops"}.issubset(manifests))
self.assertEqual(manifests["campaigns"].dependencies, ())
self.assertTrue(manifests["campaigns"].required_capabilities)
self.assertEqual(manifests["campaigns"].optional_dependencies, ("files", "mail"))
self.assertEqual(manifests["campaigns"].optional_dependencies, ("files", "mail", "notifications", "addresses"))
self.assertEqual(manifests["dashboard"].dependencies, ())
self.assertTrue(manifests["dashboard"].required_capabilities)
self.assertEqual(manifests["docs"].dependencies, ())
@@ -272,14 +272,16 @@ class ModuleSystemTests(unittest.TestCase):
self.assertTrue(scopes_grant_compatible(["admin:users:read"], "access:membership:read"))
self.assertTrue(scopes_grant_compatible(["access:tenant:read"], "system:tenants:read"))
self.assertTrue(scopes_grant_compatible(["system:*"], "access:tenant:read"))
self.assertFalse(scopes_grant_compatible(["tenant:*"], "system:tenants:read"))
self.assertFalse(scopes_grant_compatible(["tenant:*"], "access:tenant:read"))
def test_core_webui_retired_legacy_admin_api_surface(self) -> None:
webui_src = Path(__file__).resolve().parents[1] / "webui" / "src"
legacy_admin_import_pattern = re.compile(r"api/admin(?=[\"'#?]|$)")
self.assertFalse((webui_src / "api" / "admin.ts").exists())
for path in webui_src.rglob("*.ts*"):
with self.subTest(path=path.relative_to(webui_src)):
self.assertNotIn("api/admin", path.read_text(encoding="utf-8"))
self.assertIsNone(legacy_admin_import_pattern.search(path.read_text(encoding="utf-8")))
def test_platform_modules_own_live_legacy_model_tables(self) -> None:
from govoplan_admin.backend.db.models import GovernanceTemplate
@@ -2474,7 +2476,8 @@ finally:
Base.metadata.create_all(bind=database.engine, tables=[SystemSettings.__table__])
def fake_run(*_args, **kwargs):
if kwargs.get("shell"):
argv = tuple(_args[0]) if _args else ()
if kwargs.get("shell") or argv == ("restart-fails",):
return SimpleNamespace(returncode=1, stdout="", stderr="restart failed")
return SimpleNamespace(returncode=0, stdout="", stderr="")
@@ -2562,6 +2565,43 @@ finally:
self.assertEqual(database_url, (result.record_path.parent / database_backup["database_url_secret"]).read_text(encoding="utf-8"))
self.assertEqual("backup", (result.record_path.parent / "database.external.backup").read_text(encoding="utf-8"))
def test_module_installer_rejects_unsafe_external_database_hook_command(self) -> None:
root = Path(tempfile.mkdtemp(prefix="govoplan-installer-unsafe-hook-", dir=_TEST_ROOT))
settings = _settings(root)
configure_database(settings.database_url)
database = get_database()
Base.metadata.create_all(bind=database.engine, tables=[SystemSettings.__table__])
backup_command = f"{sys.executable} -c {shlex.quote('print(1)')} && {sys.executable} -c {shlex.quote('print(2)')}"
with database.session() as session:
save_maintenance_mode(session, MaintenanceMode(enabled=True))
plan = save_module_install_plan(session, [{
"module_id": "files",
"action": "install",
"python_package": "govoplan-files",
"python_ref": "govoplan-files==0.1.4",
}])
session.commit()
with self.assertRaisesRegex(module_installer_module.ModuleInstallerError, "Database backup command failed"):
run_module_install_plan(
session=session,
plan=plan,
available=available_module_manifests(),
current_enabled=("tenancy", "access"),
desired_enabled=("tenancy", "access"),
database_url="postgresql://db.example.invalid/govoplan",
runtime_dir=root / "installer",
migrate_database=True,
database_backup_command=backup_command,
database_restore_command="restore-database",
dry_run=True,
)
rejected = module_installer_module._run_operator_command("DATABASE_URL=postgres://db.example.invalid/govoplan pg_dump")
self.assertEqual(2, rejected.returncode)
self.assertIn("Environment assignments", rejected.stderr)
def test_module_installer_external_database_restore_check_runs_before_migrations(self) -> None:
root = Path(tempfile.mkdtemp(prefix="govoplan-installer-restore-check-", dir=_TEST_ROOT))
settings = _settings(root)
@@ -2654,11 +2694,13 @@ finally:
queued = queue_module_installer_request(
runtime_dir=runtime_dir,
requested_by="user-1",
tenant_id="tenant-1",
options={"migrate_database": True, "health_urls": ["http://127.0.0.1:8000/health"]},
)
request_id = str(queued["request_id"])
self.assertEqual("queued", queued["status"])
self.assertEqual("tenant-1", queued["tenant_id"])
self.assertEqual("installer-trace-1", queued["trace"]["correlation_id"])
self.assertEqual((request_id,), tuple(item["request_id"] for item in list_module_installer_requests(runtime_dir=runtime_dir)))
claimed = claim_next_module_installer_request(runtime_dir=runtime_dir)
@@ -2675,7 +2717,7 @@ finally:
self.assertEqual("completed", updated["status"])
self.assertEqual("completed", read_module_installer_request(runtime_dir=runtime_dir, request_id=request_id)["status"])
cancellable = queue_module_installer_request(runtime_dir=runtime_dir, requested_by="user-1")
cancellable = queue_module_installer_request(runtime_dir=runtime_dir, requested_by="user-1", tenant_id="tenant-1")
cancelled = cancel_module_installer_request(
runtime_dir=runtime_dir,
request_id=str(cancellable["request_id"]),
@@ -2691,6 +2733,7 @@ finally:
)
self.assertEqual("queued", retry["status"])
self.assertEqual(cancellable["request_id"], retry["retry_of"])
self.assertEqual("tenant-1", retry["tenant_id"])
settings = _settings(root)
configure_database(settings.database_url)
@@ -3030,11 +3073,11 @@ finally:
"version_min": "0.1.0",
"version_max_exclusive": "0.2.0",
}, modules["campaigns"]["requires_interfaces"])
self.assertEqual(["files", "mail"], modules["campaigns"]["optional_dependencies"])
self.assertEqual(["files", "mail", "notifications", "addresses"], modules["campaigns"]["optional_dependencies"])
self.assertEqual("requires_review", modules["files"]["migration_safety"])
self.assertIn("migration", modules["files"]["migration_notes"].lower())
self.assertEqual("0.1.7", modules["files"]["version"])
self.assertIn("@v0.1.7", modules["files"]["python_ref"])
self.assertEqual("0.1.8", modules["files"]["version"])
self.assertIn("@v0.1.8", modules["files"]["python_ref"])
def test_module_package_catalog_validates_remote_url_and_cache_fallback(self) -> None:
root = Path(tempfile.mkdtemp(prefix="govoplan-module-package-catalog-remote-", dir=_TEST_ROOT))
@@ -3067,16 +3110,6 @@ finally:
)
body = signed_path.read_text(encoding="utf-8")
class _Response:
def __enter__(self):
return self
def __exit__(self, *args):
return False
def read(self) -> bytes:
return body.encode("utf-8")
env = {
"GOVOPLAN_MODULE_PACKAGE_CATALOG_URL": "https://catalog.example.invalid/stable.json",
"GOVOPLAN_MODULE_PACKAGE_CATALOG_CACHE": str(cache_path),
@@ -3085,11 +3118,11 @@ finally:
}
trusted_keys = {"release-remote": base64.b64encode(public_key).decode("ascii")}
with patch.dict(os.environ, env):
with patch("govoplan_core.core.module_package_catalog.urllib.request.urlopen", return_value=_Response()):
with patch("govoplan_core.core.module_package_catalog.fetch_http_text", return_value=body):
fetched = validate_module_package_catalog(trusted_keys=trusted_keys)
with patch(
"govoplan_core.core.module_package_catalog.urllib.request.urlopen",
side_effect=urllib.error.URLError("offline"),
"govoplan_core.core.module_package_catalog.fetch_http_text",
side_effect=OSError("offline"),
):
cached = validate_module_package_catalog(trusted_keys=trusted_keys)
@@ -3800,12 +3833,14 @@ finally:
self._run_physical_absence_probe(enabled_modules=enabled_modules, blocked_modules=blocked_modules)
def test_module_route_factories_receive_runtime_settings(self) -> None:
app, settings = self._app_for_modules(("files", "mail"))
app, settings = self._app_for_modules(("calendar", "files", "mail"))
self.assertIsNotNone(app)
from govoplan_calendar.backend.runtime import get_settings as get_calendar_settings
from govoplan_files.backend.runtime import get_settings as get_files_settings
from govoplan_mail.backend.runtime import get_settings as get_mail_settings
self.assertIs(settings, get_calendar_settings())
self.assertIs(settings, get_files_settings())
self.assertIs(settings, get_mail_settings())