Fix retry crash for unregistered remotes in the Sessions popup

Opening a window whose target was removed from the registry (its tab
stays open by design) and failing landed in the action menu with a
"重试连接" row that indexed self.entries directly and raised KeyError,
killing the popup with a traceback.

Only offer the retry action for registered targets, degrade a stale
retry row to a message, and keep the popup alive when a handler raises
anything unexpected instead of dying with a traceback.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-10-03 02:58:45 +00:00
co-authored by Claude Fable 5.1
parent 6f933e1c80
commit 6b906de476
2 changed files with 70 additions and 7 deletions
+45
View File
@@ -1000,5 +1000,50 @@ class SelectorActionTests(unittest.TestCase):
os.waitpid(pid, 0)
class SelectorRobustnessTests(unittest.TestCase):
def test_retry_is_not_offered_for_unregistered_remote_and_never_crashes(self) -> None:
with tempfile.TemporaryDirectory() as directory:
def activate(result: SelectorResult) -> None:
raise RuntimeError("ssh: connect to host orangepi port 22: No route to host")
# The registry is empty: this window's target was removed via
# "移除远端记录" while its tab stayed open.
window = CatalogWindow(
"@7",
"remote:orangepi:default",
"R orangepi/default",
True,
False,
kind="remote",
target="orangepi",
session="default",
default_label="R orangepi/default",
)
selector = SessionSelector(
[window],
RemoteRegistry(Path(directory) / "remotes.json"),
"/missing/ssh",
activate_handler=activate,
)
self.addCleanup(selector.pool.close)
row = next(
item
for item in selector._rows() # type: ignore[attr-defined]
if item.key == "window:@7"
)
self.assertIsNone(selector._activate(row)) # type: ignore[attr-defined]
self.assertEqual(selector.view, "actions")
kinds = [item.kind for item in selector._rows()] # type: ignore[attr-defined]
self.assertIn("action-window", kinds)
self.assertNotIn("action-retry", kinds)
# A stale retry row must degrade to a message instead of a KeyError.
stale = PickerRow("retry:orangepi", "retry", "! orangepi", target="orangepi")
self.assertIsNone(selector._activate(stale)) # type: ignore[attr-defined]
self.assertEqual(selector.view, "main")
self.assertIn("未登记", selector.message)
if __name__ == "__main__":
unittest.main()