a1a77c99a6
Multiline pastes into the web terminal (especially into pi) were split into one prompt per line because bracketed-paste markers were not reaching the foreground app intact. - Put the host PTY into raw mode (tty.setraw) after openpty() so it acts as a pass-through pipe. The default canonical line discipline was line-buffering input, splitting multiline pastes at newlines, and mangling bracketed-paste markers before docker exec / pi could see them. The in-container PTY (docker exec -t) provides real discipline. - Route the mobile Paste button through xterm.js (term.paste) instead of sending raw clipboard text to the WebSocket, so content is wrapped in bracketed-paste markers when the app has enabled BPM. - Treat a text frame as a control message only when it is a JSON object with a known type (resize/ack/reset); otherwise forward as raw input so JSON-shaped pastes are no longer silently dropped. Quality gates: ruff, mypy (changed files), pytest unit (227 passed), tsc, eslint
67 lines
2.1 KiB
Python
67 lines
2.1 KiB
Python
"""Unit tests for TerminalSession docker exec invocation."""
|
|
|
|
import uuid
|
|
from unittest.mock import AsyncMock, patch
|
|
|
|
import pytest
|
|
|
|
from src.services.terminal.terminal_session import TerminalSession
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_start_passes_container_user_to_docker_exec() -> None:
|
|
"""When container_user is set, docker exec receives --user <user>."""
|
|
session = TerminalSession(
|
|
session_id=str(uuid.uuid4()),
|
|
instance_id=uuid.uuid4(),
|
|
container_id="container-123",
|
|
container_user="dev",
|
|
)
|
|
|
|
with patch(
|
|
"src.services.terminal.terminal_session.pty.openpty",
|
|
return_value=(1, 2),
|
|
):
|
|
with patch("src.services.terminal.terminal_session.tty.setraw"):
|
|
with patch(
|
|
"src.services.terminal.terminal_session.asyncio.create_subprocess_exec",
|
|
new=AsyncMock(),
|
|
) as mock_exec:
|
|
with patch("src.services.terminal.terminal_session.os.close"):
|
|
await session.start()
|
|
|
|
args, _kwargs = mock_exec.call_args
|
|
assert "docker" in args
|
|
assert "exec" in args
|
|
assert "--user" in args
|
|
user_index = args.index("--user")
|
|
assert args[user_index + 1] == "dev"
|
|
assert "container-123" in args
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.asyncio
|
|
async def test_start_omits_user_when_not_configured() -> None:
|
|
"""Without container_user, docker exec does not receive --user."""
|
|
session = TerminalSession(
|
|
session_id=str(uuid.uuid4()),
|
|
instance_id=uuid.uuid4(),
|
|
container_id="container-123",
|
|
)
|
|
|
|
with patch(
|
|
"src.services.terminal.terminal_session.pty.openpty",
|
|
return_value=(1, 2),
|
|
):
|
|
with patch("src.services.terminal.terminal_session.tty.setraw"):
|
|
with patch(
|
|
"src.services.terminal.terminal_session.asyncio.create_subprocess_exec",
|
|
new=AsyncMock(),
|
|
) as mock_exec:
|
|
with patch("src.services.terminal.terminal_session.os.close"):
|
|
await session.start()
|
|
|
|
args, _kwargs = mock_exec.call_args
|
|
assert "--user" not in args
|