@@ -303,6 +303,56 @@ def fail_update_payload(job_id, payload):
303303 assert store .jobs ["job-1" ]["error" ] == "payload write failed"
304304
305305
306+ def test_v2_retry_release_failure_still_marks_job_failed (monkeypatch ):
307+ app , _ = _build_app (monkeypatch )
308+ store = FakeJobStore ()
309+ store .jobs ["job-1" ] = {
310+ "job_id" : "job-1" ,
311+ "job_type" : "memory_ingest" ,
312+ "payload" : {"billing_account_id" : "acct-1" , "user_id" : "hunter" },
313+ "user_id" : "hunter" ,
314+ "status" : "failed" ,
315+ "timeout_seconds" : 30 ,
316+ "max_attempts" : 3 ,
317+ "retry_count" : 1 ,
318+ "attempt_count" : 1 ,
319+ "workflow_id" : "old-workflow" ,
320+ }
321+
322+ class FakeEstimate :
323+ reserved_credits = 100
324+
325+ def model_dump (self ):
326+ return {"reserved_credits" : self .reserved_credits }
327+
328+ class FakeBillingService :
329+ def estimate_required_credits (self , job_type , payload ):
330+ return FakeEstimate ()
331+
332+ def reserve_credits (self , account_id , job_id , estimated_credits ):
333+ return SimpleNamespace (reservation_id = "reservation-1" , created = True )
334+
335+ async def fake_start_job_workflow (job ):
336+ raise RuntimeError ("temporal unavailable" )
337+
338+ def fail_release (account_id , job_id ):
339+ raise RuntimeError ("mongo unavailable" )
340+
341+ monkeypatch .setattr (jobs_v2 , "get_default_job_store" , lambda : store )
342+ monkeypatch .setattr (durable , "get_default_job_store" , lambda : store )
343+ monkeypatch .setattr (jobs_v2 , "get_default_billing_service" , lambda : FakeBillingService ())
344+ monkeypatch .setattr (jobs_v2 , "release_job_reservation" , fail_release )
345+ monkeypatch .setattr (jobs_v2 , "start_job_workflow" , fake_start_job_workflow )
346+
347+ response = TestClient (app ).post ("/v2/jobs/job-1/retry" )
348+
349+ assert response .status_code == 503
350+ assert store .jobs ["job-1" ]["status" ] == "failed"
351+ assert store .jobs ["job-1" ]["error" ] == (
352+ "temporal unavailable; billing reservation release failed: mongo unavailable"
353+ )
354+
355+
306356def test_v2_retry_start_failure_keeps_reused_billing_reservation (monkeypatch ):
307357 app , _ = _build_app (monkeypatch )
308358 store = FakeJobStore ()
0 commit comments