Files
javis_bot/tests/test_tools.py
javis-bot c4abf63f38
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
Add Discord-native hybrid front-end for Jarvis (bot + bridge)
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.
2026-06-09 14:51:05 +09:00

259 lines
7.2 KiB
Python

import types
import pytest
from jarvis.tools.registry import run_tool_with_retries, ToolExecutionResult
class DummyCfg:
def __init__(self):
self.voice_debug = False
self.ollama_base_url = "http://localhost"
self.ollama_chat_model = "test"
self.llm_chat_timeout_sec = 5.0
self.location_enabled = False
self.location_ip_address = None
self.location_auto_detect = False
self.use_stdin = True
self.web_search_enabled = False
self.mcps = {}
class DummyDB:
def get_meals_between(self, since, until):
return []
def delete_meal(self, mid: int) -> bool:
return mid == 1
@pytest.mark.unit
def test_delete_meal_success(monkeypatch):
db = DummyDB()
cfg = DummyCfg()
res = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="deleteMeal",
tool_args={"id": 1},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert isinstance(res, ToolExecutionResult)
assert res.success is True
assert "deleted" in (res.reply_text or "").lower()
@pytest.mark.unit
def test_delete_meal_failure(monkeypatch):
db = DummyDB()
cfg = DummyCfg()
res = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="deleteMeal",
tool_args={"id": 2},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res.success is False
@pytest.mark.unit
def test_local_files_list_and_read(tmp_path):
# Arrange
root = tmp_path / "notes"
root.mkdir()
f1 = root / "a.txt"
f2 = root / "b.md"
f1.write_text("hello", encoding="utf-8")
f2.write_text("world", encoding="utf-8")
db = DummyDB()
cfg = DummyCfg()
# Monkeypatch expanduser to point to tmp home
import jarvis.tools.registry as tools_mod
import builtins
from pathlib import Path as _P
orig_expanduser = tools_mod.os.path.expanduser
tools_mod.os.path.expanduser = lambda p: str(tmp_path) if p == "~" or p.startswith("~") else orig_expanduser(p)
try:
# list
res_list = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="localFiles",
tool_args={"operation": "list", "path": "~/notes", "glob": "*.txt", "recursive": False},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res_list.success is True
assert "a.txt" in (res_list.reply_text or "")
# read
res_read = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="localFiles",
tool_args={"operation": "read", "path": "~/notes/a.txt"},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res_read.success is True
assert (res_read.reply_text or "").strip() == "hello"
finally:
tools_mod.os.path.expanduser = orig_expanduser
@pytest.mark.unit
def test_local_files_write_append_delete(tmp_path):
db = DummyDB()
cfg = DummyCfg()
import jarvis.tools.registry as tools_mod
orig_expanduser = tools_mod.os.path.expanduser
tools_mod.os.path.expanduser = lambda p: str(tmp_path) if p == "~" or p.startswith("~") else orig_expanduser(p)
try:
# write
res_write = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="localFiles",
tool_args={"operation": "write", "path": "~/x/y.txt", "content": "abc"},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res_write.success is True
# append
res_append = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="localFiles",
tool_args={"operation": "append", "path": "~/x/y.txt", "content": "def"},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res_append.success is True
# read back
res_read = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="localFiles",
tool_args={"operation": "read", "path": "~/x/y.txt"},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res_read.success is True
assert (res_read.reply_text or "").strip() == "abcdef"
# delete
res_del = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="localFiles",
tool_args={"operation": "delete", "path": "~/x/y.txt"},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert res_del.success is True
finally:
tools_mod.os.path.expanduser = orig_expanduser
@pytest.mark.unit
def test_fetch_web_page_success(monkeypatch):
"""Test fetchWebPage tool with a mocked successful response."""
import jarvis.tools.registry as tools_mod
# Mock a successful HTTP response
class MockResponse:
def __init__(self):
self.status_code = 200
self.content = b'''
<html>
<head><title>Test Page</title></head>
<body>
<h1>Welcome</h1>
<p>This is a test page with some content.</p>
<a href="https://example.com">Example Link</a>
</body>
</html>
'''
self.text = self.content.decode()
def raise_for_status(self):
pass
# The production tool wraps the response in ``with requests.get(...)``
# so the connection is released deterministically — mirror that here.
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
return False
def mock_requests_get(url, **kwargs):
return MockResponse()
monkeypatch.setattr(tools_mod.requests, 'get', mock_requests_get)
db = DummyDB()
cfg = DummyCfg()
res = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="fetchWebPage",
tool_args={"url": "https://example.com"},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert isinstance(res, ToolExecutionResult)
assert res.success is True
# Should contain the URL even without BeautifulSoup
assert "https://example.com" in (res.reply_text or "")
@pytest.mark.unit
def test_fetch_web_page_missing_url():
"""Test fetchWebPage tool with missing URL."""
db = DummyDB()
cfg = DummyCfg()
res = run_tool_with_retries(
db=db,
cfg=cfg,
tool_name="fetchWebPage",
tool_args={},
system_prompt="",
original_prompt="",
redacted_text="",
max_retries=0,
)
assert isinstance(res, ToolExecutionResult)
assert res.success is False
assert "url" in (res.reply_text or "").lower()