Review-ID: 36
Previous-SHA: 14302a26f9
Plan-SHA256: db38eaf9d5fcf09a0a9d62f44c373eb67bfd6fcffdc40fef5f67762f0d28c477
Bundle-SHA256: 29ce1ef1917864f79938459725201e6e74d67e21b1f817ac53d7b294eca8f1a2
170 lines
6.6 KiB
Python
170 lines
6.6 KiB
Python
import unittest
|
|
from unittest.mock import patch
|
|
|
|
from textual.widgets import Input
|
|
|
|
from chat_tui.app import (
|
|
RESPONSE_MESSAGES,
|
|
ChatApp,
|
|
ChatMessage,
|
|
MessageRole,
|
|
)
|
|
|
|
|
|
class ChatAppTest(unittest.IsolatedAsyncioTestCase):
|
|
async def test_submitted_input_and_response_are_displayed_in_order(self) -> None:
|
|
app = ChatApp()
|
|
with patch("chat_tui.app.random.choice", return_value=RESPONSE_MESSAGES[2]):
|
|
async with app.run_test() as pilot:
|
|
input_widget = app.query_one("#user-input", Input)
|
|
input_widget.value = " 첫 번째 입력 "
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
input_widget.value = "두 번째 입력"
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
self.assertEqual(
|
|
app.submitted_inputs,
|
|
["첫 번째 입력", "두 번째 입력"],
|
|
)
|
|
self.assertEqual(
|
|
app.selected_responses,
|
|
[RESPONSE_MESSAGES[2], RESPONSE_MESSAGES[2]],
|
|
)
|
|
self.assertEqual(
|
|
app.messages,
|
|
[
|
|
ChatMessage(MessageRole.USER, "첫 번째 입력"),
|
|
ChatMessage(MessageRole.AGENT, RESPONSE_MESSAGES[2]),
|
|
ChatMessage(MessageRole.USER, "두 번째 입력"),
|
|
ChatMessage(MessageRole.AGENT, RESPONSE_MESSAGES[2]),
|
|
],
|
|
)
|
|
displayed = [
|
|
str(label.renderable)
|
|
for label in app.query("#conversation Label")
|
|
]
|
|
self.assertEqual(
|
|
displayed,
|
|
[
|
|
"사용자: 첫 번째 입력",
|
|
f"에이전트: {RESPONSE_MESSAGES[2]}",
|
|
"사용자: 두 번째 입력",
|
|
f"에이전트: {RESPONSE_MESSAGES[2]}",
|
|
],
|
|
)
|
|
self.assertEqual(input_widget.value, "")
|
|
|
|
async def test_messages_have_explicit_roles_labels_and_structural_styles(
|
|
self,
|
|
) -> None:
|
|
app = ChatApp()
|
|
with patch("chat_tui.app.random.choice", return_value=RESPONSE_MESSAGES[0]):
|
|
async with app.run_test() as pilot:
|
|
input_widget = app.query_one("#user-input", Input)
|
|
input_widget.value = "역할 확인"
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
labels = list(app.query("#conversation Label"))
|
|
self.assertEqual(len(labels), 2)
|
|
self.assertEqual(
|
|
labels[0].classes,
|
|
frozenset({"chat-message", "user-message", "user-role"}),
|
|
)
|
|
self.assertEqual(
|
|
labels[1].classes,
|
|
frozenset({"chat-message", "response-message", "agent-role"}),
|
|
)
|
|
self.assertEqual(
|
|
str(labels[0].renderable),
|
|
"사용자: 역할 확인",
|
|
)
|
|
self.assertEqual(
|
|
str(labels[1].renderable),
|
|
f"에이전트: {RESPONSE_MESSAGES[0]}",
|
|
)
|
|
self.assertIn("border-left", app.CSS)
|
|
self.assertIn("border-right", app.CSS)
|
|
self.assertIn("align: left middle", app.CSS)
|
|
self.assertIn("align: right middle", app.CSS)
|
|
|
|
async def test_rendering_styles_are_derived_from_message_roles(self) -> None:
|
|
app = ChatApp()
|
|
async with app.run_test() as pilot:
|
|
conversation = app.query_one("#conversation")
|
|
app._mount_message(
|
|
conversation,
|
|
ChatMessage(MessageRole.USER, "사용자 본문"),
|
|
)
|
|
app._mount_message(
|
|
conversation,
|
|
ChatMessage(MessageRole.AGENT, "에이전트 본문"),
|
|
)
|
|
await pilot.pause()
|
|
|
|
labels = list(app.query("#conversation Label"))
|
|
self.assertEqual(len(labels), 2)
|
|
self.assertEqual(
|
|
labels[0].classes,
|
|
frozenset({"chat-message", "user-message", "user-role"}),
|
|
)
|
|
self.assertEqual(
|
|
labels[1].classes,
|
|
frozenset({"chat-message", "response-message", "agent-role"}),
|
|
)
|
|
self.assertEqual(str(labels[0].renderable), "사용자: 사용자 본문")
|
|
self.assertEqual(str(labels[1].renderable), "에이전트: 에이전트 본문")
|
|
|
|
async def test_response_selection_uses_standard_random_choice(self) -> None:
|
|
app = ChatApp()
|
|
with patch(
|
|
"chat_tui.app.random.choice",
|
|
return_value=RESPONSE_MESSAGES[4],
|
|
) as choice_mock:
|
|
async with app.run_test() as pilot:
|
|
input_widget = app.query_one("#user-input", Input)
|
|
input_widget.value = "처리할 입력"
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
choice_mock.assert_called_once_with(RESPONSE_MESSAGES)
|
|
|
|
async def test_whitespace_input_is_not_added(self) -> None:
|
|
app = ChatApp()
|
|
with patch("chat_tui.app.random.choice") as choice_mock:
|
|
async with app.run_test() as pilot:
|
|
input_widget = app.query_one("#user-input", Input)
|
|
input_widget.value = " \t \n"
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
self.assertEqual(app.submitted_inputs, [])
|
|
self.assertEqual(app.selected_responses, [])
|
|
self.assertEqual(app.messages, [])
|
|
self.assertEqual(len(app.query("#conversation Label")), 0)
|
|
self.assertEqual(input_widget.value, "")
|
|
choice_mock.assert_not_called()
|
|
|
|
async def test_empty_input_is_not_added(self) -> None:
|
|
app = ChatApp()
|
|
async with app.run_test() as pilot:
|
|
await pilot.press("enter")
|
|
await pilot.pause()
|
|
|
|
self.assertEqual(app.submitted_inputs, [])
|
|
self.assertEqual(app.selected_responses, [])
|
|
self.assertEqual(app.messages, [])
|
|
self.assertEqual(len(app.query("#conversation Label")), 0)
|
|
|
|
async def test_escape_requests_application_exit(self) -> None:
|
|
app = ChatApp()
|
|
with patch.object(app, "exit") as exit_mock:
|
|
async with app.run_test() as pilot:
|
|
await pilot.press("escape")
|
|
await pilot.pause()
|
|
|
|
exit_mock.assert_called_once_with()
|