diff --git a/muxplex/tests/test_frontend_js.py b/muxplex/tests/test_frontend_js.py index 571d869..8b3849f 100644 --- a/muxplex/tests/test_frontend_js.py +++ b/muxplex/tests/test_frontend_js.py @@ -4820,7 +4820,7 @@ def test_terminal_js_weblinks_addon_has_try_catch() -> None: var_match = re.search(r"var WebLinksAddon", body) assert var_match, "var WebLinksAddon assignment not found" weblinks_idx = var_match.start() - nearby = body[max(0, weblinks_idx - 100):weblinks_idx + 300] + nearby = body[max(0, weblinks_idx - 100) : weblinks_idx + 300] assert "try" in nearby and "catch" in nearby, ( "WebLinksAddon loading must be wrapped in try/catch" ) @@ -4837,7 +4837,7 @@ def test_terminal_js_search_addon_has_try_catch() -> None: body = match.group(1) assert "SearchAddon" in body, "createTerminal must reference SearchAddon" search_idx = body.index("SearchAddon") - nearby = body[max(0, search_idx - 200):search_idx + 300] + nearby = body[max(0, search_idx - 200) : search_idx + 300] assert "try" in nearby and "catch" in nearby, ( "SearchAddon loading must be wrapped in try/catch" ) @@ -4869,7 +4869,7 @@ def test_terminal_js_image_addon_has_try_catch() -> None: body = match.group(1) assert "ImageAddon" in body, "createTerminal must reference ImageAddon" image_idx = body.index("ImageAddon") - nearby = body[max(0, image_idx - 200):image_idx + 300] + nearby = body[max(0, image_idx - 200) : image_idx + 300] assert "try" in nearby and "catch" in nearby, ( "ImageAddon loading must be wrapped in try/catch" ) @@ -4902,3 +4902,105 @@ def test_terminal_js_open_terminal_has_ghostty_ready_guard() -> None: assert "_ghosttyReady" in body, ( "openTerminal must have a _ghosttyReady guard before creating the terminal" ) + + +# ============================================================ +# Muxterm protocol tests (task-13: remove cache/ttyd, verify muxterm) +# ============================================================ + + +def test_terminal_js_no_ttyd_prefix_encoding() -> None: + """terminal.js must not contain ttyd-style prefix bytes 0x30 or 0x31. + + The old ttyd protocol used 0x30 (input) and 0x31 (resize) prefix bytes + before each WebSocket frame. The muxterm protocol uses plain binary + for terminal I/O and JSON text frames for control messages, so these + prefix constants must not appear anywhere in terminal.js. + """ + assert "0x30" not in _TERMINAL_JS, ( + "terminal.js must not contain 0x30 (ttyd input prefix) — " + "muxterm uses raw binary for terminal I/O" + ) + assert "0x31" not in _TERMINAL_JS, ( + "terminal.js must not contain 0x31 (ttyd resize prefix) — " + "muxterm uses JSON text frames for resize" + ) + + +def test_terminal_js_no_terminal_cache() -> None: + """terminal.js must not contain _terminalCache or _cacheOrder. + + The old ttyd approach cached multiple Terminal instances so switching + sessions was instant. The muxterm architecture uses a single persistent + WebSocket with attach/detach commands, so there is no terminal cache. + """ + assert "_terminalCache" not in _TERMINAL_JS, ( + "terminal.js must not contain _terminalCache — " + "muxterm uses a single terminal with attach/detach, no caching" + ) + assert "_cacheOrder" not in _TERMINAL_JS, ( + "terminal.js must not contain _cacheOrder — " + "terminal caching was removed in the muxterm rewrite" + ) + + +def test_terminal_js_uses_json_attach() -> None: + """terminal.js must use JSON attach messages to switch sessions. + + The muxterm protocol attaches to a session by sending a JSON text + frame: { attach: "sessionName" }. This replaces the old approach + of opening a new WebSocket per session. + """ + assert "attach:" in _TERMINAL_JS, ( + "terminal.js must contain 'attach:' — " + "muxterm uses JSON { attach: sessionName } to switch sessions" + ) + + +def test_terminal_js_uses_json_resize() -> None: + """terminal.js must send resize events as JSON with cols and rows. + + The muxterm protocol sends resize as a JSON text frame: + { resize: { cols: N, rows: N } }. This replaces the old ttyd + 0x31 prefix + binary encoding. + """ + assert "resize" in _TERMINAL_JS, ( + "terminal.js must contain 'resize' — " + "muxterm uses JSON { resize: { cols, rows } } for resize events" + ) + assert "cols" in _TERMINAL_JS, ( + "terminal.js must contain 'cols' — resize JSON must include cols" + ) + assert "rows" in _TERMINAL_JS, ( + "terminal.js must contain 'rows' — resize JSON must include rows" + ) + + +def test_terminal_js_fetches_terminal_token() -> None: + """terminal.js must fetch /api/terminal-token before connecting. + + The muxterm protocol requires a short-lived token from the API + server before opening the WebSocket to the muxterm port. This + ensures authentication is handled by the main API. + """ + assert "/api/terminal-token" in _TERMINAL_JS, ( + "terminal.js must fetch /api/terminal-token — " + "muxterm requires a token before WebSocket connection" + ) + + +def test_terminal_js_connects_to_muxterm_port_directly() -> None: + """connectWebSocket URL must target muxterm port directly, not /terminal/ws. + + The old ttyd proxy routed through /terminal/ws on the main server. + The muxterm protocol connects directly to the muxterm port: + ws://host:muxtermPort/ws?token=... + """ + assert "/terminal/ws" not in _TERMINAL_JS, ( + "terminal.js must not connect to /terminal/ws — " + "muxterm connects directly to ws://host:muxtermPort/ws" + ) + assert "_muxtermPort" in _TERMINAL_JS, ( + "terminal.js must reference _muxtermPort — " + "WebSocket connects directly to the muxterm port" + )