Skip to content
4 changes: 3 additions & 1 deletion plugboard/cli/server/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ async def _post_to_api(url: str, data: dict) -> None:
def _import_recursive(path: Path, base_package: _t.Optional[str] = None) -> None:
"""Import all modules recursively from the given path."""
logger = DI.logger.resolve_sync()
for root, _dirs, files in os.walk(path):
for root, dirs, files in os.walk(path):
# Update dirs in place so os.walk skips hidden directories like .venv.
Comment thread
toby-coleman marked this conversation as resolved.
dirs[:] = [directory for directory in dirs if not directory.startswith(".")]
for file in files:
if file.endswith(".py") and not file.startswith("__"):
# Construct module name
Expand Down
50 changes: 50 additions & 0 deletions tests/unit/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
marked async so that they do not interfere with pytest-asyncio's event loop.
"""

import json
from pathlib import Path
import tempfile
import textwrap
import typing as _t
from unittest.mock import AsyncMock, MagicMock, patch

Expand Down Expand Up @@ -227,6 +229,54 @@ def test_cli_server_discover(test_project_dir: Path) -> None:
assert process_route.called


def test_cli_server_discover_ignores_hidden_directories(tmp_path: Path) -> None:
Comment thread
toby-coleman marked this conversation as resolved.
Outdated
"""Tests the server discover command ignores hidden directories like .venv."""
project_dir = tmp_path / "test_project"
project_dir.mkdir()
(project_dir / "test_file.py").write_text(
textwrap.dedent("""
from plugboard.component import Component, IOController as IO


class VisibleComponent(Component):
io = IO(outputs=["out"])

async def step(self) -> None:
self.out = 1
""").strip()
)
hidden_dir = project_dir / ".venv"
hidden_dir.mkdir()
(hidden_dir / "bad_module.py").write_text('raise RuntimeError("should not import")')

with respx.mock:
component_route = respx.post("http://test:8000/types/component").respond(
json={"status": "ok"}
)
respx.post("http://test:8000/types/connector").respond(json={"status": "ok"})
respx.post("http://test:8000/types/event").respond(json={"status": "ok"})
respx.post("http://test:8000/types/process").respond(json={"status": "ok"})

result = runner.invoke(
app,
[
"server",
"discover",
str(project_dir),
"--api-url",
"http://test:8000",
],
)

assert result.exit_code == 0
assert result.exception is None
assert "Discovery complete" in result.stdout
assert any(
json.loads(call.request.content)["name"] == "VisibleComponent"
for call in component_route.calls
)


def test_cli_server_discover_with_env_var(test_project_dir: Path) -> None:
"""Tests the server discover command with environment variable."""
with respx.mock:
Expand Down