|
1 | 1 | # ruff: noqa: PLR2004 S108 ARG005 UP012 |
2 | 2 | """Tests for WslVM and _VMSessionComputer (Windows variant). |
3 | 3 |
|
4 | | -All tests mock the WSL backend — no wsl.exe or WSL2 required. |
| 4 | +All tests mock the WSL backend - no wsl.exe or WSL2 required. |
5 | 5 | """ |
6 | 6 |
|
7 | 7 | from __future__ import annotations |
|
19 | 19 | from hexagent.computer.local._types import ResolvedMount |
20 | 20 | from hexagent.computer.local._wsl import ( |
21 | 21 | WslVM, |
| 22 | + _decode_wsl_output, |
22 | 23 | _parse_status_output, |
23 | 24 | _session_user_from_guest_mount_path, |
24 | 25 | _win_path_to_wsl, |
@@ -243,12 +244,26 @@ async def test_upload_copies_via_tmp_then_moves(self, tmp_path: Path) -> None: |
243 | 244 | assert copy_call.args[1].startswith("/tmp/.upload-") |
244 | 245 | assert copy_call.kwargs.get("host_to_guest") is True |
245 | 246 |
|
246 | | - mv_call = vm.shell.call_args_list[1] |
| 247 | + mv_call = next(c for c in vm.shell.call_args_list if " mv " in c.args[0]) |
247 | 248 | assert "sudo mv" in mv_call.args[0] |
248 | 249 | assert "/remote/file.txt" in mv_call.args[0] |
249 | 250 | assert "chown test-session:test-session" in mv_call.args[0] |
250 | 251 | assert "chmod 644" in mv_call.args[0] |
251 | 252 |
|
| 253 | + async def test_upload_uses_posix_parent_for_session_paths(self, tmp_path: Path) -> None: |
| 254 | + vm = _mock_vm() |
| 255 | + vm.copy = AsyncMock() |
| 256 | + computer = _make_computer(vm) |
| 257 | + |
| 258 | + src = tmp_path / "file.txt" |
| 259 | + src.write_text("data") |
| 260 | + |
| 261 | + await computer.upload(str(src), "/sessions/alice/mnt/uploads/file.txt") |
| 262 | + |
| 263 | + mkdir_call = next(c for c in vm.shell.call_args_list if "mkdir -p" in c.args[0]) |
| 264 | + assert "/sessions/alice/mnt/uploads" in mkdir_call.args[0] |
| 265 | + assert "\\sessions\\alice\\mnt\\uploads" not in mkdir_call.args[0] |
| 266 | + |
252 | 267 | async def test_upload_missing_src_raises_file_not_found(self, tmp_path: Path) -> None: |
253 | 268 | vm = _mock_vm() |
254 | 269 | computer = _make_computer(vm) |
@@ -288,7 +303,7 @@ async def test_download_stages_via_tmp(self, tmp_path: Path) -> None: |
288 | 303 | await computer.download("/remote/file.txt", str(dst)) |
289 | 304 |
|
290 | 305 | # First shell call: sudo cp to tmp + chmod |
291 | | - stage_call = vm.shell.call_args_list[0] |
| 306 | + stage_call = next(c for c in vm.shell.call_args_list if " cp " in c.args[0]) |
292 | 307 | assert "sudo cp" in stage_call.args[0] |
293 | 308 | assert "chmod 644" in stage_call.args[0] |
294 | 309 |
|
@@ -340,7 +355,7 @@ def test_satisfies_computer_protocol(self) -> None: |
340 | 355 |
|
341 | 356 |
|
342 | 357 | # =========================================================================== |
343 | | -# WslVM — pure logic only (no subprocess) |
| 358 | +# WslVM - pure logic only (no subprocess) |
344 | 359 | # =========================================================================== |
345 | 360 |
|
346 | 361 |
|
@@ -419,6 +434,30 @@ async def test_start_does_not_retry_on_non_transient_failure(self) -> None: |
419 | 434 | mock_apply.assert_not_awaited() |
420 | 435 |
|
421 | 436 |
|
| 437 | +# =========================================================================== |
| 438 | +# WSL output decoding |
| 439 | +# =========================================================================== |
| 440 | + |
| 441 | + |
| 442 | +class TestDecodeWslOutput: |
| 443 | + """Tests for mixed-encoding stderr decoding.""" |
| 444 | + |
| 445 | + def test_utf8_plain(self) -> None: |
| 446 | + assert _decode_wsl_output("hello".encode("utf-8")) == "hello" |
| 447 | + |
| 448 | + def test_utf16le_with_bom(self) -> None: |
| 449 | + raw = b"\xff\xfe" + "warning: test".encode("utf-16-le") |
| 450 | + assert "warning: test" in _decode_wsl_output(raw) |
| 451 | + |
| 452 | + def test_mixed_utf16le_prefix_and_utf8_tail(self) -> None: |
| 453 | + prefix = "wsl: localhost proxy config detected but not mirrored to WSL.\r\n".encode("utf-16-le") |
| 454 | + tail = b"/bin/bash: line 1: _mime_by_ext: command not found\n" |
| 455 | + text = _decode_wsl_output(prefix + tail) |
| 456 | + |
| 457 | + assert "localhost proxy config detected" in text |
| 458 | + assert "_mime_by_ext: command not found" in text |
| 459 | + |
| 460 | + |
422 | 461 | # =========================================================================== |
423 | 462 | # Status output parsing |
424 | 463 | # =========================================================================== |
|
0 commit comments