Skip to content

Commit 70d1383

Browse files
committed
test(Session.attach): Add xfail regression test for refresh after kill
why: Reproduce tmuxp issue #1002 where attach() raises exception if session is killed while user is attached. what: - Add test that simulates session killed during attach-session - Mark as xfail since attach() currently calls refresh() which fails - Documents expected behavior: attach() should not raise if session gone
1 parent 64276e4 commit 70d1383

File tree

1 file changed

+99
-0
lines changed

1 file changed

+99
-0
lines changed

tests/test_session.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pathlib
77
import shutil
88
import typing as t
9+
from contextlib import nullcontext as does_not_raise
910

1011
import pytest
1112

@@ -18,6 +19,15 @@
1819
from libtmux.window import Window
1920

2021
if t.TYPE_CHECKING:
22+
from typing import TypeAlias
23+
24+
try:
25+
from _pytest.raises import RaisesExc
26+
except ImportError:
27+
from _pytest.python_api import RaisesContext # type: ignore[attr-defined]
28+
29+
RaisesExc: TypeAlias = RaisesContext[Exception] # type: ignore[no-redef]
30+
2131
from libtmux._internal.types import StrPath
2232
from libtmux.server import Server
2333

@@ -481,3 +491,92 @@ def test_new_window_start_directory_pathlib(
481491
actual_path = str(pathlib.Path(active_pane.pane_current_path).resolve())
482492
expected_path = str(user_path.resolve())
483493
assert actual_path == expected_path
494+
495+
496+
class SessionAttachRefreshFixture(t.NamedTuple):
497+
"""Test fixture for Session.attach() refresh behavior regression.
498+
499+
This tests the scenario where a session is killed while the user is attached,
500+
and then attach() tries to call refresh() which fails because the session
501+
no longer exists.
502+
503+
See: https://github.com/tmux-python/tmuxp/issues/1002
504+
"""
505+
506+
test_id: str
507+
raises: type[Exception] | bool
508+
509+
510+
SESSION_ATTACH_REFRESH_FIXTURES: list[SessionAttachRefreshFixture] = [
511+
SessionAttachRefreshFixture(
512+
test_id="session_killed_during_attach_should_not_raise",
513+
raises=False, # attach() should NOT raise if session gone
514+
),
515+
]
516+
517+
518+
@pytest.mark.xfail(
519+
reason="Bug: attach() calls refresh() which fails if session killed during attach. "
520+
"See: https://github.com/tmux-python/tmuxp/issues/1002",
521+
raises=Exception,
522+
strict=True,
523+
)
524+
@pytest.mark.parametrize(
525+
list(SessionAttachRefreshFixture._fields),
526+
SESSION_ATTACH_REFRESH_FIXTURES,
527+
ids=[test.test_id for test in SESSION_ATTACH_REFRESH_FIXTURES],
528+
)
529+
def test_session_attach_does_not_fail_if_session_killed_during_attach(
530+
server: Server,
531+
monkeypatch: pytest.MonkeyPatch,
532+
test_id: str,
533+
raises: type[Exception] | bool,
534+
) -> None:
535+
"""Regression test: Session.attach() should not fail if session is killed.
536+
537+
When a user is attached to a tmux session via `tmuxp load`, then kills the
538+
session from within tmux (e.g., kills all windows), and then detaches,
539+
the attach() method should not raise an exception.
540+
541+
Currently, attach() calls self.refresh() after attach-session returns, which
542+
fails with TmuxObjectDoesNotExist if the session no longer exists.
543+
544+
The fix is to remove the refresh() call from attach() since:
545+
1. attach-session is a blocking interactive command
546+
2. Session state can change arbitrarily while the user is attached
547+
3. Refreshing after such a command makes no semantic sense
548+
"""
549+
from libtmux.common import tmux_cmd
550+
551+
# Create a new session specifically for this test
552+
test_session = server.new_session(detach=True)
553+
554+
# Store original cmd method
555+
original_cmd = test_session.cmd
556+
557+
# Create a mock tmux_cmd result that simulates successful attach-session
558+
class MockTmuxCmd:
559+
def __init__(self) -> None:
560+
self.stdout: list[str] = []
561+
self.stderr: list[str] = []
562+
self.cmd: list[str] = ["tmux", "attach-session"]
563+
564+
def patched_cmd(cmd_name: str, *args: t.Any, **kwargs: t.Any) -> tmux_cmd:
565+
"""Patched cmd that kills session after attach-session."""
566+
if cmd_name == "attach-session":
567+
# Simulate: attach-session succeeded, user worked, then killed session
568+
# This happens BEFORE refresh() is called
569+
test_session.kill()
570+
return MockTmuxCmd() # type: ignore[return-value]
571+
return original_cmd(cmd_name, *args, **kwargs)
572+
573+
monkeypatch.setattr(test_session, "cmd", patched_cmd)
574+
575+
# Use context manager pattern for exception handling
576+
raises_ctx: RaisesExc = (
577+
pytest.raises(t.cast("type[Exception]", raises))
578+
if raises
579+
else t.cast("RaisesExc", does_not_raise())
580+
)
581+
with raises_ctx:
582+
test_session.attach()

0 commit comments

Comments
 (0)