chore: auto-commit from dashboard

This commit is contained in:
2026-08-22 08:40:56 +00:00
parent 4d2dbfb8a7
commit fe500a7227
35 changed files with 1372 additions and 144 deletions

View File

@@ -46,6 +46,8 @@ class TestDispatch:
"commit", "push", "pull", "test", "email", "calendar",
"note", "jurnal", "search", "kb", "remind", "logs",
"doctor", "heartbeat", "help", "audio",
"sold", "trezorerie", "facturi", "firme", "vremea",
"testfallback",
}
assert set(COMMANDS.keys()) == expected

View File

@@ -217,6 +217,48 @@ class TestRegularMessage:
assert "Error: API timeout" in response
assert is_cmd is False
@patch("src.router._local_fallback_reply")
@patch("src.router._get_channel_config")
@patch("src.router._get_config")
@patch("src.router.send_message")
def test_rate_limit_falls_back_to_local_llm(
self, mock_send, mock_get_config, mock_chan_cfg, mock_fallback,
):
mock_send.side_effect = RuntimeError(
"Claude CLI error (exit 1): You've hit your session limit · resets 10:50am (UTC)"
)
mock_chan_cfg.return_value = None
mock_cfg = MagicMock()
mock_cfg.get.return_value = "sonnet"
mock_get_config.return_value = mock_cfg
mock_fallback.return_value = "⚠️ fallback reply"
response, is_cmd = route_message("ch-1", "user-1", "hello")
assert response == "⚠️ fallback reply"
assert is_cmd is False
mock_fallback.assert_called_once_with("hello")
@patch("src.router._local_fallback_reply")
@patch("src.router._get_channel_config")
@patch("src.router._get_config")
@patch("src.router.send_message")
def test_rate_limit_fallback_unavailable_surfaces_original_error(
self, mock_send, mock_get_config, mock_chan_cfg, mock_fallback,
):
mock_send.side_effect = RuntimeError(
"Claude CLI error (exit 1): You've hit your session limit · resets 10:50am (UTC)"
)
mock_chan_cfg.return_value = None
mock_cfg = MagicMock()
mock_cfg.get.return_value = "sonnet"
mock_get_config.return_value = mock_cfg
mock_fallback.return_value = None # local fallback itself unreachable
response, is_cmd = route_message("ch-1", "user-1", "hello")
assert "Error:" in response
assert "hit your session limit" in response
assert is_cmd is False
@patch("src.router._get_channel_config")
@patch("src.router._get_config")
@patch("src.router.send_message")
@@ -488,3 +530,65 @@ class TestVoiceLangMarker:
mock_clear.assert_called_once_with("ch-1")
for call in mock_clear.call_args_list:
assert not call.args[0].startswith("voice:")
# --- Rate-limit detection + local fallback ---
class TestRateLimitFallback:
def test_is_rate_limit_error_session_limit(self):
from src.router import _is_rate_limit_error
err = RuntimeError(
"Claude CLI error (exit 1): You've hit your session limit · resets 10:50am (UTC)"
)
assert _is_rate_limit_error(err) is True
def test_is_rate_limit_error_usage_limit_variant(self):
from src.router import _is_rate_limit_error
assert _is_rate_limit_error(RuntimeError("You've hit your usage limit for today")) is True
def test_is_rate_limit_error_false_for_other_errors(self):
from src.router import _is_rate_limit_error
assert _is_rate_limit_error(RuntimeError("Claude CLI error (exit 1): network timeout")) is False
@patch("src.router._get_config")
def test_local_fallback_disabled_returns_none(self, mock_get_config):
from src.router import _local_fallback_reply
mock_cfg = MagicMock()
mock_cfg.get.return_value = {"enabled": False, "url": "http://x"}
mock_get_config.return_value = mock_cfg
assert _local_fallback_reply("hello") is None
@patch("src.router.requests.post")
@patch("src.router._get_config")
def test_local_fallback_success(self, mock_get_config, mock_post):
from src.router import _local_fallback_reply
mock_cfg = MagicMock()
mock_cfg.get.return_value = {"enabled": True, "url": "http://10.0.20.161:8091/v1/chat/completions"}
mock_get_config.return_value = mock_cfg
mock_resp = MagicMock()
mock_resp.json.return_value = {"choices": [{"message": {"content": "Capitala Franței este Paris."}}]}
mock_post.return_value = mock_resp
reply = _local_fallback_reply("Care este capitala Franței?")
assert reply is not None
assert "Capitala Franței este Paris." in reply
assert "Claude e la limită" in reply
@patch("src.router.requests.post")
@patch("src.router._get_config")
def test_local_fallback_network_failure_returns_none(self, mock_get_config, mock_post):
from src.router import _local_fallback_reply
mock_cfg = MagicMock()
mock_cfg.get.return_value = {"enabled": True, "url": "http://10.0.20.161:8091/v1/chat/completions"}
mock_get_config.return_value = mock_cfg
mock_post.side_effect = ConnectionError("unreachable")
assert _local_fallback_reply("hello") is None

View File

@@ -12,6 +12,7 @@ import pytest
from src.voice.normalize import (
expand_abbreviations,
expand_currency,
expand_for_tts,
expand_numbers_ro,
expand_symbols,
normalize_for_tts,
@@ -108,11 +109,19 @@ def test_normalize_for_tts_currency_ro_accounting_format(text, expected):
("foo & bar", "foo și bar"),
("Marius @ home", "Marius la home"),
("30°", "30 grade"),
("~30", "aproximativ 30"),
])
def test_expand_symbols(text, expected):
assert expand_symbols(text) == expected
def test_expand_symbols_tilde_english():
# Bare '~' passed unexpanded to pocket-tts corrupts nearby speech
# (observed: dropped/garbled words, shortened audio) — must become
# a spoken word before reaching the TTS engine, not stay literal.
assert expand_symbols("~six thousand", lang='en') == "about six thousand"
# ============================================================
# Abbreviations (4)
# ============================================================
@@ -148,6 +157,18 @@ def test_truncate_over_200_words_appends_suffix():
assert words[200:] == ["Restul", "l-am", "scris", "în", "chat."]
# ============================================================
# English number path (pocket-tts, lang='en')
# ============================================================
@pytest.mark.parametrize("text,expected", [
("Bank balance: ~105.300 lei.", "Bank balance: about one hundred and five thousand, three hundred lei."),
("Cash balance: ~6.000 lei.", "Cash balance: about six thousand lei."),
("That takes 3.5 hours.", "That takes three point five hours."),
])
def test_expand_for_tts_english_numbers(text, expected):
assert expand_for_tts(text, lang='en') == expected
# ============================================================
# Edge cases (2)
# ============================================================