reachy_mini_minder / tests /test_update_settings.py
Boopster's picture
initial commit
af9cde9
"""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"]