from __future__ import annotations import tempfile from pathlib import Path import pytest from yuxi.channel.domain.model.plugin_registry.plugin_source import PluginOrigin from yuxi.channel.infrastructure.plugin.discovery import PluginDiscovery from yuxi.channel.infrastructure.plugin.manifest_parser import ManifestParser class TestPluginDiscovery: @pytest.fixture def discovery(self): return PluginDiscovery() @pytest.mark.asyncio async def test_discover_workspace_empty_dir(self, discovery): with tempfile.TemporaryDirectory() as tmpdir: candidates = await discovery.discover_workspace(tmpdir) assert candidates == [] @pytest.mark.asyncio async def test_discover_workspace_skips_no_manifest(self, discovery): with tempfile.TemporaryDirectory() as tmpdir: Path(tmpdir, "plugin1").mkdir() candidates = await discovery.discover_workspace(tmpdir) assert candidates == [] @pytest.mark.asyncio async def test_discover_workspace_skips_hidden_dirs(self, discovery): with tempfile.TemporaryDirectory() as tmpdir: Path(tmpdir, ".hidden").mkdir() Path(tmpdir, "_private").mkdir() candidates = await discovery.discover_workspace(tmpdir) assert candidates == [] @pytest.mark.asyncio async def test_discover_workspace_finds_manifest(self, discovery): with tempfile.TemporaryDirectory() as tmpdir: plugin_dir = Path(tmpdir, "my_plugin") plugin_dir.mkdir() manifest_path = plugin_dir / "yuxi.plugin.yaml" manifest_path.write_text( "id: my_plugin\nname: My Plugin\nversion: 1.0.0\nchannel_type: test\nentry: adapter.py\n" ) candidates = await discovery.discover_workspace(tmpdir) assert len(candidates) == 1 assert candidates[0].plugin_id == "my_plugin" assert candidates[0].source.origin == PluginOrigin.WORKSPACE assert candidates[0].source.entry_file.endswith("adapter.py") @pytest.mark.asyncio async def test_discover_all_combines_sources(self, discovery): with tempfile.TemporaryDirectory() as ws_dir, tempfile.TemporaryDirectory() as inst_dir: for d, name in [(ws_dir, "ws_plugin"), (inst_dir, "inst_plugin")]: plugin_dir = Path(d, name) plugin_dir.mkdir() manifest_path = plugin_dir / "yuxi.plugin.yaml" manifest_path.write_text(f"id: {name}\nname: {name.capitalize()}\nversion: 1.0.0\nchannel_type: test\n") discovery_ws = PluginDiscovery() candidates_ws = await discovery_ws.discover_workspace(ws_dir) candidates_inst = await discovery_ws.discover_installed(inst_dir) assert len(candidates_ws) == 1 assert len(candidates_inst) == 1 @pytest.mark.asyncio async def test_find_existing_plugin(self, discovery): with tempfile.TemporaryDirectory() as tmpdir: plugin_dir = Path(tmpdir, "target") plugin_dir.mkdir() manifest_path = plugin_dir / "yuxi.plugin.yaml" manifest_path.write_text("id: target\nname: Target\nversion: 1.0.0\nchannel_type: test\n") result = await discovery.find("target") assert result is None result = await PluginDiscovery().discover_workspace(tmpdir) assert len(result) == 1 @pytest.mark.asyncio async def test_find_nonexistent_plugin(self, discovery): result = await discovery.find("nonexistent") assert result is None