1- """Tests for session_end hook — worker stop behavior."""
1+ """Tests for session_end hook — worker stop behavior with direct filesystem checks ."""
22
33from __future__ import annotations
44
55import os
6+ from pathlib import Path
67from unittest .mock import MagicMock , patch
78
89import session_end
@@ -17,11 +18,16 @@ def test_returns_early_without_plugin_root():
1718 assert result == 0
1819
1920
20- def test_skips_stop_when_other_sessions_active ():
21+ def test_skips_stop_when_other_sessions_active (tmp_path : Path ):
2122 """Should skip worker stop when other Pilot sessions are running."""
23+ base = tmp_path / "sessions"
24+ (base / "1001" ).mkdir (parents = True )
25+ (base / "2002" ).mkdir (parents = True )
26+
2227 with (
23- patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" }),
24- patch ("session_end._get_active_session_count" , return_value = 2 ),
28+ patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" , "PILOT_SESSION_ID" : "1001" }),
29+ patch .object (session_end , "SESSIONS_DIR" , base ),
30+ patch ("session_end.os.kill" , return_value = None ),
2531 patch ("session_end.subprocess.run" ) as mock_run ,
2632 ):
2733 result = session_end .main ()
@@ -30,11 +36,14 @@ def test_skips_stop_when_other_sessions_active():
3036 mock_run .assert_not_called ()
3137
3238
33- def test_stops_worker_when_no_other_sessions ():
39+ def test_stops_worker_when_no_other_sessions (tmp_path : Path ):
3440 """Should stop worker when this is the only active session."""
41+ base = tmp_path / "sessions"
42+ (base / "1001" ).mkdir (parents = True )
43+
3544 with (
36- patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" }),
37- patch ( " session_end._get_active_session_count" , return_value = 1 ),
45+ patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" , "PILOT_SESSION_ID" : "1001" }),
46+ patch . object ( session_end , "SESSIONS_DIR" , base ),
3847 patch ("session_end.subprocess.run" , return_value = MagicMock (returncode = 0 )) as mock_run ,
3948 ):
4049 result = session_end .main ()
@@ -44,14 +53,56 @@ def test_stops_worker_when_no_other_sessions():
4453 assert "stop" in str (mock_run .call_args )
4554
4655
47- def test_stops_worker_when_zero_sessions ():
48- """Should stop worker and return 0 when no sessions active."""
56+ def test_stops_worker_when_zero_sessions (tmp_path : Path ):
57+ """Should stop worker and return 0 when no sessions exist at all."""
58+ base = tmp_path / "sessions"
59+ base .mkdir (parents = True )
60+
4961 with (
50- patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" }),
51- patch ( " session_end._get_active_session_count" , return_value = 0 ),
62+ patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" , "PILOT_SESSION_ID" : "1001" }),
63+ patch . object ( session_end , "SESSIONS_DIR" , base ),
5264 patch ("session_end.subprocess.run" , return_value = MagicMock (returncode = 0 )) as mock_run ,
5365 ):
5466 result = session_end .main ()
5567
5668 assert result == 0
5769 mock_run .assert_called_once ()
70+
71+
72+ def test_safe_default_on_directory_error ():
73+ """Should NOT stop worker when directory can't be read (safe default)."""
74+ mock_dir = MagicMock ()
75+ mock_dir .exists .side_effect = OSError ("permission denied" )
76+
77+ with (
78+ patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" , "PILOT_SESSION_ID" : "1001" }),
79+ patch .object (session_end , "SESSIONS_DIR" , mock_dir ),
80+ patch ("session_end.subprocess.run" ) as mock_run ,
81+ ):
82+ result = session_end .main ()
83+
84+ assert result == 0
85+ mock_run .assert_not_called ()
86+
87+
88+ def test_skips_dead_pid_sessions (tmp_path : Path ):
89+ """Should not count dead PID directories as active sessions."""
90+ base = tmp_path / "sessions"
91+ (base / "1001" ).mkdir (parents = True ) # current
92+ (base / "9999" ).mkdir (parents = True ) # dead
93+
94+ def kill_side_effect (pid : int , _sig : int ) -> None :
95+ if pid == 9999 :
96+ raise OSError ("No such process" )
97+
98+ with (
99+ patch .dict (os .environ , {"CLAUDE_PLUGIN_ROOT" : "/fake/plugin" , "PILOT_SESSION_ID" : "1001" }),
100+ patch .object (session_end , "SESSIONS_DIR" , base ),
101+ patch ("session_end.os.kill" , side_effect = kill_side_effect ),
102+ patch ("session_end.subprocess.run" , return_value = MagicMock (returncode = 0 )) as mock_run ,
103+ ):
104+ result = session_end .main ()
105+
106+ assert result == 0
107+ mock_run .assert_called_once ()
108+ assert "stop" in str (mock_run .call_args )
0 commit comments