| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- # -*- coding: utf-8 -*-
- """
- Tests für Keyboard Input Service.
- Testet:
- - KeyboardInputService Initialisierung und Lifecycle
- - SimpleKeyboardInput Fallback
- - create_keyboard_input Factory
- - Conversation State Management
- - Follow-Up Handling
- """
- import asyncio
- import pytest
- from unittest.mock import MagicMock, AsyncMock, patch
- from trixy_core.input.keyboard_input import (
- KeyboardInputService,
- SimpleKeyboardInput,
- create_keyboard_input,
- _check_prompt_toolkit,
- )
- from trixy_core.network.cmd import FollowUpRequest, TextInput
- # =============================================================================
- # Fixtures
- # =============================================================================
- @pytest.fixture
- def mock_app():
- """Mock-Application mit Events und Properties."""
- app = MagicMock()
- app.room = "test_room"
- app.alias = "test_alias"
- app.shutdown = MagicMock()
- # Events Mock
- app.events = MagicMock()
- app.events.trigger = AsyncMock()
- return app
- @pytest.fixture
- def mock_connection():
- """Mock-ClientConnection."""
- conn = MagicMock()
- conn.is_connected = True
- conn.send_command = AsyncMock(return_value=True)
- return conn
- @pytest.fixture
- def keyboard_service(mock_app):
- """Erstellt einen KeyboardInputService ohne Connection."""
- return KeyboardInputService(mock_app, connection=None, prompt=">>> ")
- @pytest.fixture
- def keyboard_service_with_connection(mock_app, mock_connection):
- """Erstellt einen KeyboardInputService mit Connection."""
- return KeyboardInputService(mock_app, connection=mock_connection, prompt=">>> ")
- @pytest.fixture
- def simple_input(mock_app):
- """Erstellt einen SimpleKeyboardInput."""
- return SimpleKeyboardInput(mock_app, connection=None, prompt=">>> ")
- @pytest.fixture
- def simple_input_with_connection(mock_app, mock_connection):
- """Erstellt einen SimpleKeyboardInput mit Connection."""
- return SimpleKeyboardInput(mock_app, connection=mock_connection, prompt=">>> ")
- # =============================================================================
- # Tests für _check_prompt_toolkit
- # =============================================================================
- class TestCheckPromptToolkit:
- """Tests für die prompt_toolkit Verfügbarkeitsprüfung."""
- def test_returns_bool(self):
- """Testet, dass ein Boolean zurückgegeben wird."""
- result = _check_prompt_toolkit()
- assert isinstance(result, bool)
- def test_result_is_cached(self):
- """Testet, dass das Ergebnis gecacht wird."""
- result1 = _check_prompt_toolkit()
- result2 = _check_prompt_toolkit()
- assert result1 == result2
- # =============================================================================
- # Tests für KeyboardInputService - Initialisierung
- # =============================================================================
- class TestKeyboardInputServiceInit:
- """Tests für KeyboardInputService Initialisierung."""
- def test_initial_state(self, keyboard_service):
- """Testet den Initialzustand."""
- assert keyboard_service.is_running is False
- assert keyboard_service.has_active_session is False
- assert keyboard_service._input_active is False
- def test_initial_prompt(self, keyboard_service):
- """Testet den initialen Prompt."""
- assert keyboard_service._prompt == ">>> "
- def test_custom_prompt(self, mock_app):
- """Testet benutzerdefinierten Prompt."""
- service = KeyboardInputService(mock_app, prompt="Test> ")
- assert service._prompt == "Test> "
- def test_no_connection_initially(self, keyboard_service):
- """Testet, dass keine Connection initial gesetzt ist."""
- assert keyboard_service._connection is None
- def test_with_connection(self, keyboard_service_with_connection, mock_connection):
- """Testet Service mit Connection."""
- assert keyboard_service_with_connection._connection is mock_connection
- # =============================================================================
- # Tests für KeyboardInputService - Properties
- # =============================================================================
- class TestKeyboardInputServiceProperties:
- """Tests für KeyboardInputService Properties."""
- def test_is_running_false_initially(self, keyboard_service):
- """Testet is_running initial."""
- assert keyboard_service.is_running is False
- def test_has_active_session_false_initially(self, keyboard_service):
- """Testet has_active_session initial."""
- assert keyboard_service.has_active_session is False
- def test_has_active_session_true_with_session(self, keyboard_service):
- """Testet has_active_session mit Session."""
- keyboard_service._current_session_id = "test-session-123"
- assert keyboard_service.has_active_session is True
- def test_has_active_session_false_with_empty_session(self, keyboard_service):
- """Testet has_active_session mit leerer Session."""
- keyboard_service._current_session_id = ""
- assert keyboard_service.has_active_session is False
- # =============================================================================
- # Tests für KeyboardInputService - set_connection
- # =============================================================================
- class TestKeyboardInputServiceSetConnection:
- """Tests für set_connection Methode."""
- def test_set_connection(self, keyboard_service, mock_connection):
- """Testet Setzen der Connection."""
- assert keyboard_service._connection is None
- keyboard_service.set_connection(mock_connection)
- assert keyboard_service._connection is mock_connection
- # =============================================================================
- # Tests für KeyboardInputService - Conversation State
- # =============================================================================
- class TestKeyboardInputServiceConversationState:
- """Tests für Conversation State Management."""
- @pytest.mark.asyncio
- async def test_request_input_new_conversation(self, keyboard_service):
- """Testet request_input für neue Conversation."""
- await keyboard_service.request_input()
- assert keyboard_service._awaiting_follow_up is False
- assert keyboard_service._follow_up_question == ""
- assert keyboard_service._follow_up_number == 0
- assert keyboard_service._current_session_id == ""
- @pytest.mark.asyncio
- async def test_request_input_follow_up(self, keyboard_service):
- """Testet request_input für Follow-Up."""
- await keyboard_service.request_input(
- follow_up_question="Was meinst du damit?",
- session_id="session-123"
- )
- assert keyboard_service._awaiting_follow_up is True
- assert keyboard_service._follow_up_question == "Was meinst du damit?"
- assert keyboard_service._follow_up_number == 1
- assert keyboard_service._current_session_id == "session-123"
- @pytest.mark.asyncio
- async def test_request_input_multiple_follow_ups(self, keyboard_service):
- """Testet mehrere Follow-Ups hintereinander."""
- await keyboard_service.request_input(
- follow_up_question="Frage 1?",
- session_id="session-123"
- )
- assert keyboard_service._follow_up_number == 1
- await keyboard_service.request_input(
- follow_up_question="Frage 2?",
- session_id="session-123"
- )
- assert keyboard_service._follow_up_number == 2
- @pytest.mark.asyncio
- async def test_handle_follow_up_request(self, keyboard_service):
- """Testet handle_follow_up_request."""
- request = FollowUpRequest(
- session_id="session-456",
- question="Kannst du das genauer erklären?"
- )
- # Muss als Task erstellt werden (async)
- keyboard_service.handle_follow_up_request(request)
- # Kurz warten damit Task ausgeführt wird
- await asyncio.sleep(0.01)
- assert keyboard_service._awaiting_follow_up is True
- assert keyboard_service._follow_up_question == "Kannst du das genauer erklären?"
- assert keyboard_service._current_session_id == "session-456"
- def test_handle_conversation_end_matching_session(self, keyboard_service):
- """Testet handle_conversation_end mit passender Session."""
- keyboard_service._current_session_id = "session-789"
- keyboard_service._awaiting_follow_up = True
- keyboard_service._follow_up_question = "Test?"
- keyboard_service._follow_up_number = 3
- keyboard_service.handle_conversation_end("session-789")
- assert keyboard_service._current_session_id == ""
- assert keyboard_service._awaiting_follow_up is False
- assert keyboard_service._follow_up_question == ""
- assert keyboard_service._follow_up_number == 0
- def test_handle_conversation_end_different_session(self, keyboard_service):
- """Testet handle_conversation_end mit anderer Session."""
- keyboard_service._current_session_id = "session-789"
- keyboard_service._awaiting_follow_up = True
- keyboard_service._follow_up_number = 3
- keyboard_service.handle_conversation_end("session-other")
- # State sollte unverändert sein
- assert keyboard_service._current_session_id == "session-789"
- assert keyboard_service._awaiting_follow_up is True
- assert keyboard_service._follow_up_number == 3
- # =============================================================================
- # Tests für KeyboardInputService - _process_input
- # =============================================================================
- class TestKeyboardInputServiceProcessInput:
- """Tests für _process_input Methode."""
- @pytest.mark.asyncio
- async def test_process_input_standalone_mode(self, keyboard_service, mock_app):
- """Testet Input-Verarbeitung im Standalone-Modus (keine Connection)."""
- await keyboard_service._process_input("Hallo Trixy")
- # Event sollte ausgelöst worden sein
- mock_app.events.trigger.assert_called_once()
- # Prüfe Event-Parameter
- call_args = mock_app.events.trigger.call_args
- assert call_args[0][0] == "speech_recognized"
- event_data = call_args[0][1]
- assert event_data.text == "Hallo Trixy"
- assert event_data.source == "keyboard"
- assert event_data.confidence == 1.0
- @pytest.mark.asyncio
- async def test_process_input_client_mode(self, keyboard_service_with_connection, mock_connection):
- """Testet Input-Verarbeitung im Client-Modus (mit Connection)."""
- await keyboard_service_with_connection._process_input("Test Input")
- # Command sollte gesendet worden sein
- mock_connection.send_command.assert_called_once()
- # Prüfe Command-Parameter
- call_args = mock_connection.send_command.call_args
- cmd = call_args[0][0]
- assert isinstance(cmd, TextInput)
- assert cmd.text == "Test Input"
- assert cmd.satellite_id == "test_alias"
- assert cmd.room_id == "test_room"
- assert cmd.language == "de"
- @pytest.mark.asyncio
- async def test_process_input_generates_session_id(self, keyboard_service):
- """Testet, dass Session-ID generiert wird."""
- assert keyboard_service._current_session_id == ""
- await keyboard_service._process_input("Test")
- assert keyboard_service._current_session_id != ""
- assert keyboard_service._current_session_id.startswith("kb-")
- @pytest.mark.asyncio
- async def test_process_input_preserves_session_id(self, keyboard_service):
- """Testet, dass existierende Session-ID beibehalten wird."""
- keyboard_service._current_session_id = "existing-session"
- await keyboard_service._process_input("Test")
- assert keyboard_service._current_session_id == "existing-session"
- @pytest.mark.asyncio
- async def test_process_input_resets_follow_up(self, keyboard_service):
- """Testet, dass Follow-Up-State zurückgesetzt wird."""
- keyboard_service._awaiting_follow_up = True
- keyboard_service._follow_up_question = "Test?"
- await keyboard_service._process_input("Antwort")
- assert keyboard_service._awaiting_follow_up is False
- assert keyboard_service._follow_up_question == ""
- @pytest.mark.asyncio
- async def test_process_input_follow_up_flag(self, keyboard_service_with_connection, mock_connection):
- """Testet, dass Follow-Up-Flag im Command gesetzt wird."""
- keyboard_service_with_connection._awaiting_follow_up = True
- keyboard_service_with_connection._follow_up_number = 2
- keyboard_service_with_connection._current_session_id = "session-123"
- await keyboard_service_with_connection._process_input("Follow-Up Antwort")
- call_args = mock_connection.send_command.call_args
- cmd = call_args[0][0]
- assert cmd.is_follow_up is True
- assert cmd.follow_up_number == 2
- # =============================================================================
- # Tests für SimpleKeyboardInput - Initialisierung
- # =============================================================================
- class TestSimpleKeyboardInputInit:
- """Tests für SimpleKeyboardInput Initialisierung."""
- def test_initial_state(self, simple_input):
- """Testet den Initialzustand."""
- assert simple_input._running is False
- assert simple_input._task is None
- def test_initial_prompt(self, simple_input):
- """Testet den initialen Prompt."""
- assert simple_input._prompt == ">>> "
- def test_custom_prompt(self, mock_app):
- """Testet benutzerdefinierten Prompt."""
- service = SimpleKeyboardInput(mock_app, prompt="Simple> ")
- assert service._prompt == "Simple> "
- def test_conversation_state_initial(self, simple_input):
- """Testet initialen Conversation State."""
- assert simple_input._current_session_id == ""
- assert simple_input._awaiting_follow_up is False
- assert simple_input._follow_up_number == 0
- assert simple_input._follow_up_question == ""
- # =============================================================================
- # Tests für SimpleKeyboardInput - set_connection
- # =============================================================================
- class TestSimpleKeyboardInputSetConnection:
- """Tests für SimpleKeyboardInput set_connection."""
- def test_set_connection(self, simple_input, mock_connection):
- """Testet Setzen der Connection."""
- assert simple_input._connection is None
- simple_input.set_connection(mock_connection)
- assert simple_input._connection is mock_connection
- # =============================================================================
- # Tests für SimpleKeyboardInput - Lifecycle
- # =============================================================================
- class TestSimpleKeyboardInputLifecycle:
- """Tests für SimpleKeyboardInput Lifecycle."""
- @pytest.mark.asyncio
- async def test_start_sets_running(self, simple_input):
- """Testet, dass start() _running setzt."""
- # Patch input() um blocking zu vermeiden
- with patch('builtins.input', return_value=""):
- result = await simple_input.start()
- assert result is True
- assert simple_input._running is True
- assert simple_input._task is not None
- # Cleanup
- await simple_input.stop()
- @pytest.mark.asyncio
- async def test_start_already_running(self, simple_input):
- """Testet start() wenn bereits läuft."""
- simple_input._running = True
- result = await simple_input.start()
- assert result is True
- @pytest.mark.asyncio
- async def test_stop_sets_not_running(self, simple_input):
- """Testet, dass stop() _running zurücksetzt."""
- simple_input._running = True
- simple_input._task = asyncio.create_task(asyncio.sleep(10))
- await simple_input.stop()
- assert simple_input._running is False
- # =============================================================================
- # Tests für SimpleKeyboardInput - Conversation State
- # =============================================================================
- class TestSimpleKeyboardInputConversationState:
- """Tests für SimpleKeyboardInput Conversation State."""
- @pytest.mark.asyncio
- async def test_request_input_follow_up(self, simple_input):
- """Testet request_input für Follow-Up."""
- await simple_input.request_input(
- follow_up_question="Follow-Up Frage?",
- session_id="session-abc"
- )
- assert simple_input._awaiting_follow_up is True
- assert simple_input._follow_up_question == "Follow-Up Frage?"
- assert simple_input._follow_up_number == 1
- assert simple_input._current_session_id == "session-abc"
- def test_handle_conversation_end(self, simple_input):
- """Testet handle_conversation_end."""
- simple_input._current_session_id = "session-end"
- simple_input._awaiting_follow_up = True
- simple_input._follow_up_number = 5
- simple_input.handle_conversation_end("session-end")
- assert simple_input._current_session_id == ""
- assert simple_input._awaiting_follow_up is False
- assert simple_input._follow_up_number == 0
- # =============================================================================
- # Tests für SimpleKeyboardInput - _process_input
- # =============================================================================
- class TestSimpleKeyboardInputProcessInput:
- """Tests für SimpleKeyboardInput _process_input."""
- @pytest.mark.asyncio
- async def test_process_input_standalone(self, simple_input, mock_app):
- """Testet Input-Verarbeitung ohne Connection."""
- await simple_input._process_input("Simple Test")
- mock_app.events.trigger.assert_called_once()
- call_args = mock_app.events.trigger.call_args
- assert call_args[0][0] == "speech_recognized"
- event_data = call_args[0][1]
- assert event_data.text == "Simple Test"
- assert event_data.source == "keyboard"
- @pytest.mark.asyncio
- async def test_process_input_with_connection(self, simple_input_with_connection, mock_connection):
- """Testet Input-Verarbeitung mit Connection."""
- await simple_input_with_connection._process_input("Connected Test")
- mock_connection.send_command.assert_called_once()
- call_args = mock_connection.send_command.call_args
- cmd = call_args[0][0]
- assert isinstance(cmd, TextInput)
- assert cmd.text == "Connected Test"
- @pytest.mark.asyncio
- async def test_process_input_resets_follow_up(self, simple_input):
- """Testet, dass Follow-Up zurückgesetzt wird."""
- simple_input._awaiting_follow_up = True
- simple_input._follow_up_question = "Test?"
- await simple_input._process_input("Antwort")
- assert simple_input._awaiting_follow_up is False
- assert simple_input._follow_up_question == ""
- # =============================================================================
- # Tests für create_keyboard_input Factory
- # =============================================================================
- class TestCreateKeyboardInput:
- """Tests für create_keyboard_input Factory-Funktion."""
- def test_returns_keyboard_input_service_or_simple(self, mock_app):
- """Testet, dass die Factory die richtige Klasse zurückgibt."""
- result = create_keyboard_input(mock_app)
- # Je nach prompt_toolkit Verfügbarkeit
- assert isinstance(result, (KeyboardInputService, SimpleKeyboardInput))
- def test_with_connection(self, mock_app, mock_connection):
- """Testet Factory mit Connection."""
- result = create_keyboard_input(mock_app, connection=mock_connection)
- assert result._connection is mock_connection
- def test_with_custom_prompt(self, mock_app):
- """Testet Factory mit benutzerdefiniertem Prompt."""
- result = create_keyboard_input(mock_app, prompt="Custom> ")
- assert result._prompt == "Custom> "
- def test_returns_simple_when_no_prompt_toolkit(self, mock_app):
- """Testet, dass SimpleKeyboardInput zurückgegeben wird ohne prompt_toolkit."""
- with patch('trixy_core.input.keyboard_input._check_prompt_toolkit', return_value=False):
- # Importiere erneut um den Patch zu nutzen
- from trixy_core.input.keyboard_input import create_keyboard_input as factory
- result = factory(mock_app)
- assert isinstance(result, SimpleKeyboardInput)
- def test_returns_service_when_prompt_toolkit_available(self, mock_app):
- """Testet, dass KeyboardInputService zurückgegeben wird mit prompt_toolkit."""
- with patch('trixy_core.input.keyboard_input._check_prompt_toolkit', return_value=True):
- from trixy_core.input.keyboard_input import create_keyboard_input as factory
- result = factory(mock_app)
- assert isinstance(result, KeyboardInputService)
- # =============================================================================
- # Tests für TextInput Command
- # =============================================================================
- class TestTextInputCommand:
- """Tests für TextInput Command Erstellung."""
- @pytest.mark.asyncio
- async def test_text_input_command_fields(self, keyboard_service_with_connection, mock_connection):
- """Testet, dass alle TextInput Felder korrekt gesetzt werden."""
- keyboard_service_with_connection._current_session_id = "test-session"
- keyboard_service_with_connection._awaiting_follow_up = True
- keyboard_service_with_connection._follow_up_number = 3
- await keyboard_service_with_connection._process_input("Test Text")
- call_args = mock_connection.send_command.call_args
- cmd = call_args[0][0]
- assert cmd.satellite_id == "test_alias"
- assert cmd.text == "Test Text"
- assert cmd.session_id == "test-session"
- assert cmd.room_id == "test_room"
- assert cmd.language == "de"
- assert cmd.is_follow_up is True
- assert cmd.follow_up_number == 3
- # =============================================================================
- # Integration Tests
- # =============================================================================
- class TestKeyboardInputIntegration:
- """Integrationstests für Keyboard Input."""
- @pytest.mark.asyncio
- async def test_full_conversation_flow_standalone(self, keyboard_service, mock_app):
- """Testet vollständigen Conversation-Flow im Standalone-Modus."""
- # Erste Eingabe
- await keyboard_service._process_input("Erste Nachricht")
- assert keyboard_service._current_session_id.startswith("kb-")
- session_id = keyboard_service._current_session_id
- # Follow-Up Request
- await keyboard_service.request_input(
- follow_up_question="Kannst du mehr sagen?",
- session_id=session_id
- )
- assert keyboard_service._awaiting_follow_up is True
- # Follow-Up Antwort
- await keyboard_service._process_input("Mehr Details")
- assert keyboard_service._awaiting_follow_up is False
- # Conversation Ende
- keyboard_service.handle_conversation_end(session_id)
- assert keyboard_service._current_session_id == ""
- @pytest.mark.asyncio
- async def test_full_conversation_flow_client(self, keyboard_service_with_connection, mock_connection):
- """Testet vollständigen Conversation-Flow im Client-Modus."""
- # Erste Eingabe
- await keyboard_service_with_connection._process_input("Client Nachricht")
- # Prüfe ersten Command
- first_call = mock_connection.send_command.call_args_list[0]
- first_cmd = first_call[0][0]
- assert first_cmd.is_follow_up is False
- session_id = keyboard_service_with_connection._current_session_id
- # Follow-Up Request
- await keyboard_service_with_connection.request_input(
- follow_up_question="Rückfrage?",
- session_id=session_id
- )
- # Follow-Up Antwort
- await keyboard_service_with_connection._process_input("Follow-Up Antwort")
- # Prüfe zweiten Command
- second_call = mock_connection.send_command.call_args_list[1]
- second_cmd = second_call[0][0]
- assert second_cmd.is_follow_up is True
- @pytest.mark.asyncio
- async def test_multiple_sessions(self, keyboard_service, mock_app):
- """Testet mehrere unabhängige Sessions."""
- # Erste Session
- await keyboard_service._process_input("Session 1")
- session1 = keyboard_service._current_session_id
- keyboard_service.handle_conversation_end(session1)
- # Zweite Session
- await keyboard_service._process_input("Session 2")
- session2 = keyboard_service._current_session_id
- # Sessions sollten unterschiedlich sein
- assert session1 != session2
- assert session2.startswith("kb-")
- # =============================================================================
- # Edge Cases
- # =============================================================================
- class TestEdgeCases:
- """Tests für Grenzfälle."""
- def test_handle_conversation_end_empty_session(self, keyboard_service):
- """Testet handle_conversation_end mit leerer Session."""
- keyboard_service._current_session_id = ""
- keyboard_service.handle_conversation_end("")
- # Sollte keinen Fehler werfen
- def test_handle_conversation_end_none_like(self, keyboard_service):
- """Testet handle_conversation_end mit None-ähnlichem Wert."""
- keyboard_service._current_session_id = "session-123"
- keyboard_service.handle_conversation_end("")
- # Session sollte unverändert sein
- assert keyboard_service._current_session_id == "session-123"
- @pytest.mark.asyncio
- async def test_process_input_missing_app_attributes(self, mock_app):
- """Testet _process_input wenn App-Attribute fehlen."""
- # Entferne Attribute
- del mock_app.room
- del mock_app.alias
- service = KeyboardInputService(mock_app)
- # Sollte Defaults verwenden
- await service._process_input("Test")
- call_args = mock_app.events.trigger.call_args
- event_data = call_args[0][1]
- assert event_data.room_id == "local"
- assert event_data.satellite_id == "keyboard"
- @pytest.mark.asyncio
- async def test_connection_disconnected(self, keyboard_service_with_connection, mock_connection, mock_app):
- """Testet Verhalten bei getrennter Connection."""
- mock_connection.is_connected = False
- await keyboard_service_with_connection._process_input("Offline Test")
- # Sollte auf Events zurückfallen
- mock_app.events.trigger.assert_called_once()
- mock_connection.send_command.assert_not_called()
- @pytest.mark.asyncio
- async def test_follow_up_request_with_empty_question(self, keyboard_service):
- """Testet handle_follow_up_request mit leerer Frage."""
- request = FollowUpRequest(session_id="session-123", question="")
- keyboard_service.handle_follow_up_request(request)
- await asyncio.sleep(0.01)
- # Sollte keinen Fehler werfen
|