code-puppy 0.0.154__py3-none-any.whl → 0.0.156__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- code_puppy/agent.py +26 -5
- code_puppy/agents/agent_creator_agent.py +65 -13
- code_puppy/agents/json_agent.py +8 -0
- code_puppy/agents/runtime_manager.py +12 -4
- code_puppy/command_line/command_handler.py +83 -0
- code_puppy/command_line/mcp/install_command.py +50 -1
- code_puppy/command_line/mcp/wizard_utils.py +88 -17
- code_puppy/command_line/prompt_toolkit_completion.py +18 -2
- code_puppy/config.py +8 -2
- code_puppy/main.py +17 -4
- code_puppy/mcp/__init__.py +2 -2
- code_puppy/mcp/config_wizard.py +1 -1
- code_puppy/messaging/spinner/console_spinner.py +1 -1
- code_puppy/model_factory.py +13 -12
- code_puppy/models.json +26 -0
- code_puppy/round_robin_model.py +35 -18
- code_puppy/summarization_agent.py +1 -3
- code_puppy/tools/agent_tools.py +41 -138
- code_puppy/tools/file_operations.py +116 -96
- code_puppy/tui/app.py +1 -1
- {code_puppy-0.0.154.data → code_puppy-0.0.156.data}/data/code_puppy/models.json +26 -0
- {code_puppy-0.0.154.dist-info → code_puppy-0.0.156.dist-info}/METADATA +4 -3
- {code_puppy-0.0.154.dist-info → code_puppy-0.0.156.dist-info}/RECORD +26 -48
- code_puppy/token_utils.py +0 -67
- code_puppy/tools/token_check.py +0 -32
- code_puppy/tui/tests/__init__.py +0 -1
- code_puppy/tui/tests/test_agent_command.py +0 -79
- code_puppy/tui/tests/test_chat_message.py +0 -28
- code_puppy/tui/tests/test_chat_view.py +0 -88
- code_puppy/tui/tests/test_command_history.py +0 -89
- code_puppy/tui/tests/test_copy_button.py +0 -191
- code_puppy/tui/tests/test_custom_widgets.py +0 -27
- code_puppy/tui/tests/test_disclaimer.py +0 -27
- code_puppy/tui/tests/test_enums.py +0 -15
- code_puppy/tui/tests/test_file_browser.py +0 -60
- code_puppy/tui/tests/test_help.py +0 -38
- code_puppy/tui/tests/test_history_file_reader.py +0 -107
- code_puppy/tui/tests/test_input_area.py +0 -33
- code_puppy/tui/tests/test_settings.py +0 -44
- code_puppy/tui/tests/test_sidebar.py +0 -33
- code_puppy/tui/tests/test_sidebar_history.py +0 -153
- code_puppy/tui/tests/test_sidebar_history_navigation.py +0 -132
- code_puppy/tui/tests/test_status_bar.py +0 -54
- code_puppy/tui/tests/test_timestamped_history.py +0 -52
- code_puppy/tui/tests/test_tools.py +0 -82
- {code_puppy-0.0.154.dist-info → code_puppy-0.0.156.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.154.dist-info → code_puppy-0.0.156.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.154.dist-info → code_puppy-0.0.156.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Tests for the copy button component.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from unittest.mock import MagicMock, patch
|
|
6
|
-
|
|
7
|
-
from code_puppy.tui.components.copy_button import CopyButton
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class TestCopyButton:
|
|
11
|
-
"""Test cases for the CopyButton widget."""
|
|
12
|
-
|
|
13
|
-
def test_copy_button_creation(self):
|
|
14
|
-
"""Test that a copy button can be created with text."""
|
|
15
|
-
test_text = "Hello, World!"
|
|
16
|
-
button = CopyButton(test_text)
|
|
17
|
-
|
|
18
|
-
assert button.text_to_copy == test_text
|
|
19
|
-
assert button.label == "📋 Copy"
|
|
20
|
-
|
|
21
|
-
def test_update_text_to_copy(self):
|
|
22
|
-
"""Test updating the text to copy."""
|
|
23
|
-
button = CopyButton("Initial text")
|
|
24
|
-
new_text = "Updated text"
|
|
25
|
-
|
|
26
|
-
button.update_text_to_copy(new_text)
|
|
27
|
-
|
|
28
|
-
assert button.text_to_copy == new_text
|
|
29
|
-
|
|
30
|
-
@patch("subprocess.run")
|
|
31
|
-
def test_copy_to_clipboard_macos_success(self, mock_run):
|
|
32
|
-
"""Test successful clipboard copy on macOS."""
|
|
33
|
-
mock_run.return_value = MagicMock(returncode=0)
|
|
34
|
-
|
|
35
|
-
with patch("sys.platform", "darwin"):
|
|
36
|
-
button = CopyButton("test content")
|
|
37
|
-
success, error = button.copy_to_clipboard("test content")
|
|
38
|
-
|
|
39
|
-
assert success is True
|
|
40
|
-
assert error is None
|
|
41
|
-
mock_run.assert_called_once_with(
|
|
42
|
-
["pbcopy"],
|
|
43
|
-
input="test content",
|
|
44
|
-
text=True,
|
|
45
|
-
check=True,
|
|
46
|
-
capture_output=True,
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
@patch("subprocess.run")
|
|
50
|
-
def test_copy_to_clipboard_windows_success(self, mock_run):
|
|
51
|
-
"""Test successful clipboard copy on Windows."""
|
|
52
|
-
mock_run.return_value = MagicMock(returncode=0)
|
|
53
|
-
|
|
54
|
-
with patch("sys.platform", "win32"):
|
|
55
|
-
button = CopyButton("test content")
|
|
56
|
-
success, error = button.copy_to_clipboard("test content")
|
|
57
|
-
|
|
58
|
-
assert success is True
|
|
59
|
-
assert error is None
|
|
60
|
-
mock_run.assert_called_once_with(
|
|
61
|
-
["clip"],
|
|
62
|
-
input="test content",
|
|
63
|
-
text=True,
|
|
64
|
-
check=True,
|
|
65
|
-
capture_output=True,
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
@patch("subprocess.run")
|
|
69
|
-
def test_copy_to_clipboard_linux_success(self, mock_run):
|
|
70
|
-
"""Test successful clipboard copy on Linux with xclip."""
|
|
71
|
-
mock_run.return_value = MagicMock(returncode=0)
|
|
72
|
-
|
|
73
|
-
with patch("sys.platform", "linux"):
|
|
74
|
-
button = CopyButton("test content")
|
|
75
|
-
success, error = button.copy_to_clipboard("test content")
|
|
76
|
-
|
|
77
|
-
assert success is True
|
|
78
|
-
assert error is None
|
|
79
|
-
mock_run.assert_called_once_with(
|
|
80
|
-
["xclip", "-selection", "clipboard"],
|
|
81
|
-
input="test content",
|
|
82
|
-
text=True,
|
|
83
|
-
check=True,
|
|
84
|
-
capture_output=True,
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
@patch("subprocess.run")
|
|
88
|
-
def test_copy_to_clipboard_linux_xsel_fallback(self, mock_run):
|
|
89
|
-
"""Test Linux clipboard copy falls back to xsel when xclip fails."""
|
|
90
|
-
# First call (xclip) fails, second call (xsel) succeeds
|
|
91
|
-
mock_run.side_effect = [
|
|
92
|
-
FileNotFoundError("xclip not found"),
|
|
93
|
-
MagicMock(returncode=0),
|
|
94
|
-
]
|
|
95
|
-
|
|
96
|
-
with patch("sys.platform", "linux"):
|
|
97
|
-
button = CopyButton("test content")
|
|
98
|
-
success, error = button.copy_to_clipboard("test content")
|
|
99
|
-
|
|
100
|
-
assert success is True
|
|
101
|
-
assert error is None
|
|
102
|
-
assert mock_run.call_count == 2
|
|
103
|
-
# Check that xsel was called as fallback
|
|
104
|
-
mock_run.assert_any_call(
|
|
105
|
-
["xsel", "--clipboard", "--input"],
|
|
106
|
-
input="test content",
|
|
107
|
-
text=True,
|
|
108
|
-
check=True,
|
|
109
|
-
capture_output=True,
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
@patch("subprocess.run")
|
|
113
|
-
def test_copy_to_clipboard_failure(self, mock_run):
|
|
114
|
-
"""Test clipboard copy failure handling."""
|
|
115
|
-
from subprocess import CalledProcessError
|
|
116
|
-
|
|
117
|
-
mock_run.side_effect = CalledProcessError(1, "pbcopy", "Command failed")
|
|
118
|
-
|
|
119
|
-
with patch("sys.platform", "darwin"):
|
|
120
|
-
button = CopyButton("test content")
|
|
121
|
-
success, error = button.copy_to_clipboard("test content")
|
|
122
|
-
|
|
123
|
-
assert success is False
|
|
124
|
-
assert "Clipboard command failed" in error
|
|
125
|
-
|
|
126
|
-
@patch("subprocess.run")
|
|
127
|
-
def test_copy_to_clipboard_no_utility(self, mock_run):
|
|
128
|
-
"""Test clipboard copy when utility is not found."""
|
|
129
|
-
mock_run.side_effect = FileNotFoundError("Command not found")
|
|
130
|
-
|
|
131
|
-
with patch("sys.platform", "linux"):
|
|
132
|
-
button = CopyButton("test content")
|
|
133
|
-
success, error = button.copy_to_clipboard("test content")
|
|
134
|
-
|
|
135
|
-
assert success is False
|
|
136
|
-
assert "Clipboard utilities not found" in error
|
|
137
|
-
|
|
138
|
-
def test_copy_button_labels(self):
|
|
139
|
-
"""Test that copy button has correct labels."""
|
|
140
|
-
button = CopyButton("test")
|
|
141
|
-
|
|
142
|
-
assert button._original_label == "📋 Copy"
|
|
143
|
-
assert button._copied_label == "✅ Copied!"
|
|
144
|
-
|
|
145
|
-
def test_copy_completed_message(self):
|
|
146
|
-
"""Test CopyCompleted message creation."""
|
|
147
|
-
# Test success message
|
|
148
|
-
success_msg = CopyButton.CopyCompleted(True)
|
|
149
|
-
assert success_msg.success is True
|
|
150
|
-
assert success_msg.error is None
|
|
151
|
-
|
|
152
|
-
# Test error message
|
|
153
|
-
error_msg = CopyButton.CopyCompleted(False, "Test error")
|
|
154
|
-
assert error_msg.success is False
|
|
155
|
-
assert error_msg.error == "Test error"
|
|
156
|
-
|
|
157
|
-
@patch.object(CopyButton, "copy_to_clipboard")
|
|
158
|
-
@patch.object(CopyButton, "post_message")
|
|
159
|
-
def test_action_press_success(self, mock_post_message, mock_copy):
|
|
160
|
-
"""Test action_press method with successful copy."""
|
|
161
|
-
mock_copy.return_value = (True, None)
|
|
162
|
-
|
|
163
|
-
button = CopyButton("test content")
|
|
164
|
-
button.action_press()
|
|
165
|
-
|
|
166
|
-
mock_copy.assert_called_once_with("test content")
|
|
167
|
-
mock_post_message.assert_called_once()
|
|
168
|
-
# Note: timer is currently commented out in implementation
|
|
169
|
-
|
|
170
|
-
# Check that the message posted is a CopyCompleted with success=True
|
|
171
|
-
call_args = mock_post_message.call_args[0][0]
|
|
172
|
-
assert isinstance(call_args, CopyButton.CopyCompleted)
|
|
173
|
-
assert call_args.success is True
|
|
174
|
-
|
|
175
|
-
@patch.object(CopyButton, "copy_to_clipboard")
|
|
176
|
-
@patch.object(CopyButton, "post_message")
|
|
177
|
-
def test_action_press_failure(self, mock_post_message, mock_copy):
|
|
178
|
-
"""Test action_press method with failed copy."""
|
|
179
|
-
mock_copy.return_value = (False, "Test error")
|
|
180
|
-
|
|
181
|
-
button = CopyButton("test content")
|
|
182
|
-
button.action_press()
|
|
183
|
-
|
|
184
|
-
mock_copy.assert_called_once_with("test content")
|
|
185
|
-
mock_post_message.assert_called_once()
|
|
186
|
-
|
|
187
|
-
# Check that the message posted is a CopyCompleted with success=False
|
|
188
|
-
call_args = mock_post_message.call_args[0][0]
|
|
189
|
-
assert isinstance(call_args, CopyButton.CopyCompleted)
|
|
190
|
-
assert call_args.success is False
|
|
191
|
-
assert call_args.error == "Test error"
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from code_puppy.tui.components.custom_widgets import CustomTextArea
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class DummyEvent:
|
|
7
|
-
def __init__(self, key):
|
|
8
|
-
self.key = key
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class TestCustomTextArea(unittest.TestCase):
|
|
12
|
-
def setUp(self):
|
|
13
|
-
self.text_area = CustomTextArea()
|
|
14
|
-
|
|
15
|
-
def test_message_sent_on_enter(self):
|
|
16
|
-
# Simulate pressing Enter
|
|
17
|
-
event = DummyEvent("enter")
|
|
18
|
-
# Should not raise
|
|
19
|
-
self.text_area._on_key(event)
|
|
20
|
-
|
|
21
|
-
def test_message_sent_class(self):
|
|
22
|
-
msg = CustomTextArea.MessageSent()
|
|
23
|
-
self.assertIsInstance(msg, CustomTextArea.MessageSent)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if __name__ == "__main__":
|
|
27
|
-
unittest.main()
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from unittest.mock import MagicMock
|
|
3
|
-
|
|
4
|
-
# Skip importing the non-existent module
|
|
5
|
-
# Commenting out: from code_puppy.tui.screens.disclaimer import DisclaimerScreen
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
# We'll use unittest.skip to skip the entire test class
|
|
9
|
-
@unittest.skip("DisclaimerScreen has been removed from the codebase")
|
|
10
|
-
class TestDisclaimerScreen(unittest.TestCase):
|
|
11
|
-
def setUp(self):
|
|
12
|
-
# Create a mock screen instead of the real one
|
|
13
|
-
self.screen = MagicMock()
|
|
14
|
-
self.screen.get_disclaimer_content.return_value = "Prompt responsibly"
|
|
15
|
-
self.screen.compose.return_value = [MagicMock()]
|
|
16
|
-
|
|
17
|
-
def test_get_disclaimer_content(self):
|
|
18
|
-
content = self.screen.get_disclaimer_content()
|
|
19
|
-
self.assertIn("Prompt responsibly", content)
|
|
20
|
-
|
|
21
|
-
def test_compose(self):
|
|
22
|
-
widgets = list(self.screen.compose())
|
|
23
|
-
self.assertGreaterEqual(len(widgets), 1)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if __name__ == "__main__":
|
|
27
|
-
unittest.main()
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from code_puppy.tui.models.enums import MessageType
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
class TestMessageType(unittest.TestCase):
|
|
7
|
-
def test_enum_values(self):
|
|
8
|
-
self.assertEqual(MessageType.USER.value, "user")
|
|
9
|
-
self.assertEqual(MessageType.AGENT.value, "agent")
|
|
10
|
-
self.assertEqual(MessageType.SYSTEM.value, "system")
|
|
11
|
-
self.assertEqual(MessageType.ERROR.value, "error")
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
if __name__ == "__main__":
|
|
15
|
-
unittest.main()
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
"""Tests for the FileBrowser component."""
|
|
2
|
-
|
|
3
|
-
from unittest.mock import MagicMock
|
|
4
|
-
|
|
5
|
-
import pytest
|
|
6
|
-
|
|
7
|
-
# Import only Sidebar which exists, and skip FileBrowser
|
|
8
|
-
from code_puppy.tui.components import Sidebar
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Use pytest.skip for skipping the FileBrowser tests
|
|
12
|
-
@pytest.mark.skip(reason="FileBrowser component has been removed from the codebase")
|
|
13
|
-
class TestFileBrowser:
|
|
14
|
-
"""Test the FileBrowser component."""
|
|
15
|
-
|
|
16
|
-
def test_file_browser_creation(self):
|
|
17
|
-
"""Test that FileBrowser can be created."""
|
|
18
|
-
# Create a mock instead of the real component
|
|
19
|
-
browser = MagicMock()
|
|
20
|
-
assert browser is not None
|
|
21
|
-
|
|
22
|
-
def test_file_browser_has_directory_tree(self):
|
|
23
|
-
"""Test that FileBrowser contains a DirectoryTree widget."""
|
|
24
|
-
browser = MagicMock()
|
|
25
|
-
browser.compose = MagicMock()
|
|
26
|
-
# This is a basic structure test - in a real app test we'd mount it
|
|
27
|
-
assert hasattr(browser, "compose")
|
|
28
|
-
|
|
29
|
-
def test_file_browser_message_type(self):
|
|
30
|
-
"""Test that FileBrowser.FileSelected message works."""
|
|
31
|
-
|
|
32
|
-
# Create a mock message class
|
|
33
|
-
class MockFileSelected:
|
|
34
|
-
def __init__(self, file_path):
|
|
35
|
-
self.file_path = file_path
|
|
36
|
-
|
|
37
|
-
message = MockFileSelected("/test/path/file.py")
|
|
38
|
-
assert message.file_path == "/test/path/file.py"
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
class TestSidebarTabs:
|
|
42
|
-
"""Test the enhanced Sidebar with tabs."""
|
|
43
|
-
|
|
44
|
-
def test_sidebar_creation(self):
|
|
45
|
-
"""Test that enhanced Sidebar can be created."""
|
|
46
|
-
sidebar = Sidebar()
|
|
47
|
-
assert sidebar is not None
|
|
48
|
-
|
|
49
|
-
def test_sidebar_has_compose_method(self):
|
|
50
|
-
"""Test that Sidebar has the compose method for tab layout."""
|
|
51
|
-
sidebar = Sidebar()
|
|
52
|
-
assert hasattr(sidebar, "compose")
|
|
53
|
-
# Skip checking methods that may have been removed
|
|
54
|
-
# Comment out removed methods:
|
|
55
|
-
# assert hasattr(sidebar, "load_models_list")
|
|
56
|
-
# assert hasattr(sidebar, "on_file_browser_file_selected")
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if __name__ == "__main__":
|
|
60
|
-
pytest.main([__file__])
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from textual.app import App
|
|
4
|
-
|
|
5
|
-
from code_puppy.tui.screens.help import HelpScreen
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestHelpScreen(unittest.TestCase):
|
|
9
|
-
def setUp(self):
|
|
10
|
-
self.screen = HelpScreen()
|
|
11
|
-
|
|
12
|
-
def test_get_help_content(self):
|
|
13
|
-
content = self.screen.get_help_content()
|
|
14
|
-
self.assertIn("Code Puppy TUI", content)
|
|
15
|
-
|
|
16
|
-
def test_compose(self):
|
|
17
|
-
# Create a minimal app context for testing
|
|
18
|
-
class TestApp(App):
|
|
19
|
-
def compose(self):
|
|
20
|
-
yield self.screen
|
|
21
|
-
|
|
22
|
-
app = TestApp()
|
|
23
|
-
self.screen = HelpScreen()
|
|
24
|
-
|
|
25
|
-
# Test that compose returns widgets without error
|
|
26
|
-
try:
|
|
27
|
-
# Use app.run_test() context to provide proper app context
|
|
28
|
-
with app:
|
|
29
|
-
widgets = list(self.screen.compose())
|
|
30
|
-
self.assertGreaterEqual(len(widgets), 1)
|
|
31
|
-
except Exception:
|
|
32
|
-
# If compose still fails, just verify the method exists
|
|
33
|
-
self.assertTrue(hasattr(self.screen, "compose"))
|
|
34
|
-
self.assertTrue(callable(getattr(self.screen, "compose")))
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
if __name__ == "__main__":
|
|
38
|
-
unittest.main()
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import tempfile
|
|
3
|
-
import unittest
|
|
4
|
-
|
|
5
|
-
from code_puppy.tui.models.command_history import HistoryFileReader
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestHistoryFileReader(unittest.TestCase):
|
|
9
|
-
def setUp(self):
|
|
10
|
-
# Create a temporary file for testing
|
|
11
|
-
self.temp_file = tempfile.NamedTemporaryFile(delete=False)
|
|
12
|
-
self.temp_file_path = self.temp_file.name
|
|
13
|
-
|
|
14
|
-
# Sample content with multiple commands
|
|
15
|
-
sample_content = """
|
|
16
|
-
# 2023-01-01T12:00:00
|
|
17
|
-
First command
|
|
18
|
-
|
|
19
|
-
# 2023-01-01T13:00:00
|
|
20
|
-
Second command
|
|
21
|
-
with multiple lines
|
|
22
|
-
|
|
23
|
-
# 2023-01-01T14:00:00
|
|
24
|
-
Third command
|
|
25
|
-
"""
|
|
26
|
-
# Write sample content to the temporary file
|
|
27
|
-
with open(self.temp_file_path, "w") as f:
|
|
28
|
-
f.write(sample_content)
|
|
29
|
-
|
|
30
|
-
# Initialize reader with the temp file
|
|
31
|
-
self.reader = HistoryFileReader(self.temp_file_path)
|
|
32
|
-
|
|
33
|
-
def tearDown(self):
|
|
34
|
-
# Clean up the temporary file
|
|
35
|
-
if os.path.exists(self.temp_file_path):
|
|
36
|
-
os.unlink(self.temp_file_path)
|
|
37
|
-
|
|
38
|
-
def test_read_history(self):
|
|
39
|
-
# Test reading history entries
|
|
40
|
-
entries = self.reader.read_history()
|
|
41
|
-
|
|
42
|
-
# Check that we have the correct number of entries
|
|
43
|
-
self.assertEqual(len(entries), 3)
|
|
44
|
-
|
|
45
|
-
# Check that entries are in reverse chronological order (newest first)
|
|
46
|
-
self.assertEqual(entries[0]["timestamp"], "2023-01-01T14:00:00")
|
|
47
|
-
self.assertEqual(entries[0]["command"], "Third command")
|
|
48
|
-
|
|
49
|
-
self.assertEqual(entries[1]["timestamp"], "2023-01-01T13:00:00")
|
|
50
|
-
self.assertEqual(entries[1]["command"], "Second command\nwith multiple lines")
|
|
51
|
-
|
|
52
|
-
self.assertEqual(entries[2]["timestamp"], "2023-01-01T12:00:00")
|
|
53
|
-
self.assertEqual(entries[2]["command"], "First command")
|
|
54
|
-
|
|
55
|
-
def test_read_history_with_limit(self):
|
|
56
|
-
# Test reading history with a limit
|
|
57
|
-
entries = self.reader.read_history(max_entries=2)
|
|
58
|
-
|
|
59
|
-
# Check that we only get the specified number of entries
|
|
60
|
-
self.assertEqual(len(entries), 2)
|
|
61
|
-
|
|
62
|
-
# Check that we get the most recent entries
|
|
63
|
-
self.assertEqual(entries[0]["timestamp"], "2023-01-01T14:00:00")
|
|
64
|
-
self.assertEqual(entries[1]["timestamp"], "2023-01-01T13:00:00")
|
|
65
|
-
|
|
66
|
-
def test_read_history_empty_file(self):
|
|
67
|
-
# Create an empty file
|
|
68
|
-
empty_file = tempfile.NamedTemporaryFile(delete=False)
|
|
69
|
-
empty_file_path = empty_file.name
|
|
70
|
-
empty_file.close()
|
|
71
|
-
|
|
72
|
-
try:
|
|
73
|
-
# Create reader with empty file
|
|
74
|
-
empty_reader = HistoryFileReader(empty_file_path)
|
|
75
|
-
|
|
76
|
-
# Should return empty list
|
|
77
|
-
entries = empty_reader.read_history()
|
|
78
|
-
self.assertEqual(len(entries), 0)
|
|
79
|
-
finally:
|
|
80
|
-
# Clean up
|
|
81
|
-
if os.path.exists(empty_file_path):
|
|
82
|
-
os.unlink(empty_file_path)
|
|
83
|
-
|
|
84
|
-
def test_read_history_nonexistent_file(self):
|
|
85
|
-
# Create reader with non-existent file
|
|
86
|
-
nonexistent_reader = HistoryFileReader("/nonexistent/file/path")
|
|
87
|
-
|
|
88
|
-
# Should return empty list, not raise an exception
|
|
89
|
-
entries = nonexistent_reader.read_history()
|
|
90
|
-
self.assertEqual(len(entries), 0)
|
|
91
|
-
|
|
92
|
-
def test_format_timestamp(self):
|
|
93
|
-
# Test default formatting
|
|
94
|
-
formatted = self.reader.format_timestamp("2023-01-01T12:34:56")
|
|
95
|
-
self.assertEqual(formatted, "12:34:56")
|
|
96
|
-
|
|
97
|
-
# Test custom format
|
|
98
|
-
formatted = self.reader.format_timestamp("2023-01-01T12:34:56", "%H:%M")
|
|
99
|
-
self.assertEqual(formatted, "12:34")
|
|
100
|
-
|
|
101
|
-
# Test invalid timestamp
|
|
102
|
-
formatted = self.reader.format_timestamp("invalid")
|
|
103
|
-
self.assertEqual(formatted, "invalid")
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if __name__ == "__main__":
|
|
107
|
-
unittest.main()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from textual.app import App
|
|
4
|
-
|
|
5
|
-
from code_puppy.tui.components.input_area import InputArea
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestInputArea(unittest.TestCase):
|
|
9
|
-
def setUp(self):
|
|
10
|
-
self.input_area = InputArea()
|
|
11
|
-
|
|
12
|
-
def test_compose(self):
|
|
13
|
-
# Create a minimal app context for testing
|
|
14
|
-
class TestApp(App):
|
|
15
|
-
def compose(self):
|
|
16
|
-
yield self.input_area
|
|
17
|
-
|
|
18
|
-
app = TestApp()
|
|
19
|
-
self.input_area = InputArea()
|
|
20
|
-
|
|
21
|
-
# Test that compose returns widgets without error
|
|
22
|
-
try:
|
|
23
|
-
with app:
|
|
24
|
-
widgets = list(self.input_area.compose())
|
|
25
|
-
self.assertGreaterEqual(len(widgets), 3)
|
|
26
|
-
except Exception:
|
|
27
|
-
# If compose still fails, just verify the method exists
|
|
28
|
-
self.assertTrue(hasattr(self.input_area, "compose"))
|
|
29
|
-
self.assertTrue(callable(getattr(self.input_area, "compose")))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if __name__ == "__main__":
|
|
33
|
-
unittest.main()
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from textual.app import App
|
|
4
|
-
|
|
5
|
-
from code_puppy.tui.screens.settings import SettingsScreen
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestSettingsScreen(unittest.TestCase):
|
|
9
|
-
def setUp(self):
|
|
10
|
-
self.screen = SettingsScreen()
|
|
11
|
-
|
|
12
|
-
def test_compose(self):
|
|
13
|
-
# Create a minimal app context for testing
|
|
14
|
-
class TestApp(App):
|
|
15
|
-
def compose(self):
|
|
16
|
-
yield self.screen
|
|
17
|
-
|
|
18
|
-
app = TestApp()
|
|
19
|
-
self.screen = SettingsScreen()
|
|
20
|
-
|
|
21
|
-
# Test that compose returns widgets without error
|
|
22
|
-
try:
|
|
23
|
-
with app:
|
|
24
|
-
widgets = list(self.screen.compose())
|
|
25
|
-
self.assertGreaterEqual(len(widgets), 1)
|
|
26
|
-
except Exception:
|
|
27
|
-
# If compose still fails, just verify the method exists
|
|
28
|
-
self.assertTrue(hasattr(self.screen, "compose"))
|
|
29
|
-
self.assertTrue(callable(getattr(self.screen, "compose")))
|
|
30
|
-
|
|
31
|
-
def test_load_model_options_fallback(self):
|
|
32
|
-
class DummySelect:
|
|
33
|
-
def set_options(self, options):
|
|
34
|
-
self.options = options
|
|
35
|
-
|
|
36
|
-
select = DummySelect()
|
|
37
|
-
# Should fallback to default if file not found
|
|
38
|
-
self.screen.load_model_options(select)
|
|
39
|
-
self.assertTrue(hasattr(select, "options"))
|
|
40
|
-
self.assertGreaterEqual(len(select.options), 1)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if __name__ == "__main__":
|
|
44
|
-
unittest.main()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
|
|
3
|
-
from textual.app import App
|
|
4
|
-
|
|
5
|
-
from code_puppy.tui.components.sidebar import Sidebar
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class TestSidebar(unittest.TestCase):
|
|
9
|
-
def setUp(self):
|
|
10
|
-
self.sidebar = Sidebar()
|
|
11
|
-
|
|
12
|
-
def test_compose(self):
|
|
13
|
-
# Create a minimal app context for testing
|
|
14
|
-
class TestApp(App):
|
|
15
|
-
def compose(self):
|
|
16
|
-
yield self.sidebar
|
|
17
|
-
|
|
18
|
-
app = TestApp()
|
|
19
|
-
self.sidebar = Sidebar()
|
|
20
|
-
|
|
21
|
-
# Test that compose returns widgets without error
|
|
22
|
-
try:
|
|
23
|
-
with app:
|
|
24
|
-
widgets = list(self.sidebar.compose())
|
|
25
|
-
self.assertGreaterEqual(len(widgets), 1)
|
|
26
|
-
except Exception:
|
|
27
|
-
# If compose still fails, just verify the method exists
|
|
28
|
-
self.assertTrue(hasattr(self.sidebar, "compose"))
|
|
29
|
-
self.assertTrue(callable(getattr(self.sidebar, "compose")))
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
if __name__ == "__main__":
|
|
33
|
-
unittest.main()
|