| from __future__ import annotations |
|
|
| from types import SimpleNamespace |
| from unittest.mock import MagicMock |
|
|
| from jarvis.runtime_preferences import ( |
| detect_voice_profile_updates, |
| learn_voice_preferences, |
| set_persona_style, |
| voice_profile_summary, |
| ) |
|
|
|
|
| def test_detect_voice_profile_updates_extracts_multiple_fields() -> None: |
| updates = detect_voice_profile_updates( |
| "Please be brief, speak slower, and use a formal tone." |
| ) |
| assert updates["verbosity"] == "brief" |
| assert updates["pace"] == "slow" |
| assert updates["tone"] == "formal" |
|
|
|
|
| def test_detect_voice_profile_updates_requires_style_hints() -> None: |
| updates = detect_voice_profile_updates("Could you check the weather in Boston?") |
| assert updates == {} |
|
|
|
|
| def test_detect_voice_profile_updates_handles_confirmation_preferences() -> None: |
| updates = detect_voice_profile_updates( |
| "I prefer fewer confirmations, and please be more direct." |
| ) |
| assert updates["confirmations"] == "minimal" |
| assert updates["tone"] == "direct" |
|
|
|
|
| def test_voice_profile_summary_renders_expected_shape() -> None: |
| text = voice_profile_summary( |
| { |
| "verbosity": "detailed", |
| "confirmations": "strict", |
| "pace": "fast", |
| "tone": "empathetic", |
| } |
| ) |
| assert "verbosity=detailed" in text |
| assert "confirmations=strict" in text |
| assert "pace=fast" in text |
| assert "tone=empathetic" in text |
|
|
|
|
| def test_learn_voice_preferences_updates_profile_memory_and_telemetry() -> None: |
| memory = SimpleNamespace(upsert_summary=MagicMock()) |
| runtime = SimpleNamespace( |
| _parse_control_choice=lambda value, valid: value if value in valid else None, |
| _active_voice_user=lambda: "operator", |
| _active_voice_profile=lambda user=None: {}, |
| _voice_user_profiles={}, |
| _telemetry={"preference_update_turns": 0.0, "preference_update_fields": 0.0}, |
| _last_learned_preferences={}, |
| brain=SimpleNamespace(_memory=memory), |
| _persist_runtime_state_safe=MagicMock(), |
| _publish_voice_status=MagicMock(), |
| ) |
|
|
| updates = learn_voice_preferences( |
| runtime, |
| "Please be brief and speak slower.", |
| now_ts=12.0, |
| valid_voice_profile_verbosity={"normal", "brief", "detailed"}, |
| valid_voice_profile_confirmations={"standard", "minimal", "strict"}, |
| valid_voice_profile_pace={"normal", "slow", "fast"}, |
| valid_voice_profile_tone={"auto", "formal", "direct", "empathetic", "witty"}, |
| ) |
|
|
| assert updates == {"verbosity": "brief", "pace": "slow"} |
| assert runtime._voice_user_profiles["operator"]["verbosity"] == "brief" |
| assert runtime._telemetry["preference_update_turns"] == 1.0 |
| assert runtime._telemetry["preference_update_fields"] == 2.0 |
| assert runtime._last_learned_preferences["applied_at"] == 12.0 |
| memory.upsert_summary.assert_called_once() |
| runtime._persist_runtime_state_safe.assert_called_once() |
| runtime._publish_voice_status.assert_called_once() |
|
|
|
|
| def test_learn_voice_preferences_returns_empty_without_style_updates() -> None: |
| runtime = SimpleNamespace( |
| _parse_control_choice=lambda value, valid: value if value in valid else None, |
| _active_voice_user=lambda: "operator", |
| _active_voice_profile=lambda user=None: {}, |
| _voice_user_profiles={}, |
| _telemetry={}, |
| _last_learned_preferences={}, |
| brain=SimpleNamespace(_memory=None), |
| _persist_runtime_state_safe=MagicMock(), |
| _publish_voice_status=MagicMock(), |
| ) |
|
|
| updates = learn_voice_preferences( |
| runtime, |
| "Could you check the weather?", |
| valid_voice_profile_verbosity={"normal", "brief", "detailed"}, |
| valid_voice_profile_confirmations={"standard", "minimal", "strict"}, |
| valid_voice_profile_pace={"normal", "slow", "fast"}, |
| valid_voice_profile_tone={"auto", "formal", "direct", "empathetic", "witty"}, |
| ) |
|
|
| assert updates == {} |
| runtime._persist_runtime_state_safe.assert_not_called() |
| runtime._publish_voice_status.assert_not_called() |
|
|
|
|
| def test_set_persona_style_updates_config_and_memory_summary() -> None: |
| memory = SimpleNamespace(upsert_summary=MagicMock()) |
| runtime = SimpleNamespace( |
| config=SimpleNamespace(persona_style="composed"), |
| brain=SimpleNamespace(_memory=memory), |
| ) |
|
|
| set_persona_style(runtime, "witty") |
|
|
| assert runtime.config.persona_style == "witty" |
| memory.upsert_summary.assert_called_once_with("persona_style", "witty") |
|
|