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.
111 lines
3.8 KiB
Python
111 lines
3.8 KiB
Python
"""
|
|
Tests for the MCP server catalogue.
|
|
|
|
Verifies catalogue integrity, entry conversion, and wizard filtering.
|
|
"""
|
|
|
|
from desktop_app.mcp_catalogue import (
|
|
CATALOGUE,
|
|
CATALOGUE_BY_NAME,
|
|
MCPEntry,
|
|
get_wizard_entries,
|
|
)
|
|
|
|
|
|
class TestCatalogueIntegrity:
|
|
"""Tests for catalogue data integrity."""
|
|
|
|
def test_no_duplicate_names(self):
|
|
"""Every catalogue entry must have a unique name."""
|
|
names = [e.name for e in CATALOGUE]
|
|
assert len(names) == len(set(names)), (
|
|
f"Duplicate names: {[n for n in names if names.count(n) > 1]}"
|
|
)
|
|
|
|
def test_all_entries_have_required_fields(self):
|
|
"""Every entry needs name, display_name, description, command."""
|
|
for e in CATALOGUE:
|
|
assert e.name.strip(), f"Entry missing name"
|
|
assert e.display_name.strip(), f"Entry '{e.name}' missing display_name"
|
|
assert e.description.strip(), f"Entry '{e.name}' missing description"
|
|
assert e.command.strip(), f"Entry '{e.name}' missing command"
|
|
|
|
def test_api_key_entries_have_env_var(self):
|
|
"""Entries that need an API key must specify the env var name."""
|
|
for e in CATALOGUE:
|
|
if e.needs_api_key:
|
|
assert e.api_key_env_var, (
|
|
f"Entry '{e.name}' needs API key but has no api_key_env_var"
|
|
)
|
|
|
|
def test_by_name_matches_catalogue(self):
|
|
"""CATALOGUE_BY_NAME should contain exactly the same entries."""
|
|
assert len(CATALOGUE_BY_NAME) == len(CATALOGUE)
|
|
for e in CATALOGUE:
|
|
assert e.name in CATALOGUE_BY_NAME
|
|
assert CATALOGUE_BY_NAME[e.name] is e
|
|
|
|
|
|
class TestMCPEntryToConfig:
|
|
"""Tests for MCPEntry.to_config() conversion."""
|
|
|
|
def test_basic_entry(self):
|
|
entry = MCPEntry(
|
|
name="test",
|
|
display_name="Test",
|
|
description="A test server",
|
|
command="npx",
|
|
args=["-y", "@test/server"],
|
|
)
|
|
cfg = entry.to_config()
|
|
assert cfg["transport"] == "stdio"
|
|
assert cfg["command"] == "npx"
|
|
assert cfg["args"] == ["-y", "@test/server"]
|
|
assert "env" not in cfg
|
|
|
|
def test_entry_with_env(self):
|
|
entry = MCPEntry(
|
|
name="test",
|
|
display_name="Test",
|
|
description="A test server",
|
|
command="npx",
|
|
args=[],
|
|
env={"API_KEY": "secret"},
|
|
)
|
|
cfg = entry.to_config()
|
|
assert cfg["env"] == {"API_KEY": "secret"}
|
|
|
|
def test_to_config_returns_independent_copy(self):
|
|
"""Calling to_config twice should return separate dicts."""
|
|
entry = CATALOGUE[0]
|
|
a = entry.to_config()
|
|
b = entry.to_config()
|
|
assert a == b
|
|
a["args"].append("extra")
|
|
assert a != b # mutating one shouldn't affect the other
|
|
|
|
|
|
class TestWizardEntries:
|
|
"""Tests for get_wizard_entries() filtering."""
|
|
|
|
def test_only_returns_featured(self):
|
|
"""get_wizard_entries() should only return wizard_featured entries."""
|
|
entries = get_wizard_entries()
|
|
assert len(entries) > 0
|
|
for e in entries:
|
|
assert e.wizard_featured is True
|
|
|
|
def test_no_api_key_required(self):
|
|
"""Wizard entries should not require API keys (they're meant for quick setup)."""
|
|
for e in get_wizard_entries():
|
|
assert not e.needs_api_key, (
|
|
f"Wizard entry '{e.name}' requires an API key — "
|
|
"wizard entries should be zero-config"
|
|
)
|
|
|
|
def test_wizard_entries_are_subset_of_catalogue(self):
|
|
"""Every wizard entry must also exist in the full catalogue."""
|
|
wizard_names = {e.name for e in get_wizard_entries()}
|
|
catalogue_names = {e.name for e in CATALOGUE}
|
|
assert wizard_names.issubset(catalogue_names)
|