@@ -178,3 +178,70 @@ def test_getter_methods(mock_executor):
178178 assert mock_executor .get_expected_num_active_requests () == 5
179179 assert mock_executor ._get_new_active_requests_queue_latency () == 10.5
180180 assert mock_executor .get_waiting_queue_size () == 1
181+
182+
183+ def _classify_termination (request , enable_partial_reuse_for_disagg , is_vswa , pp_size ):
184+ """Reproduce the termination logic from _handle_responses (py_executor.py).
185+
186+ Returns:
187+ "terminate" | "stats_only" | "skip"
188+ """
189+ force_terminate_for_partial_reuse = (
190+ enable_partial_reuse_for_disagg and not is_vswa and pp_size == 1
191+ )
192+ if request .is_disagg_context_complete_state :
193+ return "stats_only"
194+ elif force_terminate_for_partial_reuse :
195+ return "terminate"
196+ elif not request .is_disagg_context_transmission_state :
197+ return "terminate"
198+ return "skip"
199+
200+
201+ def _make_request (complete_state , transmission_state ):
202+ req = Mock ()
203+ req .is_disagg_context_complete_state = complete_state
204+ req .is_disagg_context_transmission_state = transmission_state
205+ return req
206+
207+
208+ class TestDisaggTerminationGuard :
209+ """Verify _handle_responses does not double-terminate DISAGG_CONTEXT_COMPLETE
210+ requests that were already cleaned up by _check_disagg_ctx_cache_transfer_status
211+ (nvbug/5961736)."""
212+
213+ def test_normal_path_skips_context_complete (self ):
214+ """Without partial reuse, CONTEXT_COMPLETE goes to stats only."""
215+ req = _make_request (complete_state = True , transmission_state = False )
216+ assert _classify_termination (req , False , False , 1 ) == "stats_only"
217+
218+ def test_normal_path_skips_transmission_in_progress (self ):
219+ """Without partial reuse, TRANS_IN_PROGRESS is skipped (still in flight)."""
220+ req = _make_request (complete_state = False , transmission_state = True )
221+ assert _classify_termination (req , False , False , 1 ) == "skip"
222+
223+ def test_normal_path_terminates_regular_request (self ):
224+ """Without partial reuse, a normal finished request is terminated."""
225+ req = _make_request (complete_state = False , transmission_state = False )
226+ assert _classify_termination (req , False , False , 1 ) == "terminate"
227+
228+ def test_partial_reuse_terminates_non_complete (self ):
229+ """With partial reuse, non-CONTEXT_COMPLETE requests are terminated."""
230+ for complete , transmission in [(False , True ), (False , False )]:
231+ req = _make_request (complete , transmission )
232+ assert _classify_termination (req , True , False , 1 ) == "terminate"
233+
234+ def test_partial_reuse_skips_context_complete (self ):
235+ """With partial reuse, CONTEXT_COMPLETE still goes to stats only."""
236+ req = _make_request (complete_state = True , transmission_state = False )
237+ assert _classify_termination (req , True , False , 1 ) == "stats_only"
238+
239+ def test_partial_reuse_disabled_by_vswa (self ):
240+ """VSWA disables partial reuse path, falling back to normal logic."""
241+ req = _make_request (complete_state = True , transmission_state = False )
242+ assert _classify_termination (req , True , True , 1 ) == "stats_only"
243+
244+ def test_partial_reuse_disabled_by_pp (self ):
245+ """PP > 1 disables partial reuse path, falling back to normal logic."""
246+ req = _make_request (complete_state = True , transmission_state = False )
247+ assert _classify_termination (req , True , False , 2 ) == "stats_only"
0 commit comments