@@ -296,6 +296,43 @@ def test_worker_runs_local_task(tmp_path):
296296 assert (tmp_path / "new" ).exists ()
297297
298298
299+ def test_queue_status_counts_thread_safe (tmp_path ):
300+ """Bug-Fix (Bug-Sweep): status_counts() muss Thread-sicher unter Lock laufen.
301+ pending-Property und tasks-Direktzugriff in _refresh_status ersetzt durch
302+ status_counts() um Race Condition zwischen UI- und Worker-Thread zu vermeiden."""
303+ q = Queue (tmp_path )
304+ # Leere Queue
305+ n_pending , n_failed = q .status_counts ()
306+ assert n_pending == 0 and n_failed == 0
307+
308+ # Einen Task hinzufügen
309+ src = _mkdir_with_file (tmp_path / "src1" )
310+ t1 = Task (chain = [Step (op = "rename" , src = str (src ), arg = "dst1" )])
311+ q .add (t1 )
312+ n_pending , n_failed = q .status_counts ()
313+ assert n_pending == 1 and n_failed == 0
314+
315+ # retry_count > 0 -> zählt als "failed_with_retries"
316+ q .tasks [0 ].retry_count = 1
317+ n_pending , n_failed = q .status_counts ()
318+ assert n_pending == 1 and n_failed == 1
319+
320+ # Status "done" -> nicht mehr pending
321+ q .tasks [0 ].status = "done"
322+ n_pending , n_failed = q .status_counts ()
323+ assert n_pending == 0 and n_failed == 0
324+
325+
326+ def test_queue_pending_property_thread_safe (tmp_path ):
327+ """Bug-Fix (Bug-Sweep): pending-Property muss Lock halten beim Lesen von tasks."""
328+ q = Queue (tmp_path )
329+ src = _mkdir_with_file (tmp_path / "src2" )
330+ q .add (Task (chain = [Step (op = "rename" , src = str (src ), arg = "dst2" )]))
331+ pending = q .pending
332+ assert len (pending ) == 1
333+ assert pending [0 ].status == "pending"
334+
335+
299336# ── CLI ─────────────────────────────────────────────────────────────
300337
301338def test_cli_chain_invalid_op_returns_2 (tmp_path , monkeypatch ):
0 commit comments