Skip to content

Commit 43ea173

Browse files
committed
fix(runtime): preserve Windows locator diagnostics
Signed-off-by: mika <211269698+mikamikasuki@users.noreply.github.com>
1 parent 9f6a87f commit 43ea173

2 files changed

Lines changed: 59 additions & 3 deletions

File tree

‎loopx/control_plane/effect_runtime.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -463,6 +463,12 @@ def _read_info(path: Path, *, fingerprint: str) -> dict[str, Any] | None:
463463
try:
464464
payload = json.loads(path.read_text(encoding="utf-8"))
465465
except PermissionError as exc:
466+
# On Windows, opening a directory as a file reports access denied.
467+
# Defer that occupied-locator case to the managed server, which can
468+
# publish the shared filesystem diagnostic through its startup envelope.
469+
# Keep symlinks and genuinely unreadable metadata on the host-permission path.
470+
if path.is_dir() and not path.is_symlink():
471+
return None
466472
raise EffectRuntimeHostPermissionError() from exc
467473
except (FileNotFoundError, json.JSONDecodeError, OSError):
468474
return None

‎tests/control_plane/test_effect_runtime_integration.py‎

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,33 @@ def _journal(effect_id: str) -> dict[str, object]:
6464
}
6565

6666

67+
@pytest.mark.parametrize("occupied_with_directory", [True, False])
68+
def test_read_info_distinguishes_directory_from_unreadable_runtime_metadata(
69+
tmp_path: Path,
70+
monkeypatch,
71+
occupied_with_directory: bool,
72+
) -> None:
73+
info_path = tmp_path / "runtime-info"
74+
if occupied_with_directory:
75+
info_path.mkdir()
76+
else:
77+
info_path.write_text("{}", encoding="utf-8")
78+
79+
original_read_text = Path.read_text
80+
81+
def deny_read(path: Path, *args, **kwargs):
82+
if path == info_path:
83+
raise PermissionError("synthetic denied read")
84+
return original_read_text(path, *args, **kwargs)
85+
86+
monkeypatch.setattr(Path, "read_text", deny_read)
87+
if occupied_with_directory:
88+
assert effect_runtime._read_info(info_path, fingerprint="fixture") is None
89+
else:
90+
with pytest.raises(effect_runtime.EffectRuntimeHostPermissionError):
91+
effect_runtime._read_info(info_path, fingerprint="fixture")
92+
93+
6794
def _raw_runtime_response(
6895
info: dict[str, object],
6996
payload: bytes,
@@ -931,19 +958,32 @@ def terminate(self) -> None:
931958

932959

933960
@pytest.mark.parametrize(
934-
("failure", "expected_code"),
961+
("failure", "simulate_permission_denied", "expected_code"),
935962
[
936963
# Windows rejects replacement of an occupied directory with EPERM/EACCES;
937964
# preserve that platform's shared permission diagnostic rather than
938965
# requiring the Unix EISDIR classification.
939-
("directory", "io_permission_denied" if os.name == "nt" else "io_is_directory"),
940-
("live_lock", "mutation_lock_timeout"),
966+
(
967+
"directory",
968+
False,
969+
"io_permission_denied" if os.name == "nt" else "io_is_directory",
970+
),
971+
# Exercise the Windows Python open-directory error on every platform
972+
# through the production runtime request path, retaining the running
973+
# platform's native filesystem diagnostic from the managed server.
974+
(
975+
"directory",
976+
True,
977+
"io_permission_denied" if os.name == "nt" else "io_is_directory",
978+
),
979+
("live_lock", False, "mutation_lock_timeout"),
941980
],
942981
)
943982
def test_locator_publication_failure_surfaces_safe_typed_startup_diagnostic(
944983
tmp_path: Path,
945984
monkeypatch,
946985
failure: str,
986+
simulate_permission_denied: bool,
947987
expected_code: str,
948988
) -> None:
949989
marker = "private-locator-fixture"
@@ -958,6 +998,16 @@ def test_locator_publication_failure_surfaces_safe_typed_startup_diagnostic(
958998
else:
959999
lock_path.write_text(json.dumps(lock_owner), encoding="utf-8")
9601000

1001+
if simulate_permission_denied:
1002+
original_read_text = Path.read_text
1003+
1004+
def deny_directory_read(path: Path, *args, **kwargs):
1005+
if path == info_path:
1006+
raise PermissionError("synthetic denied directory open")
1007+
return original_read_text(path, *args, **kwargs)
1008+
1009+
monkeypatch.setattr(Path, "read_text", deny_directory_read)
1010+
9611011
captures: list[bytes] = []
9621012
read_stderr = effect_runtime._read_startup_stderr
9631013

0 commit comments

Comments
 (0)