"""Tests for the UpdateSettings tool.""" import pytest from unittest.mock import MagicMock, AsyncMock, patch from reachy_mini_conversation_app.database import MiniMinderDB from reachy_mini_conversation_app.tools.core_tools import ToolDependencies from reachy_mini_conversation_app.profiles._reachy_mini_minder_locked_profile.update_settings import ( UpdateSettings, ) @pytest.fixture def deps() -> ToolDependencies: """Create ToolDependencies with in-memory database.""" db = MiniMinderDB(":memory:") return ToolDependencies( reachy_mini=MagicMock(), movement_manager=MagicMock(), database=db, ) @pytest.mark.asyncio @patch( "reachy_mini_conversation_app.profiles._reachy_mini_minder_locked_profile.update_settings.emit_settings_updated", new_callable=AsyncMock, ) @patch( "reachy_mini_conversation_app.profiles._reachy_mini_minder_locked_profile.update_settings.emit_ui_component", new_callable=AsyncMock, ) async def test_update_display_name( mock_ui: AsyncMock, mock_event: AsyncMock, deps: ToolDependencies ) -> None: """Updating display_name writes to the database and emits events.""" tool = UpdateSettings() result = await tool(deps, display_name="Alex") assert result["status"] == "saved" assert "display_name" in result["updated_fields"] # Verify database was updated profile = deps.database.get_or_create_profile() assert profile["display_name"] == "Alex" mock_event.assert_awaited_once() called_fields = mock_event.call_args[0][0] assert "display_name" in called_fields mock_ui.assert_awaited_once() call_args = mock_ui.call_args assert call_args[0][0] == "SettingsConfirmation" assert any(c["field"] == "Display Name" for c in call_args[0][1]["changes"]) @pytest.mark.asyncio @patch( "reachy_mini_conversation_app.profiles._reachy_mini_minder_locked_profile.update_settings.emit_settings_updated", new_callable=AsyncMock, ) @patch( "reachy_mini_conversation_app.profiles._reachy_mini_minder_locked_profile.update_settings.emit_ui_component", new_callable=AsyncMock, ) async def test_update_multiple_fields( mock_ui: AsyncMock, mock_event: AsyncMock, deps: ToolDependencies ) -> None: """Updating multiple fields at once works correctly.""" tool = UpdateSettings() result = await tool( deps, display_name="Sam", neurologist_name="Dr. Williams", neurologist_email="dr@example.com", ) assert result["status"] == "saved" assert len(result["updated_fields"]) == 3 profile = deps.database.get_or_create_profile() assert profile["display_name"] == "Sam" assert profile["neurologist_name"] == "Dr. Williams" assert profile["neurologist_email"] == "dr@example.com" mock_event.assert_awaited_once() called_fields = mock_event.call_args[0][0] assert "display_name" in called_fields assert "neurologist_name" in called_fields assert "neurologist_email" in called_fields @pytest.mark.asyncio async def test_update_no_fields(deps: ToolDependencies) -> None: """Calling with no fields returns an error.""" tool = UpdateSettings() result = await tool(deps) assert "error" in result assert "No fields" in result["error"] @pytest.mark.asyncio async def test_update_no_database() -> None: """Calling without a database returns an error.""" bare_deps = ToolDependencies( reachy_mini=MagicMock(), movement_manager=MagicMock(), ) tool = UpdateSettings() result = await tool(bare_deps, display_name="Test") assert "error" in result assert "Database" in result["error"] @pytest.mark.asyncio async def test_tool_spec() -> None: """Tool spec should be valid.""" tool = UpdateSettings() spec = tool.spec() assert spec["type"] == "function" assert spec["name"] == "update_settings" assert isinstance(spec["description"], str) assert "parameters" in spec assert "display_name" in spec["parameters"]["properties"] assert "timezone" in spec["parameters"]["properties"]