@@ -381,7 +381,14 @@ def create_snapshot(snapshot_id: str, sandbox_id: str):
381381 runtime .calls .append ((snapshot_id , sandbox_id ))
382382 return ready_status
383383
384+ def delete_snapshot (snapshot_id : str , image : str | None = None ) -> None :
385+ stored = repo .get (snapshot_id )
386+ assert stored is not None
387+ assert stored .status .state == SnapshotState .DELETING
388+ runtime .delete_calls .append ((snapshot_id , image ))
389+
384390 runtime .create_snapshot = create_snapshot
391+ runtime .delete_snapshot = delete_snapshot
385392 created = service .create_snapshot ("sbx-001" , CreateSnapshotRequest (name = "checkpoint" ))
386393
387394 service .delete_snapshot (created .id )
@@ -420,8 +427,49 @@ def delete_snapshot(snapshot_id: str, image: str | None = None) -> None:
420427 with pytest .raises (HTTPException ) as exc_info :
421428 service .delete_snapshot ("snap-in-use" )
422429
430+ stored = repo .get ("snap-in-use" )
423431 assert exc_info .value .status_code == 409
424- assert repo .get ("snap-in-use" ) is not None
432+ assert stored is not None
433+ assert stored .status .state == SnapshotState .DELETING
434+
435+
436+ def test_snapshot_service_recovers_delete_after_runtime_cleanup_succeeds (tmp_path ) -> None :
437+ repo = SQLiteSnapshotRepository (tmp_path / "snapshots.db" )
438+ runtime = StubSnapshotRuntime ()
439+ service = PersistedSnapshotService (
440+ repo ,
441+ StubSandboxService (),
442+ snapshot_runtime = runtime ,
443+ snapshot_executor = ImmediateExecutor (),
444+ )
445+
446+ record = _snapshot_record (
447+ "snap-delete-crash" ,
448+ SnapshotState .READY ,
449+ image = "opensandbox-snapshots:snap-delete-crash" ,
450+ )
451+ repo .create (record )
452+
453+ original_delete = repo .delete
454+
455+ def crash_delete (snapshot_id : str ) -> None :
456+ raise RuntimeError ("simulated metadata delete crash" )
457+
458+ repo .delete = crash_delete
459+ with pytest .raises (RuntimeError , match = "simulated metadata delete crash" ):
460+ service .delete_snapshot ("snap-delete-crash" )
461+
462+ stored = repo .get ("snap-delete-crash" )
463+ assert stored is not None
464+ assert stored .status .state == SnapshotState .DELETING
465+ assert runtime .delete_calls == [("snap-delete-crash" , "opensandbox-snapshots:snap-delete-crash" )]
466+
467+ repo .delete = original_delete
468+ recovery_runtime = StubSnapshotRuntime ()
469+ PersistedSnapshotService (repo , StubSandboxService (), snapshot_runtime = recovery_runtime )
470+
471+ assert recovery_runtime .delete_calls == [("snap-delete-crash" , "opensandbox-snapshots:snap-delete-crash" )]
472+ assert repo .get ("snap-delete-crash" ) is None
425473
426474
427475def test_snapshot_service_worker_cleans_up_snapshot_deleted_during_creation (tmp_path ) -> None :
0 commit comments