intermittent commit
This commit is contained in:
@@ -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())
|
||||
|
||||
|
||||
Reference in New Issue
Block a user