Expose searchable module catalog compatibility
This commit is contained in:
+138
-1
@@ -5,8 +5,12 @@ from unittest.mock import patch
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from govoplan_admin.backend.api.v1.routes import _catalog_plan_item
|
||||
from govoplan_admin.backend.api.v1.routes import (
|
||||
_catalog_plan_item,
|
||||
_module_package_catalog_item,
|
||||
)
|
||||
from govoplan_admin.backend.api.v1.schemas import SystemSettingsItem
|
||||
from govoplan_core.core.modules import ModuleInterfaceProvider, ModuleManifest
|
||||
|
||||
|
||||
class CatalogPlanItemTests(unittest.TestCase):
|
||||
@@ -114,6 +118,139 @@ class CatalogPlanItemTests(unittest.TestCase):
|
||||
self.assertEqual(missing.exception.status_code, 404)
|
||||
self.assertEqual(missing.exception.detail, "Catalog install/update entry not found: calendar")
|
||||
|
||||
with self.subTest("withdrawn entry"), self.assertRaises(HTTPException) as withdrawn:
|
||||
_catalog_plan_item(
|
||||
"calendar",
|
||||
set(),
|
||||
validation={
|
||||
"valid": True,
|
||||
"modules": [{
|
||||
"module_id": "calendar",
|
||||
"action": "install",
|
||||
"availability": "withdrawn",
|
||||
"availability_reason": "Superseded after a security review.",
|
||||
}],
|
||||
},
|
||||
)
|
||||
self.assertEqual(withdrawn.exception.status_code, 409)
|
||||
self.assertIn("security review", withdrawn.exception.detail)
|
||||
|
||||
def test_catalog_item_reports_dependency_interface_and_update_compatibility(self) -> None:
|
||||
raw_item: dict[str, object] = {
|
||||
"module_id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.2.0",
|
||||
"action": "install",
|
||||
"dependencies": ["access"],
|
||||
"current_version_min": "0.1.5",
|
||||
"current_version_max_exclusive": "0.2.0",
|
||||
"requires_interfaces": [{
|
||||
"name": "files.storage",
|
||||
"version_min": "2.0.0",
|
||||
"optional": False,
|
||||
}],
|
||||
}
|
||||
available = {
|
||||
"calendar": ModuleManifest(id="calendar", name="Calendar", version="0.1.4"),
|
||||
"files": ModuleManifest(
|
||||
id="files",
|
||||
name="Files",
|
||||
version="0.1.0",
|
||||
provides_interfaces=(ModuleInterfaceProvider(name="files.storage", version="1.0.0"),),
|
||||
),
|
||||
}
|
||||
with patch(
|
||||
"govoplan_admin.backend.api.v1.routes.module_license_decision",
|
||||
return_value={"allowed": True, "enforced": False, "missing_features": []},
|
||||
):
|
||||
item = _module_package_catalog_item(raw_item, available, [raw_item])
|
||||
|
||||
self.assertFalse(item.compatible)
|
||||
self.assertFalse(item.plan_allowed)
|
||||
self.assertEqual("blocked", item.catalog_state)
|
||||
self.assertTrue(item.update_available)
|
||||
self.assertEqual(3, len(item.compatibility_reasons))
|
||||
self.assertTrue(any("update window" in reason for reason in item.compatibility_reasons))
|
||||
self.assertTrue(any("Required module access" in reason for reason in item.compatibility_reasons))
|
||||
self.assertTrue(any("Required interface files.storage" in reason for reason in item.compatibility_reasons))
|
||||
|
||||
def test_catalog_item_accepts_dependencies_and_interfaces_from_same_catalog(self) -> None:
|
||||
raw_item: dict[str, object] = {
|
||||
"module_id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.2.0",
|
||||
"action": "install",
|
||||
"dependencies": ["access"],
|
||||
"requires_interfaces": [{
|
||||
"name": "access.directory",
|
||||
"version_min": "1.0.0",
|
||||
"optional": False,
|
||||
}],
|
||||
}
|
||||
provider: dict[str, object] = {
|
||||
"module_id": "access",
|
||||
"availability": "available",
|
||||
"provides_interfaces": [{"name": "access.directory", "version": "1.1.0"}],
|
||||
}
|
||||
with patch(
|
||||
"govoplan_admin.backend.api.v1.routes.module_license_decision",
|
||||
return_value={"allowed": True, "enforced": False, "missing_features": []},
|
||||
):
|
||||
item = _module_package_catalog_item(raw_item, {}, [raw_item, provider])
|
||||
|
||||
self.assertTrue(item.compatible)
|
||||
self.assertTrue(item.plan_allowed)
|
||||
self.assertEqual("available", item.catalog_state)
|
||||
self.assertEqual([], item.compatibility_reasons)
|
||||
|
||||
def test_catalog_item_distinguishes_current_release_and_downgrade(self) -> None:
|
||||
available = {
|
||||
"calendar": ModuleManifest(id="calendar", name="Calendar", version="0.2.0"),
|
||||
}
|
||||
license_decision = {"allowed": True, "enforced": False, "missing_features": []}
|
||||
with patch(
|
||||
"govoplan_admin.backend.api.v1.routes.module_license_decision",
|
||||
return_value=license_decision,
|
||||
):
|
||||
current = _module_package_catalog_item({
|
||||
"module_id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.2.0",
|
||||
"action": "install",
|
||||
}, available)
|
||||
refresh = _module_package_catalog_item({
|
||||
"module_id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.2.0",
|
||||
"action": "install",
|
||||
"allow_same_version": True,
|
||||
}, available)
|
||||
downgrade = _module_package_catalog_item({
|
||||
"module_id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.1.9",
|
||||
"action": "install",
|
||||
}, available)
|
||||
allowed_downgrade = _module_package_catalog_item({
|
||||
"module_id": "calendar",
|
||||
"name": "Calendar",
|
||||
"version": "0.1.9",
|
||||
"action": "install",
|
||||
"allow_downgrade": True,
|
||||
}, available)
|
||||
|
||||
self.assertTrue(current.compatible)
|
||||
self.assertFalse(current.plan_allowed)
|
||||
self.assertFalse(current.update_available)
|
||||
self.assertEqual("installed", current.catalog_state)
|
||||
self.assertTrue(refresh.plan_allowed)
|
||||
self.assertFalse(downgrade.compatible)
|
||||
self.assertFalse(downgrade.plan_allowed)
|
||||
self.assertTrue(any("does not allow a downgrade" in reason for reason in downgrade.compatibility_reasons))
|
||||
self.assertTrue(allowed_downgrade.compatible)
|
||||
self.assertTrue(allowed_downgrade.plan_allowed)
|
||||
self.assertFalse(allowed_downgrade.update_available)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user