This commit is contained in:
86cloudyun-afk 2026-04-24 17:25:34 -05:00 committed by GitHub
commit 0f3be6640c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 55 additions and 3 deletions

View file

@ -374,9 +374,16 @@ class WhatsAppAdapter(BasePlatformAdapter):
logger.warning("[%s] Could not acquire session lock (non-fatal): %s", self.name, e)
try:
# Auto-install npm dependencies if node_modules doesn't exist
# Auto-install npm dependencies if the bridge install is incomplete
bridge_dir = bridge_path.parent
if not (bridge_dir / "node_modules").exists():
node_modules_dir = bridge_dir / "node_modules"
dependency_sentinels = [
node_modules_dir / "@whiskeysockets" / "baileys" / "package.json",
node_modules_dir / "express" / "package.json",
node_modules_dir / "qrcode-terminal" / "package.json",
node_modules_dir / "pino" / "package.json",
]
if not node_modules_dir.exists() or any(not sentinel.exists() for sentinel in dependency_sentinels):
print(f"[{self.name}] Installing WhatsApp bridge dependencies...")
try:
install_result = subprocess.run(

View file

@ -1324,7 +1324,15 @@ def cmd_whatsapp(args):
print(f"\n✗ Bridge script not found at {bridge_script}")
return
if not (bridge_dir / "node_modules").exists():
node_modules_dir = bridge_dir / "node_modules"
dependency_sentinels = [
node_modules_dir / "@whiskeysockets" / "baileys" / "package.json",
node_modules_dir / "express" / "package.json",
node_modules_dir / "qrcode-terminal" / "package.json",
node_modules_dir / "pino" / "package.json",
]
if not node_modules_dir.exists() or any(not sentinel.exists() for sentinel in dependency_sentinels):
print("\n→ Installing WhatsApp bridge dependencies (this can take a few minutes)...")
npm = shutil.which("npm")
if not npm:

View file

@ -214,6 +214,43 @@ class TestFileHandleClosedOnError:
class TestConnectCleanup:
"""Verify failure paths release the scoped session lock."""
@pytest.mark.asyncio
async def test_releases_lock_when_baileys_package_is_missing(self):
adapter = _make_adapter()
def _path_exists(path_obj):
path_str = str(path_obj).replace("\\", "/")
if path_str.endswith("node_modules"):
return True
if path_str.endswith("@whiskeysockets/baileys/package.json"):
return False
return True
install_result = MagicMock(returncode=0, stderr="")
mock_proc = MagicMock()
mock_proc.poll.return_value = 1
mock_proc.returncode = 1
mock_fh = MagicMock()
with (
patch("gateway.platforms.whatsapp.check_whatsapp_requirements", return_value=True),
patch.object(Path, "exists", autospec=True, side_effect=_path_exists),
patch.object(Path, "mkdir", return_value=None),
patch("subprocess.run", return_value=install_result) as mock_run,
patch("subprocess.Popen", return_value=mock_proc),
patch("builtins.open", return_value=mock_fh),
patch("gateway.platforms.whatsapp.asyncio.sleep", new_callable=AsyncMock),
patch("gateway.platforms.whatsapp.asyncio.create_task"),
patch("gateway.status.acquire_scoped_lock", return_value=(True, None)),
patch("gateway.status.release_scoped_lock") as mock_release,
):
result = await adapter.connect()
assert result is False
assert any(call.args and call.args[0][:2] == ["npm", "install"] for call in mock_run.call_args_list)
mock_release.assert_called_once_with("whatsapp-session", str(adapter._session_path))
assert adapter._platform_lock_identity is None
@pytest.mark.asyncio
async def test_releases_lock_when_npm_install_fails(self):
adapter = _make_adapter()