Don't resize terminal on session disconnect
Rename DISCONNECT_RESIZE to DEFAULT_TERMINAL_SIZE Update tests for removed _resize_on_disconnect and stricter available check Bump version to 0.2.7
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[tool.poetry]
|
[tool.poetry]
|
||||||
name = "textual-webterm"
|
name = "textual-webterm"
|
||||||
version = "0.2.6"
|
version = "0.2.7"
|
||||||
description = "Serve terminal sessions over the web"
|
description = "Serve terminal sessions over the web"
|
||||||
authors = ["Will McGugan <will@textualize.io>"]
|
authors = ["Will McGugan <will@textualize.io>"]
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
log = logging.getLogger("textual-web")
|
log = logging.getLogger("textual-web")
|
||||||
|
|
||||||
DISCONNECT_RESIZE = (132, 45)
|
DEFAULT_TERMINAL_SIZE = (132, 45)
|
||||||
|
|
||||||
SCREENSHOT_CACHE_SECONDS = 1.0
|
SCREENSHOT_CACHE_SECONDS = 1.0
|
||||||
SCREENSHOT_MAX_CACHE_SECONDS = 60.0
|
SCREENSHOT_MAX_CACHE_SECONDS = 60.0
|
||||||
@@ -431,14 +431,6 @@ class LocalServer:
|
|||||||
|
|
||||||
return session_created
|
return session_created
|
||||||
|
|
||||||
async def _resize_on_disconnect(self, route_key: str) -> None:
|
|
||||||
session_process = self.session_manager.get_session_by_route_key(RouteKey(route_key))
|
|
||||||
if session_process is None or not hasattr(session_process, "set_terminal_size"):
|
|
||||||
return
|
|
||||||
width, height = DISCONNECT_RESIZE
|
|
||||||
with contextlib.suppress(OSError):
|
|
||||||
await session_process.set_terminal_size(width, height)
|
|
||||||
|
|
||||||
async def _handle_websocket(self, request: web.Request) -> web.WebSocketResponse:
|
async def _handle_websocket(self, request: web.Request) -> web.WebSocketResponse:
|
||||||
route_key = request.match_info["route_key"]
|
route_key = request.match_info["route_key"]
|
||||||
ws = web.WebSocketResponse(heartbeat=30.0, max_msg_size=64 * 1024)
|
ws = web.WebSocketResponse(heartbeat=30.0, max_msg_size=64 * 1024)
|
||||||
@@ -478,7 +470,6 @@ class LocalServer:
|
|||||||
finally:
|
finally:
|
||||||
log.info("WebSocket connection closed for route %s", route_key)
|
log.info("WebSocket connection closed for route %s", route_key)
|
||||||
self._websocket_connections.pop(route_key, None)
|
self._websocket_connections.pop(route_key, None)
|
||||||
await self._resize_on_disconnect(route_key)
|
|
||||||
|
|
||||||
return ws
|
return ws
|
||||||
|
|
||||||
@@ -535,15 +526,15 @@ class LocalServer:
|
|||||||
|
|
||||||
# Parse requested dimensions (used when creating new sessions)
|
# Parse requested dimensions (used when creating new sessions)
|
||||||
try:
|
try:
|
||||||
req_width = int(request.query.get("width", str(DISCONNECT_RESIZE[0])))
|
req_width = int(request.query.get("width", str(DEFAULT_TERMINAL_SIZE[0])))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
req_width = DISCONNECT_RESIZE[0]
|
req_width = DEFAULT_TERMINAL_SIZE[0]
|
||||||
req_width = max(10, min(400, req_width))
|
req_width = max(10, min(400, req_width))
|
||||||
|
|
||||||
try:
|
try:
|
||||||
req_height = int(request.query.get("height", str(DISCONNECT_RESIZE[1])))
|
req_height = int(request.query.get("height", str(DEFAULT_TERMINAL_SIZE[1])))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
req_height = DISCONNECT_RESIZE[1]
|
req_height = DEFAULT_TERMINAL_SIZE[1]
|
||||||
req_height = max(5, min(200, req_height))
|
req_height = max(5, min(200, req_height))
|
||||||
|
|
||||||
session_process = self.session_manager.get_session_by_route_key(RouteKey(route_key))
|
session_process = self.session_manager.get_session_by_route_key(RouteKey(route_key))
|
||||||
|
|||||||
@@ -67,13 +67,16 @@ class TestDockerStatsCollector:
|
|||||||
"""Tests for Docker stats collector."""
|
"""Tests for Docker stats collector."""
|
||||||
|
|
||||||
def test_available_checks_socket(self, tmp_path):
|
def test_available_checks_socket(self, tmp_path):
|
||||||
"""available property checks socket existence."""
|
"""available property checks socket existence and connectivity."""
|
||||||
socket_path = tmp_path / "docker.sock"
|
socket_path = tmp_path / "docker.sock"
|
||||||
collector = DockerStatsCollector(str(socket_path))
|
collector = DockerStatsCollector(str(socket_path))
|
||||||
assert collector.available is False
|
assert collector.available is False
|
||||||
|
|
||||||
|
# Just touching the file isn't enough - need actual socket connectivity
|
||||||
|
# Since we can't easily create a real Unix socket in tests,
|
||||||
|
# verify that a non-socket file returns False
|
||||||
socket_path.touch()
|
socket_path.touch()
|
||||||
assert collector.available is True
|
assert collector.available is False # File exists but can't connect
|
||||||
|
|
||||||
def test_get_cpu_history_empty(self):
|
def test_get_cpu_history_empty(self):
|
||||||
"""Empty history returns empty list."""
|
"""Empty history returns empty list."""
|
||||||
|
|||||||
@@ -167,17 +167,6 @@ class TestLocalServerHelpers:
|
|||||||
assert session_created is True
|
assert session_created is True
|
||||||
ws.send_json.assert_awaited_once_with(["error", "No app configured"])
|
ws.send_json.assert_awaited_once_with(["error", "No app configured"])
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
|
||||||
async def test_resize_on_disconnect_calls_set_terminal_size(self, server, monkeypatch):
|
|
||||||
session = MagicMock()
|
|
||||||
session.set_terminal_size = AsyncMock()
|
|
||||||
|
|
||||||
monkeypatch.setattr(server.session_manager, "get_session_by_route_key", lambda _rk: session)
|
|
||||||
|
|
||||||
await server._resize_on_disconnect("rk")
|
|
||||||
|
|
||||||
session.set_terminal_size.assert_called_once_with(132, 45)
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.asyncio
|
||||||
async def test_create_terminal_session_sends_error_if_no_apps(self, server):
|
async def test_create_terminal_session_sends_error_if_no_apps(self, server):
|
||||||
ws = MagicMock()
|
ws = MagicMock()
|
||||||
|
|||||||
Reference in New Issue
Block a user