Release v0.1.16
Module Package Release / publish-packages (push) Successful in 12s

This commit is contained in:
2026-08-05 19:53:46 +02:00
parent add7a99f6d
commit f1a5be2a93
35 changed files with 1467 additions and 195 deletions
+43
View File
@@ -184,6 +184,12 @@ class DatabaseMigrationTests(unittest.TestCase):
system_settings_count = connection.execute(
text("SELECT COUNT(*) FROM core_system_settings WHERE id = 'global'"),
).scalar_one()
default_locale = connection.execute(
text(
"SELECT default_locale FROM core_system_settings "
"WHERE id = 'global'"
),
).scalar_one()
current = database_migration_heads(connection)
self.assertEqual(
@@ -204,6 +210,7 @@ class DatabaseMigrationTests(unittest.TestCase):
self.assertIn("file_assets", tables)
self.assertIn("mail_server_profiles", tables)
self.assertEqual(system_settings_count, 1)
self.assertEqual(default_locale, "de")
self.assertEqual(
role_flags,
{"system_owner": True, "system_admin": False, "system_auditor": False},
@@ -211,6 +218,42 @@ class DatabaseMigrationTests(unittest.TestCase):
finally:
engine.dispose()
def test_german_reference_migration_preserves_explicit_english(self) -> None:
with tempfile.TemporaryDirectory(prefix="govoplan-locale-migration-test-") as directory:
database = Path(directory) / "locale.db"
url = f"sqlite:///{database}"
config = alembic_config(database_url=url, enabled_modules=())
command.upgrade(config, "f25c9d3e7a01")
engine = create_engine(url)
try:
with engine.begin() as connection:
connection.execute(
text(
"UPDATE core_system_settings "
"SET default_locale = 'en', "
"updated_at = '2040-01-01 00:00:00' "
"WHERE id = 'global'"
)
)
finally:
engine.dispose()
command.upgrade(config, "heads")
engine = create_engine(url)
try:
with engine.connect() as connection:
default_locale = connection.execute(
text(
"SELECT default_locale FROM core_system_settings "
"WHERE id = 'global'"
)
).scalar_one()
self.assertEqual(default_locale, "en")
finally:
engine.dispose()
def test_default_module_baselines_apply_to_fresh_database(self) -> None:
with tempfile.TemporaryDirectory(prefix="govoplan-default-baseline-test-") as directory:
database = Path(directory) / "default.db"
+29
View File
@@ -0,0 +1,29 @@
from __future__ import annotations
import unittest
from govoplan_core.i18n import (
REFERENCE_LANGUAGE_CODE,
normalize_enabled_language_codes,
normalize_language_packages,
preferred_language_code,
system_i18n_payload,
)
class I18nReferenceLanguageTests(unittest.TestCase):
def test_german_is_the_new_installation_reference_default(self) -> None:
packages = normalize_language_packages()
self.assertEqual("de", REFERENCE_LANGUAGE_CODE)
self.assertEqual(["de", "en"], [item["code"] for item in packages])
self.assertEqual(
["de", "en"],
normalize_enabled_language_codes(None, packages),
)
self.assertEqual("de", system_i18n_payload(None)["default_language"])
self.assertEqual("de", preferred_language_code(None, ()))
if __name__ == "__main__":
unittest.main()
+68
View File
@@ -0,0 +1,68 @@
from __future__ import annotations
import unittest
from govoplan_core.core.information_governance import (
InformationGovernanceDeclarationError,
InformationGovernanceDimension,
ModuleInformationGovernance,
information_governance_from_mapping,
information_governance_maturity_issues,
)
class InformationGovernanceTests(unittest.TestCase):
def test_default_applies_contract_without_claiming_enforcement(self) -> None:
declaration = ModuleInformationGovernance()
self.assertTrue(declaration.current_authorization_for_historical_reads)
self.assertEqual(
{"contract_only"},
{item.adoption for item in declaration.dimensions.values()},
)
self.assertEqual("1", declaration.to_dict()["contract_version"])
def test_enforced_adoption_requires_evidence(self) -> None:
with self.assertRaisesRegex(
InformationGovernanceDeclarationError,
"requires evidence",
):
InformationGovernanceDimension(adoption="enforced")
with self.assertRaisesRegex(
InformationGovernanceDeclarationError,
"covered object types",
):
InformationGovernanceDimension(
adoption="enforced",
evidence=("tests/test_information_governance.py",),
)
def test_catalog_shape_round_trips(self) -> None:
declaration = ModuleInformationGovernance()
self.assertEqual(
declaration,
information_governance_from_mapping(declaration.to_dict()),
)
def test_reference_ready_rejects_unproved_dimensions(self) -> None:
issues = information_governance_maturity_issues(
ModuleInformationGovernance(),
maturity="reference_ready",
)
self.assertEqual(1, len(issues))
self.assertIn("temporal_browsing", issues[0])
self.assertIn("purpose_aware_access", issues[0])
self.assertEqual(
(),
information_governance_maturity_issues(
ModuleInformationGovernance(),
maturity="vertical_slice",
),
)
if __name__ == "__main__":
unittest.main()
+74 -1
View File
@@ -106,6 +106,7 @@ from govoplan_core.core.module_package_catalog import (
sign_module_package_catalog,
validate_module_package_catalog,
)
from govoplan_core.core.information_governance import ModuleInformationGovernance
from govoplan_core.core.modules import DocumentationTopic, FrontendModule, FrontendRoute, MigrationRetirementPlan, ModuleCompatibility, ModuleMigrationTask, ModuleMigrationTaskContext, ModuleMigrationTaskResult, ModuleUninstallGuardResult, PublicFrontendRoute
from govoplan_core.core.module_guards import drop_table_retirement_provider
from govoplan_core.core.modules import MigrationSpec, ModuleInterfaceProvider, ModuleInterfaceRequirement, ModuleManifest, PermissionDefinition, RoleTemplate
@@ -119,7 +120,8 @@ from govoplan_core.security.module_permissions import scopes_grant_compatible
from govoplan_core.security.permissions import scope_grants
from govoplan_core.server.app import create_app
from govoplan_core.server.config import GovoplanServerConfig
from govoplan_core.server.platform import create_platform_router
from govoplan_core.server.platform import _documentation_help_contexts, create_platform_router
from govoplan_core.core.views import ViewSurface
from govoplan_core.server.registry import available_module_manifests, build_platform_registry
from govoplan_core.server.route_validation import RouteCollisionError
from govoplan_core.tenancy.scope import Tenant, create_scope_tables
@@ -597,6 +599,70 @@ class ModuleSystemTests(unittest.TestCase):
[item["path"] for item in public_module["frontend"]["public_routes"]],
)
def test_declared_surfaces_receive_static_documentation_fallbacks(self) -> None:
manifest = ModuleManifest(
id="example",
name="Example",
version="test",
documentation=(
DocumentationTopic(
id="example.user",
title="Example user guide",
summary="User guide.",
documentation_types=("user",),
order=20,
),
DocumentationTopic(
id="example.admin",
title="Example administrator guide",
summary="Administrator guide.",
documentation_types=("admin",),
order=10,
),
),
frontend=FrontendModule(
module_id="example",
routes=(
FrontendRoute(
path="/example",
component="ExamplePage",
surface_id="example.workspace",
),
),
settings_routes=(
FrontendRoute(
path="/admin?section=example",
component="ExampleAdmin",
surface_id="example.admin.settings",
),
),
view_surfaces=(
ViewSurface(
id="example.workspace",
module_id="example",
kind="route",
label="Example workspace",
),
ViewSurface(
id="example.admin.settings",
module_id="example",
kind="section",
label="Example settings",
),
),
),
)
contexts = {
item["id"]: item for item in _documentation_help_contexts(manifest)
}
self.assertEqual("example.user", contexts["example.workspace"]["topic_id"])
self.assertEqual(
"example.admin",
contexts["example.admin.settings"]["topic_id"],
)
def test_registry_rejects_duplicate_public_frontend_routes(self) -> None:
registry = PlatformRegistry()
for module_id in ("first", "second"):
@@ -3300,6 +3366,7 @@ finally:
"recovery_notes": "Restore rehearsal completed for the release candidate.",
"provides_interfaces": [{"name": "files.spaces", "version": "1.2.0"}],
"requires_interfaces": [{"name": "access.directory", "version_min": "1.0.0", "optional": True}],
"information_governance": ModuleInformationGovernance().to_dict(),
"artifact_integrity": {
"python": {
"ref": "govoplan-files==0.1.4",
@@ -3345,6 +3412,12 @@ finally:
[{"name": "access.directory", "optional": True, "version_min": "1.0.0"}],
catalog[0]["requires_interfaces"],
)
self.assertEqual(
"contract_only",
catalog[0]["information_governance"]["dimensions"]["retention"][
"adoption"
],
)
self.assertEqual("0" * 64, catalog[0]["artifact_integrity"]["python"]["sha256"])
validation = validate_module_package_catalog(catalog_path)
+16
View File
@@ -40,6 +40,22 @@ class _Consumer:
self.added.append(queue)
def test_api_runtime_identity_uses_the_installed_distribution_version(
monkeypatch,
) -> None:
from govoplan_core.server import default_config
monkeypatch.setattr(
default_config,
"version",
lambda distribution: "0.1.15"
if distribution == "govoplan-core"
else "unexpected",
)
assert default_config._core_distribution_version() == "0.1.15"
def test_api_runtime_agent_fails_readiness_on_heartbeat_error() -> None:
agent = RuntimeNodeAgent(
settings=SimpleNamespace(
+23 -1
View File
@@ -1,12 +1,34 @@
from __future__ import annotations
import unittest
from unittest.mock import patch
from types import SimpleNamespace
from unittest.mock import MagicMock, patch
from govoplan_core.commands.wait_for_database import main
from govoplan_core.db.migrations import configured_migration_heads
class WaitForDatabaseTests(unittest.TestCase):
def test_configured_heads_resolve_cross_branch_dependencies(self) -> None:
scripts = MagicMock()
scripts.get_revisions.return_value = (
SimpleNamespace(revision="head-b"),
SimpleNamespace(revision="head-a"),
)
with (
patch("govoplan_core.db.migrations.alembic_config"),
patch(
"govoplan_core.db.migrations.ScriptDirectory.from_config",
return_value=scripts,
),
):
heads = configured_migration_heads("sqlite://")
self.assertEqual(("head-a", "head-b"), heads)
scripts.get_revisions.assert_called_once_with("heads")
scripts.get_heads.assert_not_called()
def test_waits_until_exact_configured_heads_are_visible(self) -> None:
with (
patch(