11import contextlib
22import io
33import json
4+ import os
45import subprocess
56from pathlib import Path
67from unittest .mock import patch
1415 _get_mine_dir ,
1516 _log ,
1617 _maybe_auto_ingest ,
18+ _mine_already_running ,
1719 _parse_harness_input ,
1820 _sanitize_session_id ,
1921 _validate_transcript_path ,
@@ -250,9 +252,10 @@ def test_maybe_auto_ingest_with_env(tmp_path):
250252 mempal_dir .mkdir ()
251253 with patch .dict ("os.environ" , {"MEMPAL_DIR" : str (mempal_dir )}):
252254 with patch ("mempalace.hooks_cli.STATE_DIR" , tmp_path ):
253- with patch ("mempalace.hooks_cli.subprocess.Popen" ) as mock_popen :
254- _maybe_auto_ingest ()
255- mock_popen .assert_called_once ()
255+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , tmp_path / "mine.pid" ):
256+ with patch ("mempalace.hooks_cli.subprocess.Popen" ) as mock_popen :
257+ _maybe_auto_ingest ()
258+ mock_popen .assert_called_once ()
256259
257260
258261def test_maybe_auto_ingest_with_transcript (tmp_path ):
@@ -261,9 +264,10 @@ def test_maybe_auto_ingest_with_transcript(tmp_path):
261264 transcript .write_text ("" )
262265 with patch .dict ("os.environ" , {}, clear = True ):
263266 with patch ("mempalace.hooks_cli.STATE_DIR" , tmp_path ):
264- with patch ("mempalace.hooks_cli.subprocess.Popen" ) as mock_popen :
265- _maybe_auto_ingest (str (transcript ))
266- mock_popen .assert_called_once ()
267+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , tmp_path / "mine.pid" ):
268+ with patch ("mempalace.hooks_cli.subprocess.Popen" ) as mock_popen :
269+ _maybe_auto_ingest (str (transcript ))
270+ mock_popen .assert_called_once ()
267271
268272
269273def test_maybe_auto_ingest_oserror (tmp_path ):
@@ -272,8 +276,54 @@ def test_maybe_auto_ingest_oserror(tmp_path):
272276 mempal_dir .mkdir ()
273277 with patch .dict ("os.environ" , {"MEMPAL_DIR" : str (mempal_dir )}):
274278 with patch ("mempalace.hooks_cli.STATE_DIR" , tmp_path ):
275- with patch ("mempalace.hooks_cli.subprocess.Popen" , side_effect = OSError ("fail" )):
276- _maybe_auto_ingest () # should not raise
279+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , tmp_path / "mine.pid" ):
280+ with patch ("mempalace.hooks_cli.subprocess.Popen" , side_effect = OSError ("fail" )):
281+ _maybe_auto_ingest () # should not raise
282+
283+
284+ def test_maybe_auto_ingest_skips_when_mine_running (tmp_path ):
285+ """Does not spawn a new mine process if one is already running."""
286+ mempal_dir = tmp_path / "project"
287+ mempal_dir .mkdir ()
288+ with patch .dict ("os.environ" , {"MEMPAL_DIR" : str (mempal_dir )}):
289+ with patch ("mempalace.hooks_cli.STATE_DIR" , tmp_path ):
290+ with patch ("mempalace.hooks_cli._mine_already_running" , return_value = True ):
291+ with patch ("mempalace.hooks_cli.subprocess.Popen" ) as mock_popen :
292+ _maybe_auto_ingest ()
293+ mock_popen .assert_not_called ()
294+
295+
296+ # --- _mine_already_running ---
297+
298+
299+ def test_mine_already_running_no_file (tmp_path ):
300+ """Returns False when no PID file exists."""
301+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , tmp_path / "mine.pid" ):
302+ assert _mine_already_running () is False
303+
304+
305+ def test_mine_already_running_dead_pid (tmp_path ):
306+ """Returns False when PID file contains a PID that no longer exists."""
307+ pid_file = tmp_path / "mine.pid"
308+ pid_file .write_text ("999999999" ) # almost certainly not a real PID
309+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , pid_file ):
310+ assert _mine_already_running () is False
311+
312+
313+ def test_mine_already_running_live_pid (tmp_path ):
314+ """Returns True when PID file contains the current process's own PID."""
315+ pid_file = tmp_path / "mine.pid"
316+ pid_file .write_text (str (os .getpid ())) # current process is definitely alive
317+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , pid_file ):
318+ assert _mine_already_running () is True
319+
320+
321+ def test_mine_already_running_corrupt_file (tmp_path ):
322+ """Returns False when PID file contains non-integer content."""
323+ pid_file = tmp_path / "mine.pid"
324+ pid_file .write_text ("not-a-pid" )
325+ with patch ("mempalace.hooks_cli._MINE_PID_FILE" , pid_file ):
326+ assert _mine_already_running () is False
277327
278328
279329# --- _get_mine_dir ---
0 commit comments