Add Discord-native hybrid front-end for Jarvis (bot + bridge)
Some checks failed
Release / semantic-release (push) Successful in 59s
tests / Unit tests (Linux, Python 3.11) (push) Successful in 13m45s
Release / build-linux (push) Failing after 7m47s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled
Some checks failed
Release / semantic-release (push) Successful in 59s
tests / Unit tests (Linux, Python 3.11) (push) Successful in 13m45s
Release / build-linux (push) Failing after 7m47s
Release / build-windows (push) Has been cancelled
Release / build-macos (arm64, macos-latest) (push) Has been cancelled
Release / build-macos (x64, macos-15-intel) (push) Has been cancelled
Release / release-main (push) Has been cancelled
Release / release-develop (push) Has been cancelled
Transform isair/jarvis into a Discord-controlled voice assistant running on the Ubuntu VNC desktop, keeping the mature ~39k-line Python brain intact. - bot/ (Node + bun, discord.js): /자비스 slash commands (ephemeral), voice channel join + voice receive/playback, pluggable VNC screen broadcast (selfbot live / noVNC / screenshot) - bridge/ (Python, Flask): wraps jarvis STT + run_reply_engine + Piper TTS behind a thin localhost HTTP API - .env.example, scripts/ (start_bridge/start_bot/dev), README rewrite, docs/language-comparison.md and docs/vnc-xfce-setup.md Language decision: hybrid (Python brain + Node/bun Discord layer) because Discord blocks bot video; native screen broadcast only works via a Node selfbot library.
This commit is contained in:
122
tests/test_mcp_integration.py
Normal file
122
tests/test_mcp_integration.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Integration tests for MCP tools in the reply engine.
|
||||
|
||||
These tests require more complex setup and may not run in basic CI environments.
|
||||
They can be run locally or in development environments with git hooks.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import Mock, patch
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_mcp_tools_integrated_with_reply_engine():
|
||||
"""Test that MCP tools are properly integrated with the reply engine's tool discovery."""
|
||||
from jarvis.tools.registry import discover_mcp_tools, generate_tools_description, BUILTIN_TOOLS
|
||||
|
||||
# Mock MCP client
|
||||
class FakeMCPClient:
|
||||
def __init__(self, config):
|
||||
pass
|
||||
|
||||
def list_tools(self, server_name):
|
||||
if server_name == "test-server":
|
||||
return [
|
||||
{"name": "tool1", "description": "Test tool 1"},
|
||||
{"name": "tool2", "description": "Test tool 2"}
|
||||
]
|
||||
return []
|
||||
|
||||
with patch('jarvis.tools.registry.MCPClient', FakeMCPClient):
|
||||
# Test discovery
|
||||
mcps_config = {"test-server": {"command": "fake"}}
|
||||
mcp_tools, _errors = discover_mcp_tools(mcps_config)
|
||||
|
||||
# Test tool registration (simulate what reply engine does)
|
||||
allowed_tools = list(BUILTIN_TOOLS.keys())
|
||||
for mcp_tool_name in mcp_tools.keys():
|
||||
if mcp_tool_name not in allowed_tools:
|
||||
allowed_tools.append(mcp_tool_name)
|
||||
|
||||
# Test tool descriptions include MCP tools
|
||||
description = generate_tools_description(allowed_tools, mcp_tools)
|
||||
|
||||
# Assertions
|
||||
assert "test-server__tool1" in allowed_tools
|
||||
assert "test-server__tool2" in allowed_tools
|
||||
assert "test-server__tool1" in description
|
||||
assert "test-server__tool2" in description
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_mcp_tool_execution_in_context():
|
||||
"""Test MCP tool execution with proper context and error handling."""
|
||||
from jarvis.tools.registry import run_tool_with_retries, ToolExecutionResult
|
||||
|
||||
class MockDB:
|
||||
pass
|
||||
|
||||
class MockConfig:
|
||||
def __init__(self):
|
||||
self.mcps = {"test-server": {"command": "fake"}}
|
||||
self.voice_debug = False
|
||||
|
||||
# Mock successful execution
|
||||
class FakeMCPClient:
|
||||
def __init__(self, config):
|
||||
pass
|
||||
|
||||
def invoke_tool(self, server_name, tool_name, arguments):
|
||||
return {"text": f"Executed {tool_name} on {server_name}", "isError": False}
|
||||
|
||||
with patch('jarvis.tools.registry.MCPClient', FakeMCPClient):
|
||||
result = run_tool_with_retries(
|
||||
db=MockDB(),
|
||||
cfg=MockConfig(),
|
||||
tool_name="test-server__example_tool",
|
||||
tool_args={"param": "value"},
|
||||
system_prompt="test",
|
||||
original_prompt="test",
|
||||
redacted_text="test",
|
||||
max_retries=0
|
||||
)
|
||||
|
||||
assert result.success is True
|
||||
assert "Executed example_tool on test-server" in result.reply_text
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
def test_mcp_error_handling_in_context():
|
||||
"""Test that MCP errors are properly handled in execution context."""
|
||||
from jarvis.tools.registry import run_tool_with_retries
|
||||
|
||||
class MockDB:
|
||||
pass
|
||||
|
||||
class MockConfig:
|
||||
def __init__(self):
|
||||
self.mcps = {"test-server": {"command": "fake"}}
|
||||
self.voice_debug = False
|
||||
|
||||
# Mock failing execution
|
||||
class FailingMCPClient:
|
||||
def __init__(self, config):
|
||||
pass
|
||||
|
||||
def invoke_tool(self, server_name, tool_name, arguments):
|
||||
return {"text": "Tool failed", "isError": True}
|
||||
|
||||
with patch('jarvis.tools.registry.MCPClient', FailingMCPClient):
|
||||
result = run_tool_with_retries(
|
||||
db=MockDB(),
|
||||
cfg=MockConfig(),
|
||||
tool_name="test-server__failing_tool",
|
||||
tool_args={},
|
||||
system_prompt="test",
|
||||
original_prompt="test",
|
||||
redacted_text="test",
|
||||
max_retries=0
|
||||
)
|
||||
|
||||
assert result.success is False
|
||||
assert result.error_message == "Tool failed"
|
||||
Reference in New Issue
Block a user