Outlook COM (`outlook_handlers.py`, agent):
- 4 handlers mirroring Rust pack: unread_count, latest, send_clipboard,
summarize_inbox. Uses `win32com.client` with lazy import so the module
loads even when pywin32 is missing — handlers degrade gracefully and
speak a "Outlook недоступен" reply.
- `requirements.txt`: pywin32>=305 (optional).
- 7 unit tests mock `_outlook_session` via unittest.mock.
Time tracker (`time_tracker_handlers.py`, agent):
- 5 handlers: start, stop, today, week, reset. JSON store at
`<here>/time_tracker.json`, atomic write-then-rename.
- Same shape as the Rust pack's `jarvis.state` blob: current_session_start
+ sessions[{start,end}]. Local-calendar today boundary.
- Full Russian pluralisation (час/часа/часов, минута/минуты/минут).
- 14 unit tests using the existing `isolated_state` fixture in conftest.py.
`extensions.py` + `main.py`: imports + 9 new proxy do_* functions + 9
new dispatch elifs. `commands.yaml`: 9 new entries.
Tests: 60 → 81 (+14 tracker + 7 outlook).
103 lines
3 KiB
Python
103 lines
3 KiB
Python
"""Tests for outlook_handlers — COM bridge mocked via unittest.mock.
|
|
|
|
We patch the module-level `_outlook_session` to return a fake (app, ns) pair
|
|
made of MagicMocks so we never need a live Outlook install.
|
|
"""
|
|
|
|
import importlib
|
|
import sys
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
|
|
def _fresh_module():
|
|
if 'outlook_handlers' in sys.modules:
|
|
del sys.modules['outlook_handlers']
|
|
return importlib.import_module('outlook_handlers')
|
|
|
|
|
|
def _make_inbox_with_unread(count):
|
|
"""Build a fake Inbox object whose UnReadItemCount is `count`."""
|
|
inbox = MagicMock()
|
|
inbox.UnReadItemCount = count
|
|
return inbox
|
|
|
|
|
|
def test_unread_count_speaks_singular_for_one():
|
|
mod = _fresh_module()
|
|
spoken = []
|
|
mod.set_speak(lambda t: spoken.append(t))
|
|
|
|
app = MagicMock()
|
|
ns = MagicMock()
|
|
ns.GetDefaultFolder.return_value = _make_inbox_with_unread(1)
|
|
with patch.object(mod, '_outlook_session', return_value=(app, ns)):
|
|
mod.do_outlook_unread_count({}, '')
|
|
|
|
assert spoken, "expected the handler to speak"
|
|
assert "Одно" in spoken[0]
|
|
|
|
|
|
def test_unread_count_zero_says_no_unread():
|
|
mod = _fresh_module()
|
|
spoken = []
|
|
mod.set_speak(lambda t: spoken.append(t))
|
|
|
|
app = MagicMock()
|
|
ns = MagicMock()
|
|
ns.GetDefaultFolder.return_value = _make_inbox_with_unread(0)
|
|
with patch.object(mod, '_outlook_session', return_value=(app, ns)):
|
|
mod.do_outlook_unread_count({}, '')
|
|
|
|
assert spoken
|
|
assert "нет" in spoken[0].lower()
|
|
|
|
|
|
def test_unread_count_plural_count():
|
|
mod = _fresh_module()
|
|
spoken = []
|
|
mod.set_speak(lambda t: spoken.append(t))
|
|
|
|
ns = MagicMock()
|
|
ns.GetDefaultFolder.return_value = _make_inbox_with_unread(7)
|
|
with patch.object(mod, '_outlook_session', return_value=(MagicMock(), ns)):
|
|
mod.do_outlook_unread_count({}, '')
|
|
|
|
assert "7" in spoken[0]
|
|
|
|
|
|
def test_unavailable_outlook_says_so():
|
|
mod = _fresh_module()
|
|
spoken = []
|
|
mod.set_speak(lambda t: spoken.append(t))
|
|
|
|
with patch.object(mod, '_outlook_session', return_value=(None, None)):
|
|
mod.do_outlook_unread_count({}, '')
|
|
|
|
assert spoken
|
|
assert "Outlook" in spoken[0]
|
|
|
|
|
|
def test_extract_recipient_strips_trigger():
|
|
mod = _fresh_module()
|
|
assert mod._extract_recipient("отправь письмо Дмитрию") == "Дмитрию"
|
|
assert mod._extract_recipient("email this to Bob") == "Bob"
|
|
assert mod._extract_recipient("Bob") == "Bob"
|
|
assert mod._extract_recipient("") == ""
|
|
|
|
|
|
def test_send_clipboard_no_recipient_prompts_for_name():
|
|
mod = _fresh_module()
|
|
spoken = []
|
|
mod.set_speak(lambda t: spoken.append(t))
|
|
|
|
# No trigger and no recipient in the voice -> _extract_recipient returns ""
|
|
mod.do_outlook_send_clipboard({}, "")
|
|
|
|
assert spoken
|
|
assert "имя" in spoken[0].lower() or "name" in spoken[0].lower()
|
|
|
|
|
|
def test_strip_html_basic():
|
|
mod = _fresh_module()
|
|
assert mod._strip_html("<b>Hi</b> there") == "Hi there"
|
|
assert mod._strip_html("") == ""
|