Adds first formal test suite for Python fork — 42 tests covering memory_store,
profiles_store, scheduler_store, macros_store, llm_backend. All pass.
Tests (tests/, ~290 lines)
conftest.py — `isolated_state` fixture: rewires _PATH/_PROFILES_DIR etc to
a per-test tmp_path so concurrent runs don't touch real data.
test_memory_store.py — 9 tests: remember/recall/forget round-trip,
search by key/value, limit/empty query,
persistence across reload, build_llm_context
test_profiles_store.py — 7 tests: seeded defaults, set_active persistence,
allows_command logic (default/work/driving)
test_scheduler_store.py — 12 tests: parser (daily/at/every/in, ru units,
bad input), add/remove/clear/remove_by_text,
_next_fire for each schedule kind
test_macros_store.py — 8 tests: is_macro_control filter, start/save
round-trip, control-phrase filtering, list_names
sorted, replay unknown raises
test_llm_backend.py — 6 tests: parse_backend aliases (ru + en),
persist round-trip, auto-detect logic
Run: cd /c/Jarvis/python && python -m pytest tests/
Three new fun packs (commands.yaml: 151 → 154)
now_playing — Windows Media Session API via PowerShell
"что играет" / "что за песня" / "какой трек"
Works with Spotify, YouTube, Foobar, Yandex Music, anything
that exposes SMTC (Win10 1803+).
world_clock — worldtimeapi.org (free, no key)
"сколько времени в Токио" / "время в Лондоне"
21 Russian + world cities pre-mapped to IANA timezones.
daily_quote — zenquotes.io (free, no key) + LLM translation
"цитата дня" / "вдохнови меня"
Fetches English quote, translates to Russian via active LLM
(Groq or Ollama), speaks "text — author."
Pack count: 151 → 154. Tests: 42 pytest + ast.parse + yaml.safe_load.
68 lines
2.3 KiB
Python
68 lines
2.3 KiB
Python
"""Tests for llm_backend.parse_backend + state machine."""
|
|
|
|
import os
|
|
import importlib
|
|
import sys
|
|
|
|
|
|
def test_parse_backend_groq_aliases():
|
|
if 'llm_backend' in sys.modules:
|
|
del sys.modules['llm_backend']
|
|
m = importlib.import_module('llm_backend')
|
|
for alias in ('groq', 'GROQ', 'cloud', 'облако', 'клауд', ' Groq '):
|
|
assert m.parse_backend(alias) == 'groq', f"failed: {alias!r}"
|
|
|
|
|
|
def test_parse_backend_ollama_aliases():
|
|
if 'llm_backend' in sys.modules:
|
|
del sys.modules['llm_backend']
|
|
m = importlib.import_module('llm_backend')
|
|
for alias in ('ollama', 'OLLAMA', 'local', 'локальный', 'локал', 'локальн'):
|
|
assert m.parse_backend(alias) == 'ollama', f"failed: {alias!r}"
|
|
|
|
|
|
def test_parse_backend_unknown_returns_none():
|
|
if 'llm_backend' in sys.modules:
|
|
del sys.modules['llm_backend']
|
|
m = importlib.import_module('llm_backend')
|
|
assert m.parse_backend('openai') is None
|
|
assert m.parse_backend('claude') is None
|
|
assert m.parse_backend('') is None
|
|
assert m.parse_backend(None) is None
|
|
|
|
|
|
def test_persist_round_trip(tmp_path, monkeypatch):
|
|
if 'llm_backend' in sys.modules:
|
|
del sys.modules['llm_backend']
|
|
m = importlib.import_module('llm_backend')
|
|
monkeypatch.setattr(m, '_CHOICE_FILE', str(tmp_path / 'llm_backend.txt'))
|
|
m._persist('ollama')
|
|
assert m._read_persisted() == 'ollama'
|
|
m._persist('groq')
|
|
assert m._read_persisted() == 'groq'
|
|
|
|
|
|
def test_auto_detect_fallback_to_ollama(tmp_path, monkeypatch):
|
|
"""When no GROQ_TOKEN in config, auto-detect should pick ollama."""
|
|
if 'llm_backend' in sys.modules:
|
|
del sys.modules['llm_backend']
|
|
m = importlib.import_module('llm_backend')
|
|
|
|
# Mock config module with no GROQ_TOKEN
|
|
class FakeConfig:
|
|
GROQ_TOKEN = ''
|
|
monkeypatch.setitem(sys.modules, 'config', FakeConfig())
|
|
|
|
assert m._auto_detect_backend() == 'ollama'
|
|
|
|
|
|
def test_auto_detect_picks_groq_when_token_present(tmp_path, monkeypatch):
|
|
if 'llm_backend' in sys.modules:
|
|
del sys.modules['llm_backend']
|
|
m = importlib.import_module('llm_backend')
|
|
|
|
class FakeConfig:
|
|
GROQ_TOKEN = 'gsk_fake_test_token'
|
|
monkeypatch.setitem(sys.modules, 'config', FakeConfig())
|
|
|
|
assert m._auto_detect_backend() == 'groq'
|