diff --git a/ods/extensions/services/dashboard-api/config.py b/ods/extensions/services/dashboard-api/config.py index a49b57c12..5dc8ba0f5 100644 --- a/ods/extensions/services/dashboard-api/config.py +++ b/ods/extensions/services/dashboard-api/config.py @@ -535,9 +535,20 @@ def _load_core_service_ids() -> frozenset: core_ids_path = Path(INSTALL_DIR) / "config" / "core-service-ids.json" if core_ids_path.exists(): try: - return frozenset(json.loads(core_ids_path.read_text(encoding="utf-8"))) - except (json.JSONDecodeError, OSError): - pass + payload = json.loads(core_ids_path.read_text(encoding="utf-8")) + if ( + not isinstance(payload, list) + or not payload + or any(not isinstance(item, str) or not item for item in payload) + ): + raise ValueError("core service IDs must be a non-empty string list") + return frozenset(payload) + except (json.JSONDecodeError, OSError, ValueError) as exc: + logger.warning( + "Invalid core service ID registry at %s: %s; using defaults", + core_ids_path, + exc, + ) # Fallback to hardcoded list return frozenset({ "dashboard-api", "dashboard", "llama-server", "model-router", "open-webui", diff --git a/ods/extensions/services/dashboard-api/tests/test_config.py b/ods/extensions/services/dashboard-api/tests/test_config.py index e1e0adfe3..6f1c52fa7 100644 --- a/ods/extensions/services/dashboard-api/tests/test_config.py +++ b/ods/extensions/services/dashboard-api/tests/test_config.py @@ -102,6 +102,32 @@ def test_extension_catalog_skips_non_object_entries(monkeypatch, tmp_path): monkeypatch.setattr(config, "CATALOG_PATH", catalog) assert config.load_extension_catalog() == [valid] +def test_core_service_ids_load_valid_registry(monkeypatch, tmp_path): + registry = tmp_path / "config" / "core-service-ids.json" + registry.parent.mkdir() + registry.write_text( + json.dumps(["custom-core", "dashboard"]), encoding="utf-8" + ) + monkeypatch.setattr(config, "INSTALL_DIR", str(tmp_path)) + + assert config._load_core_service_ids() == frozenset({ + "custom-core", "dashboard", + }) + + +@pytest.mark.parametrize("payload", [{"dashboard": True}, [], [None], "dashboard"]) +def test_core_service_ids_fall_back_for_invalid_registry( + monkeypatch, tmp_path, payload +): + registry = tmp_path / "config" / "core-service-ids.json" + registry.parent.mkdir() + registry.write_text(json.dumps(payload), encoding="utf-8") + monkeypatch.setattr(config, "INSTALL_DIR", str(tmp_path)) + + loaded = config._load_core_service_ids() + + assert "dashboard-api" in loaded + assert "llama-server" in loaded class TestReadManifestFile: