"""Bundled plugin tree must be complete enough for marketplace install and UI.""" from __future__ import annotations import json from pathlib import Path from octop.infra.agents.plugins.bundled import default_bundled_plugins_root from octop.infra.agents.plugins.manager import PluginManager from octop.infra.agents.plugins.seed import seed_bundled_plugins def _catalog_ids() -> set[str]: root = default_bundled_plugins_root() found: set[str] = set() for child in root.iterdir(): if not child.is_dir() or child.name.startswith("_"): continue if (child / "plugin.yaml").is_file(): found.add(child.name) return found def test_bundled_plugin_dirs_have_manifest_and_ui() -> None: root = default_bundled_plugins_root() found = _catalog_ids() assert found, "expected at least one catalog plugin" for name in found: child = root / name assert (child / "main.py").is_file(), child assert (child / "ui" / "index.js").is_file(), child assert (child / "ui" / "manifest.json").is_file(), child def test_seed_does_not_auto_install_real_catalog(tmp_path: Path) -> None: plugins_dir = tmp_path / "plugins" config_path = tmp_path / "config.json" copied = seed_bundled_plugins( bundled_root=default_bundled_plugins_root(), plugins_dir=plugins_dir, config_path=config_path, ) assert copied == [] assert list(plugins_dir.iterdir()) == [] if plugins_dir.exists() else True def test_market_install_copies_from_catalog(tmp_path: Path) -> None: plugins_dir = tmp_path / "plugins" config_path = tmp_path / "config.json" mgr = PluginManager(plugins_dir=plugins_dir, config_path=config_path) catalog = mgr.list_market() assert catalog sample = next(item for item in catalog if not item.get("error")) plugin_id = str(sample["id"]) loaded = mgr.install_from_market(plugin_id) assert loaded.manifest.id == plugin_id assert (plugins_dir / plugin_id / "plugin.yaml").is_file() raw = json.loads(config_path.read_text(encoding="utf-8")) assert raw["plugins"][plugin_id]["enabled"] is True market = mgr.list_market() row = next(item for item in market if item.get("id") == plugin_id) assert row["installed"] is True assert row["update_available"] is False def test_offline_bundled_plugins_load() -> None: from octop_harness.plugins import PluginRegistry, load_plugin_dir PluginRegistry.reset() root = default_bundled_plugins_root() try: for name in ("fortune", "pomodoro", "mini-games", "qrcode", "tetris"): loaded = load_plugin_dir(root / name, install_deps=False) assert loaded.manifest.id == name assert loaded.tools finally: PluginRegistry.reset()