J.A.R.V.I.S-py/tests/test_profiles_store.py

62 lines
2.1 KiB
Python
Raw Permalink Normal View History

feat+test: pytest suite + Now Playing + World Clock + Daily Quote packs (154 commands) 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.
2026-05-16 00:56:02 +03:00
"""Tests for profiles_store."""
import os
import json
def test_default_seeded_on_init(isolated_state, tmp_path):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
p._init()
names = p.list_names()
# 5 defaults: default, work, game, sleep, driving
for expected in ('default', 'work', 'game', 'sleep', 'driving'):
assert expected in names, f"missing seeded profile: {expected}"
def test_active_defaults_to_default(isolated_state):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
a = p.active()
assert a['name'] == 'default'
def test_set_active_persists(isolated_state):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
p.set_active('work')
assert p.active_name() == 'work'
assert os.path.isfile(p._ACTIVE_FILE)
with open(p._ACTIVE_FILE, encoding='utf-8') as f:
assert f.read().strip() == 'work'
def test_set_active_unknown_raises(isolated_state):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
try:
p.set_active('nonexistent')
assert False, "should have raised"
except ValueError:
pass
def test_allows_command_default_allows_all(isolated_state):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
p.set_active('default')
assert p.allows_command('anything')
assert p.allows_command('games.steam.launch')
def test_allows_command_work_disables_games(isolated_state):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
p.set_active('work')
assert not p.allows_command('games.steam.launch')
assert not p.allows_command('fun.joke')
assert p.allows_command('time.current')
def test_allows_command_driving_whitelist(isolated_state):
p = isolated_state('profiles_store', ['_PROFILES_DIR', '_ACTIVE_FILE'])
p.set_active('driving')
assert p.allows_command('music.play')
assert p.allows_command('weather.now')
assert not p.allows_command('windows.minimize')
assert not p.allows_command('screen.brightness')