fix: rename src/websocket to src/ws_manager to avoid websocket-client clash

selenium depends on websocket-client which installs a top-level
`websocket` package that shadows our src/websocket/ module on CI.
Renaming to ws_manager eliminates the conflict entirely — no more
sys.path hacks needed in conftest or Selenium tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alexander Payne
2026-02-25 07:57:28 -05:00
parent e483748816
commit 3463f4e4a4
11 changed files with 18 additions and 43 deletions

View File

@@ -41,7 +41,7 @@ src/
lightning/ # Lightning backend abstraction (mock + LND)
agent_core/ # Substrate-agnostic agent interface
voice/ # NLU intent detection (regex-based, no cloud)
websocket/ # WebSocket manager (ws_manager singleton)
ws_manager/ # WebSocket manager (ws_manager singleton)
notifications/ # Push notification store (notifier singleton)
shortcuts/ # Siri Shortcuts API endpoints
telegram_bot/ # Telegram bridge
@@ -256,7 +256,7 @@ runner.stop(info["container_id"])
```python
from dashboard.store import message_log
from notifications.push import notifier
from websocket.handler import ws_manager
from ws_manager.handler import ws_manager
from timmy_serve.payment_handler import payment_handler
from swarm.coordinator import coordinator
```

View File

@@ -63,7 +63,7 @@ src/
lightning/ # Lightning backend abstraction (mock + LND)
agent_core/ # Substrate-agnostic agent interface
voice/ # NLU intent detection (regex-based, local)
websocket/ # WebSocket connection manager (ws_manager singleton)
ws_manager/ # WebSocket connection manager (ws_manager singleton)
notifications/ # Push notification store (notifier singleton)
shortcuts/ # Siri Shortcuts API endpoints
telegram_bot/ # Telegram bridge
@@ -96,7 +96,7 @@ Core services are module-level singleton instances imported directly:
```python
from dashboard.store import message_log
from notifications.push import notifier
from websocket.handler import ws_manager
from ws_manager.handler import ws_manager
from timmy_serve.payment_handler import payment_handler
from swarm.coordinator import coordinator
```

View File

@@ -212,7 +212,7 @@ src/
lightning/ # Lightning backend abstraction (mock + LND)
agent_core/ # Substrate-agnostic agent interface
voice/ # NLU intent detection
websocket/ # WebSocket connection manager
ws_manager/ # WebSocket connection manager
notifications/ # Push notification store
shortcuts/ # Siri Shortcuts endpoints
telegram_bot/ # Telegram bridge

View File

@@ -86,7 +86,7 @@ include = [
"src/config.py",
"src/self_tdd",
"src/swarm",
"src/websocket",
"src/ws_manager",
"src/voice",
"src/notifications",
"src/shortcuts",

View File

@@ -9,7 +9,7 @@ import logging
from fastapi import APIRouter, WebSocket, WebSocketDisconnect
from websocket.handler import ws_manager
from ws_manager.handler import ws_manager
logger = logging.getLogger(__name__)

View File

@@ -6,15 +6,6 @@ import sys
from pathlib import Path
from unittest.mock import MagicMock
# ── Fix websocket-client shadowing project's src/websocket/ ──────────────────
# selenium → websocket-client installs a top-level `websocket` package that
# shadows our src/websocket/ module. Ensure src/ is at the FRONT of sys.path
# so our module wins the import race.
_src = str(Path(__file__).resolve().parent.parent / "src")
if _src in sys.path:
sys.path.remove(_src)
sys.path.insert(0, _src)
import pytest
from fastapi.testclient import TestClient

View File

@@ -6,14 +6,19 @@ Requires:
- selenium pip package
Run:
SELENIUM_UI=1 pytest tests/functional/test_ui_selenium.py -v --override-ini='pythonpath='
SELENIUM_UI=1 pytest tests/functional/test_ui_selenium.py -v
"""
import os
import sys
import time
import pytest
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
# Skip entire module unless SELENIUM_UI=1 is set
pytestmark = pytest.mark.skipif(
@@ -24,27 +29,6 @@ pytestmark = pytest.mark.skipif(
DASHBOARD_URL = os.environ.get("DASHBOARD_URL", "http://localhost:8000")
# ── Prevent src/websocket from shadowing the websocket-client package ────────
# Selenium depends on websocket-client which provides `from websocket import
# WebSocketApp`. The project's src/websocket/ module would shadow that import.
# Remove "src" from sys.path for this module since we don't import project code.
_src_paths = [p for p in sys.path if p.endswith("/src") or p.endswith("\\src")]
for _p in _src_paths:
sys.path.remove(_p)
from selenium import webdriver # noqa: E402
from selenium.webdriver.chrome.options import Options # noqa: E402
from selenium.webdriver.common.by import By # noqa: E402
from selenium.webdriver.common.keys import Keys # noqa: E402
from selenium.webdriver.support import expected_conditions as EC # noqa: E402
from selenium.webdriver.support.ui import WebDriverWait # noqa: E402
# Restore paths so other test modules aren't affected
for _p in _src_paths:
if _p not in sys.path:
sys.path.append(_p)
@pytest.fixture(scope="module")
def driver():
"""Headless Chrome WebDriver, shared across tests in this module."""

View File

@@ -1,10 +1,10 @@
"""Tests for websocket/handler.py — WebSocket manager."""
"""Tests for ws_manager/handler.py — WebSocket manager."""
import json
import pytest
from websocket.handler import WebSocketManager, WSEvent
from ws_manager.handler import WebSocketManager, WSEvent
def test_ws_event_to_json():

View File

@@ -1,4 +1,4 @@
"""Extended tests for websocket/handler.py — broadcast, disconnect, convenience."""
"""Extended tests for ws_manager/handler.py — broadcast, disconnect, convenience."""
import asyncio
import json
@@ -6,7 +6,7 @@ from unittest.mock import AsyncMock, MagicMock
import pytest
from websocket.handler import WebSocketManager, WSEvent
from ws_manager.handler import WebSocketManager, WSEvent
class TestWSEventSerialization: