项目文件夹

文件
Alezander9 51d62fbbe7 fix(_ipc): drop bu-<NAME> filename prefix when BH_TMP_DIR is set
When BH_TMP_DIR is set, the caller owns the directory and is expected to
give each daemon its own — so the bu-<NAME> filename prefix is redundant.
Dropping it cuts ~30 chars off the AF_UNIX path on POSIX, which matters
because sun_path is 104 bytes on macOS / 108 on Linux and a long BU_NAME
(e.g. an opencode-style session id) can blow past it.

Concretely the failing path on macOS for a 30-char session id under
~/.local/share/bcode/sessions/<sessionID>/bu-<sessionID>.sock was 117
bytes; with this change it becomes ~83 bytes.

When BH_TMP_DIR is unset, _TMP is the shared default (/tmp on POSIX,
gettempdir() on Windows) and the bu-<NAME> prefix is kept to disambiguate
multiple daemons sharing the dir. Backward compatible by default.

admin._daemon_endpoint_names: when BH_TMP_DIR is set, the dir is per-daemon
by construction, so glob discovery doesn't apply — just check whether our
local endpoint exists. Without BH_TMP_DIR, fall back to the existing
shared-dir glob.

Validation (_check on BU_NAME) still runs in both modes to catch garbage
names early.
2026-04-28 22:30:57 -07:00

159 行
5.9 KiB
Python

from browser_harness import admin
class FakeSocket:
def __init__(self, response=b'{"target_id":"target-1","session_id":"session-1","page":null}\n'):
self.response = response
self.closed = False
self.sent = b""
def sendall(self, data):
self.sent += data
def recv(self, _size):
out, self.response = self.response, b""
return out
def close(self):
self.closed = True
def test_local_chrome_mode_is_false_when_env_provides_remote_cdp():
assert not admin._is_local_chrome_mode({"BU_CDP_WS": "ws://example.test/devtools/browser/1"})
def test_local_chrome_mode_is_false_when_process_env_provides_remote_cdp(monkeypatch):
monkeypatch.setenv("BU_CDP_WS", "ws://example.test/devtools/browser/1")
assert not admin._is_local_chrome_mode()
def test_handshake_timeout_needs_chrome_remote_debugging_prompt():
msg = "CDP WS handshake failed: timed out during opening handshake"
assert admin._needs_chrome_remote_debugging_prompt(msg)
def test_handshake_403_needs_chrome_remote_debugging_prompt():
msg = "CDP WS handshake failed: server rejected WebSocket connection: HTTP 403"
assert admin._needs_chrome_remote_debugging_prompt(msg)
def test_stale_websocket_does_not_open_chrome_inspect():
msg = "no close frame received or sent"
assert not admin._needs_chrome_remote_debugging_prompt(msg)
def test_daemon_endpoint_names_discovers_valid_socket_names(tmp_path, monkeypatch):
monkeypatch.setattr(admin.ipc, "IS_WINDOWS", False)
monkeypatch.setattr(admin.ipc, "BH_TMP_DIR", None) # shared-tmpdir mode
monkeypatch.setattr(admin.ipc, "_TMP", tmp_path)
(tmp_path / "bu-default.sock").touch()
(tmp_path / "bu-remote_1.sock").touch()
(tmp_path / "bu-invalid.name.sock").touch()
(tmp_path / "not-bu-default.sock").touch()
assert admin._daemon_endpoint_names() == ["default", "remote_1"]
def test_daemon_endpoint_names_with_bh_tmp_dir_returns_local_name_when_sock_exists(tmp_path, monkeypatch):
monkeypatch.setattr(admin.ipc, "IS_WINDOWS", False)
monkeypatch.setattr(admin.ipc, "BH_TMP_DIR", str(tmp_path))
monkeypatch.setattr(admin.ipc, "_TMP", tmp_path)
monkeypatch.setattr(admin, "NAME", "session-xyz")
(tmp_path / "bu.sock").touch()
assert admin._daemon_endpoint_names() == ["session-xyz"]
def test_daemon_endpoint_names_with_bh_tmp_dir_returns_empty_when_sock_missing(tmp_path, monkeypatch):
monkeypatch.setattr(admin.ipc, "IS_WINDOWS", False)
monkeypatch.setattr(admin.ipc, "BH_TMP_DIR", str(tmp_path))
monkeypatch.setattr(admin.ipc, "_TMP", tmp_path)
monkeypatch.setattr(admin, "NAME", "session-xyz")
assert admin._daemon_endpoint_names() == []
def test_active_browser_connections_counts_only_healthy_daemons(monkeypatch):
monkeypatch.setattr(admin, "_daemon_endpoint_names", lambda: ["default", "stale", "remote"])
def fake_connect(name, timeout=1.0):
if name == "stale":
raise ConnectionRefusedError()
if name == "remote":
return FakeSocket(b'{"error":"no close frame received or sent"}\n')
return FakeSocket()
monkeypatch.setattr(admin.ipc, "connect", fake_connect)
assert admin.active_browser_connections() == 1
def test_browser_connections_returns_attached_page(monkeypatch):
monkeypatch.setattr(admin, "_daemon_endpoint_names", lambda: ["default"])
response = (
b'{"target_id":"target-1","session_id":"session-1",'
b'"page":{"targetId":"target-1","title":"Cat - Wikipedia","url":"https://en.wikipedia.org/wiki/Cat"}}\n'
)
monkeypatch.setattr(admin.ipc, "connect", lambda name, timeout=1.0: FakeSocket(response))
assert admin.browser_connections() == [
{
"name": "default",
"page": {"title": "Cat - Wikipedia", "url": "https://en.wikipedia.org/wiki/Cat"},
}
]
def test_run_doctor_prints_active_browser_connections_and_active_pages(monkeypatch, capsys):
monkeypatch.setattr(admin, "_version", lambda: "0.1.0")
monkeypatch.setattr(admin, "_install_mode", lambda: "git")
monkeypatch.setattr(admin, "_chrome_running", lambda: True)
monkeypatch.setattr(admin, "daemon_alive", lambda: True)
monkeypatch.setattr(admin, "browser_connections", lambda: [
{
"name": "default",
"page": {"title": "Example", "url": "https://example.test"},
},
{
"name": "cats",
"page": {"title": "Cat - Wikipedia", "url": "https://en.wikipedia.org/wiki/Cat"},
},
])
monkeypatch.setattr(admin, "_latest_release_tag", lambda: "0.1.0")
monkeypatch.setattr("shutil.which", lambda _cmd: None)
monkeypatch.delenv("BROWSER_USE_API_KEY", raising=False)
assert admin.run_doctor() == 0
out = capsys.readouterr().out
assert "[ok ] active browser connections — 2" in out
assert " default — active page: Example — https://example.test" in out
assert " cats — active page: Cat - Wikipedia — https://en.wikipedia.org/wiki/Cat" in out
def test_doctor_page_output_truncates_long_text(monkeypatch, capsys):
monkeypatch.setattr(admin, "_version", lambda: "0.1.0")
monkeypatch.setattr(admin, "_install_mode", lambda: "git")
monkeypatch.setattr(admin, "_chrome_running", lambda: True)
monkeypatch.setattr(admin, "daemon_alive", lambda: True)
monkeypatch.setattr(admin, "DOCTOR_TEXT_LIMIT", 20)
monkeypatch.setattr(admin, "browser_connections", lambda: [
{
"name": "default",
"page": {"title": "A very long page title", "url": "https://example.test/very/long/path"},
}
])
monkeypatch.setattr(admin, "_latest_release_tag", lambda: "0.1.0")
monkeypatch.setattr("shutil.which", lambda _cmd: None)
monkeypatch.delenv("BROWSER_USE_API_KEY", raising=False)
assert admin.run_doctor() == 0
out = capsys.readouterr().out
assert "A very long page ..." in out
assert "https://example.t..." in out