Bump minor version and update ghostty-web
This commit is contained in:
@@ -21,19 +21,19 @@ class TestTerminalSession:
|
||||
|
||||
def test_import(self):
|
||||
"""Test that module can be imported."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
assert TerminalSession is not None
|
||||
|
||||
def test_replay_buffer_size(self):
|
||||
"""Test replay buffer size constant."""
|
||||
from textual_webterm.terminal_session import REPLAY_BUFFER_SIZE
|
||||
from webterm.terminal_session import REPLAY_BUFFER_SIZE
|
||||
|
||||
assert REPLAY_BUFFER_SIZE == 256 * 1024 # 64KB
|
||||
|
||||
def test_init(self):
|
||||
"""Test TerminalSession initialization."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -46,17 +46,26 @@ class TestTerminalSession:
|
||||
|
||||
def test_init_default_shell(self):
|
||||
"""Test that default shell is used when command is empty."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
with patch.dict(os.environ, {"SHELL": "/bin/zsh"}):
|
||||
session = TerminalSession(mock_poller, "test-session", "")
|
||||
assert session.command == "/bin/zsh"
|
||||
|
||||
def test_package_version_fallback(self):
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
with (
|
||||
patch("webterm.terminal_session.version", side_effect=RuntimeError()),
|
||||
patch("webterm.terminal_session.PackageNotFoundError", RuntimeError),
|
||||
):
|
||||
assert TerminalSession._package_version() == "0.0.0"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_replay_buffer_add(self):
|
||||
"""Test adding data to replay buffer."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -68,7 +77,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_replay_buffer_multiple_adds(self):
|
||||
"""Test adding multiple chunks to replay buffer."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -80,7 +89,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_replay_buffer_overflow(self):
|
||||
"""Test that replay buffer trims old data when exceeding limit."""
|
||||
from textual_webterm.terminal_session import (
|
||||
from webterm.terminal_session import (
|
||||
REPLAY_BUFFER_SIZE,
|
||||
TerminalSession,
|
||||
)
|
||||
@@ -99,7 +108,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_screen_state_updates_with_data(self):
|
||||
"""Test that pyte screen updates when data is received."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -114,7 +123,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_screen_handles_cursor_positioning(self):
|
||||
"""Test that pyte screen correctly handles cursor positioning (tmux-style)."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -133,7 +142,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_screen_state_returns_dirty_flag(self):
|
||||
"""Test that get_screen_state returns has_changes flag based on pyte dirty tracking."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -158,7 +167,7 @@ class TestTerminalSession:
|
||||
|
||||
def test_update_connector(self):
|
||||
"""Test updating connector."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -169,7 +178,7 @@ class TestTerminalSession:
|
||||
|
||||
def test_is_running_not_started(self):
|
||||
"""Test is_running when session not started."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -179,7 +188,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_bytes_no_fd(self):
|
||||
"""Test send_bytes returns False when no master_fd."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -190,7 +199,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_meta(self):
|
||||
"""Test send_meta returns True."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -201,7 +210,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_no_pid(self):
|
||||
"""Test close when no pid."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -212,7 +221,7 @@ class TestTerminalSession:
|
||||
@pytest.mark.asyncio
|
||||
async def test_wait_no_task(self):
|
||||
"""Test wait when no task."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -222,7 +231,7 @@ class TestTerminalSession:
|
||||
|
||||
def test_repr(self):
|
||||
"""Test repr output."""
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
session = TerminalSession(mock_poller, "test-session", "bash")
|
||||
@@ -233,7 +242,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_uses_shlex_split_and_execvp_with_args(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
mock_poller = MagicMock()
|
||||
command = 'echo "hello world"'
|
||||
@@ -241,15 +250,15 @@ class TestTerminalSession:
|
||||
|
||||
with (
|
||||
patch(
|
||||
"textual_webterm.terminal_session.pty.fork", return_value=(pty.CHILD, 123)
|
||||
"webterm.terminal_session.pty.fork", return_value=(pty.CHILD, 123)
|
||||
) as mock_fork,
|
||||
patch("textual_webterm.terminal_session.version", return_value="0.0.0"),
|
||||
patch("textual_webterm.terminal_session.shlex.split", wraps=shlex.split) as mock_split,
|
||||
patch("webterm.terminal_session.version", return_value="0.0.0"),
|
||||
patch("webterm.terminal_session.shlex.split", wraps=shlex.split) as mock_split,
|
||||
patch(
|
||||
"textual_webterm.terminal_session.os.execvp", side_effect=OSError()
|
||||
"webterm.terminal_session.os.execvp", side_effect=OSError()
|
||||
) as mock_execvp,
|
||||
patch(
|
||||
"textual_webterm.terminal_session.os._exit", side_effect=SystemExit(1)
|
||||
"webterm.terminal_session.os._exit", side_effect=SystemExit(1)
|
||||
) as mock_exit,
|
||||
pytest.raises(SystemExit),
|
||||
):
|
||||
@@ -262,13 +271,13 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_parent_branch_sets_fd_and_pid(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
|
||||
with (
|
||||
patch("textual_webterm.terminal_session.pty.fork", return_value=(1234, 99)),
|
||||
patch("webterm.terminal_session.pty.fork", return_value=(1234, 99)),
|
||||
patch.object(session, "_set_terminal_size") as set_size,
|
||||
):
|
||||
await session.open(width=80, height=24)
|
||||
@@ -279,16 +288,16 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_bad_command_exits(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bad")
|
||||
|
||||
with (
|
||||
patch("textual_webterm.terminal_session.pty.fork", return_value=(pty.CHILD, 123)),
|
||||
patch("textual_webterm.terminal_session.shlex.split", side_effect=ValueError("bad")),
|
||||
patch("webterm.terminal_session.pty.fork", return_value=(pty.CHILD, 123)),
|
||||
patch("webterm.terminal_session.shlex.split", side_effect=ValueError("bad")),
|
||||
patch(
|
||||
"textual_webterm.terminal_session.os._exit", side_effect=SystemExit(1)
|
||||
"webterm.terminal_session.os._exit", side_effect=SystemExit(1)
|
||||
) as mock_exit,
|
||||
pytest.raises(SystemExit),
|
||||
):
|
||||
@@ -298,7 +307,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_screen_lines_strips(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -319,7 +328,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_screen_state_no_changes(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -352,7 +361,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_screen_state_clears_dirty(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -389,7 +398,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_screen_has_changes_reads_dirty(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -414,7 +423,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_bytes_handles_closed_fd(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
poller.write = AsyncMock(side_effect=KeyError)
|
||||
@@ -426,7 +435,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_reads_from_poller_and_closes(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
queue: asyncio.Queue[bytes | None] = asyncio.Queue()
|
||||
await queue.put(b"hello")
|
||||
@@ -444,7 +453,7 @@ class TestTerminalSession:
|
||||
session.master_fd = 10
|
||||
session._connector = connector
|
||||
|
||||
with patch("textual_webterm.terminal_session.os.close") as mock_close:
|
||||
with patch("webterm.terminal_session.os.close") as mock_close:
|
||||
await session.run()
|
||||
|
||||
connector.on_data.assert_awaited_once_with(b"hello")
|
||||
@@ -454,7 +463,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_updates_connector_when_already_running(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -472,7 +481,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_bytes_writes_via_poller(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
poller.write = AsyncMock()
|
||||
@@ -485,15 +494,15 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_open_set_terminal_size_oserror_closes_fd_and_clears_master_fd(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
|
||||
with (
|
||||
patch("textual_webterm.terminal_session.pty.fork", return_value=(1234, 99)),
|
||||
patch("webterm.terminal_session.pty.fork", return_value=(1234, 99)),
|
||||
patch.object(session, "_set_terminal_size", side_effect=OSError("bad")),
|
||||
patch("textual_webterm.terminal_session.os.close") as mock_close,
|
||||
patch("webterm.terminal_session.os.close") as mock_close,
|
||||
pytest.raises(OSError),
|
||||
):
|
||||
await session.open(width=80, height=24)
|
||||
@@ -503,7 +512,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_set_terminal_size_uses_executor(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -516,20 +525,20 @@ class TestTerminalSession:
|
||||
run_in_executor.assert_awaited_once_with(None, session._set_terminal_size, 80, 24)
|
||||
|
||||
def test__set_terminal_size_calls_ioctl(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
session.master_fd = 10
|
||||
|
||||
with patch("textual_webterm.terminal_session.fcntl.ioctl") as mock_ioctl:
|
||||
with patch("webterm.terminal_session.fcntl.ioctl") as mock_ioctl:
|
||||
session._set_terminal_size(80, 24)
|
||||
|
||||
assert mock_ioctl.called
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_start_creates_task_when_not_running(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -547,7 +556,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_without_connector_still_closes(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
queue: asyncio.Queue[bytes | None] = asyncio.Queue()
|
||||
await queue.put(b"hello")
|
||||
@@ -561,7 +570,7 @@ class TestTerminalSession:
|
||||
session.master_fd = 10
|
||||
session._connector = None
|
||||
|
||||
with patch("textual_webterm.terminal_session.os.close") as mock_close:
|
||||
with patch("webterm.terminal_session.os.close") as mock_close:
|
||||
await session.run()
|
||||
|
||||
poller.remove_file.assert_called_once_with(10)
|
||||
@@ -569,7 +578,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_oserror_still_closes(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
queue = MagicMock()
|
||||
queue.get = AsyncMock(side_effect=OSError("boom"))
|
||||
@@ -582,7 +591,7 @@ class TestTerminalSession:
|
||||
session.master_fd = 10
|
||||
session._connector = None
|
||||
|
||||
with patch("textual_webterm.terminal_session.os.close") as mock_close:
|
||||
with patch("webterm.terminal_session.os.close") as mock_close:
|
||||
await session.run()
|
||||
|
||||
poller.remove_file.assert_called_once_with(10)
|
||||
@@ -590,26 +599,26 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_process_lookup_error_is_ignored(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
session.pid = 123
|
||||
|
||||
with patch("textual_webterm.terminal_session.os.kill", side_effect=ProcessLookupError()):
|
||||
with patch("webterm.terminal_session.os.kill", side_effect=ProcessLookupError()):
|
||||
await session.close()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_close_logs_warning_on_unexpected_exception(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
session.pid = 123
|
||||
|
||||
with (
|
||||
patch("textual_webterm.terminal_session.os.kill", side_effect=RuntimeError("x")),
|
||||
patch("textual_webterm.terminal_session.log.warning") as warn,
|
||||
patch("webterm.terminal_session.os.kill", side_effect=RuntimeError("x")),
|
||||
patch("webterm.terminal_session.log.warning") as warn,
|
||||
):
|
||||
await session.close()
|
||||
|
||||
@@ -617,7 +626,7 @@ class TestTerminalSession:
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_wait_suppresses_cancelled_error(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -629,7 +638,7 @@ class TestTerminalSession:
|
||||
await session.wait()
|
||||
|
||||
def test_is_running_false_when_kill_fails(self):
|
||||
from textual_webterm.terminal_session import TerminalSession
|
||||
from webterm.terminal_session import TerminalSession
|
||||
|
||||
poller = MagicMock()
|
||||
session = TerminalSession(poller, "sid", "bash")
|
||||
@@ -637,5 +646,5 @@ class TestTerminalSession:
|
||||
session._task = MagicMock()
|
||||
session.pid = 123
|
||||
|
||||
with patch("textual_webterm.terminal_session.os.kill", side_effect=OSError()):
|
||||
with patch("webterm.terminal_session.os.kill", side_effect=OSError()):
|
||||
assert session.is_running() is False
|
||||
|
||||
Reference in New Issue
Block a user