2525import requests
2626
2727import storage_add_bucket_label
28- import storage_async_upload
2928import storage_async_download
29+ import storage_async_upload
3030import storage_batch_request
3131import storage_bucket_delete_default_kms_key
3232import storage_change_default_storage_class
4444import storage_delete_file
4545import storage_delete_file_archived_generation
4646import storage_disable_bucket_lifecycle_management
47+ import storage_disable_soft_delete
4748import storage_disable_versioning
4849import storage_download_byte_range
4950import storage_download_file
5960import storage_get_autoclass
6061import storage_get_bucket_labels
6162import storage_get_bucket_metadata
62- import storage_get_soft_deleted_bucket
6363import storage_get_metadata
6464import storage_get_service_account
65+ import storage_get_soft_delete_policy
66+ import storage_get_soft_deleted_bucket
6567import storage_list_buckets
66- import storage_list_soft_deleted_buckets
67- import storage_restore_soft_deleted_bucket
6868import storage_list_file_archived_generations
6969import storage_list_files
7070import storage_list_files_with_prefix
71+ import storage_list_soft_deleted_buckets
72+ import storage_list_soft_deleted_object_versions
73+ import storage_list_soft_deleted_objects
7174import storage_make_public
7275import storage_move_file
7376import storage_object_get_kms_key
7477import storage_remove_bucket_label
7578import storage_remove_cors_configuration
7679import storage_rename_file
80+ import storage_restore_object
81+ import storage_restore_soft_deleted_bucket
7782import storage_set_autoclass
7883import storage_set_bucket_default_kms_key
7984import storage_set_client_endpoint
80- import storage_set_object_retention_policy
8185import storage_set_metadata
86+ import storage_set_object_retention_policy
87+ import storage_set_soft_delete_policy
8288import storage_trace_quickstart
8389import storage_transfer_manager_download_bucket
8490import storage_transfer_manager_download_chunks_concurrently
@@ -147,6 +153,21 @@ def test_soft_deleted_bucket():
147153 yield bucket
148154
149155
156+ @pytest .fixture (scope = "function" )
157+ def test_soft_delete_enabled_bucket ():
158+ """Yields a bucket with soft-delete enabled that is deleted after the test completes."""
159+ bucket = None
160+ while bucket is None or bucket .exists ():
161+ bucket_name = f"storage-snippets-test-{ uuid .uuid4 ()} "
162+ bucket = storage .Client ().bucket (bucket_name )
163+ # Soft-delete retention for 7 days (minimum allowed by API)
164+ bucket .soft_delete_policy .retention_duration_seconds = 7 * 24 * 60 * 60
165+ # Soft-delete requires a region
166+ bucket .create (location = "US-CENTRAL1" )
167+ yield bucket
168+ bucket .delete (force = True )
169+
170+
150171@pytest .fixture (scope = "function" )
151172def test_public_bucket ():
152173 # The new projects don't allow to make a bucket available to public, so
@@ -230,13 +251,17 @@ def test_bucket_metadata(test_bucket, capsys):
230251
231252
232253def test_get_soft_deleted_bucket (test_soft_deleted_bucket , capsys ):
233- storage_get_soft_deleted_bucket .get_soft_deleted_bucket (test_soft_deleted_bucket .name , test_soft_deleted_bucket .generation )
254+ storage_get_soft_deleted_bucket .get_soft_deleted_bucket (
255+ test_soft_deleted_bucket .name , test_soft_deleted_bucket .generation
256+ )
234257 out , _ = capsys .readouterr ()
235258 assert test_soft_deleted_bucket .name in out
236259
237260
238261def test_restore_soft_deleted_bucket (test_soft_deleted_bucket , capsys ):
239- storage_restore_soft_deleted_bucket .restore_bucket (test_soft_deleted_bucket .name , test_soft_deleted_bucket .generation )
262+ storage_restore_soft_deleted_bucket .restore_bucket (
263+ test_soft_deleted_bucket .name , test_soft_deleted_bucket .generation
264+ )
240265 out , _ = capsys .readouterr ()
241266 assert test_soft_deleted_bucket .name in out
242267
@@ -309,7 +334,9 @@ def test_async_download(test_bucket, capsys):
309334 blob = test_bucket .blob (source )
310335 blob .upload_from_string (source )
311336
312- asyncio .run (storage_async_download .async_download_blobs (test_bucket .name , * source_files ))
337+ asyncio .run (
338+ storage_async_download .async_download_blobs (test_bucket .name , * source_files )
339+ )
313340 out , _ = capsys .readouterr ()
314341 for x in range (object_count ):
315342 assert f"Downloaded storage object async_sample_blob_{ x } " in out
@@ -877,7 +904,10 @@ def test_object_retention_policy(test_bucket_create, capsys):
877904 test_bucket_create .name
878905 )
879906 out , _ = capsys .readouterr ()
880- assert f"Created bucket { test_bucket_create .name } with object retention enabled setting" in out
907+ assert (
908+ f"Created bucket { test_bucket_create .name } with object retention enabled setting"
909+ in out
910+ )
881911
882912 blob_name = "test_object_retention"
883913 storage_set_object_retention_policy .set_object_retention_policy (
@@ -898,7 +928,10 @@ def test_create_bucket_hierarchical_namespace(test_bucket_create, capsys):
898928 test_bucket_create .name
899929 )
900930 out , _ = capsys .readouterr ()
901- assert f"Created bucket { test_bucket_create .name } with hierarchical namespace enabled" in out
931+ assert (
932+ f"Created bucket { test_bucket_create .name } with hierarchical namespace enabled"
933+ in out
934+ )
902935
903936
904937def test_storage_trace_quickstart (test_bucket , capsys ):
@@ -911,3 +944,96 @@ def test_storage_trace_quickstart(test_bucket, capsys):
911944 assert (
912945 f"Downloaded storage object { blob_name } from bucket { test_bucket .name } " in out
913946 )
947+
948+
949+ def test_storage_disable_soft_delete (test_soft_delete_enabled_bucket , capsys ):
950+ bucket_name = test_soft_delete_enabled_bucket .name
951+ storage_disable_soft_delete .disable_soft_delete (bucket_name )
952+ out , _ = capsys .readouterr ()
953+ assert f"Soft-delete policy is disabled for bucket { bucket_name } " in out
954+
955+
956+ def test_storage_get_soft_delete_policy (test_soft_delete_enabled_bucket , capsys ):
957+ bucket_name = test_soft_delete_enabled_bucket .name
958+ storage_get_soft_delete_policy .get_soft_delete_policy (bucket_name )
959+ out , _ = capsys .readouterr ()
960+ assert f"Soft-delete policy for { bucket_name } " in out
961+ assert "Object soft-delete policy is enabled" in out
962+ assert "Object retention duration: " in out
963+ assert "Policy effective time: " in out
964+
965+ # Disable the soft-delete policy
966+ test_soft_delete_enabled_bucket .soft_delete_policy .retention_duration_seconds = 0
967+ test_soft_delete_enabled_bucket .patch ()
968+ storage_get_soft_delete_policy .get_soft_delete_policy (bucket_name )
969+ out , _ = capsys .readouterr ()
970+ assert f"Soft-delete policy for { bucket_name } " in out
971+ assert "Object soft-delete policy is disabled" in out
972+
973+
974+ def test_storage_set_soft_delete_policy (test_soft_delete_enabled_bucket , capsys ):
975+ bucket_name = test_soft_delete_enabled_bucket .name
976+ retention_duration_seconds = 10 * 24 * 60 * 60 # 10 days
977+ storage_set_soft_delete_policy .set_soft_delete_policy (
978+ bucket_name , retention_duration_seconds
979+ )
980+ out , _ = capsys .readouterr ()
981+ assert (
982+ f"Soft delete policy for bucket { bucket_name } was set to { retention_duration_seconds } seconds retention period"
983+ in out
984+ )
985+
986+
987+ def test_storage_list_soft_deleted_objects (test_soft_delete_enabled_bucket , capsys ):
988+ bucket_name = test_soft_delete_enabled_bucket .name
989+ blob_name = f"test_object_{ uuid .uuid4 ().hex } .txt"
990+ blob_content = "This object will be soft-deleted for listing."
991+ blob = test_soft_delete_enabled_bucket .blob (blob_name )
992+ blob .upload_from_string (blob_content )
993+ blob_generation = blob .generation
994+
995+ blob .delete () # Soft-delete the object
996+ storage_list_soft_deleted_objects .list_soft_deleted_objects (bucket_name )
997+ out , _ = capsys .readouterr ()
998+ assert f"Name: { blob_name } , Generation: { blob_generation } " in out
999+
1000+
1001+ def test_storage_list_soft_deleted_object_versions (
1002+ test_soft_delete_enabled_bucket , capsys
1003+ ):
1004+ bucket_name = test_soft_delete_enabled_bucket .name
1005+ blob_name = f"test_object_{ uuid .uuid4 ().hex } .txt"
1006+ blob_content = "This object will be soft-deleted for version listing."
1007+ blob = test_soft_delete_enabled_bucket .blob (blob_name )
1008+ blob .upload_from_string (blob_content )
1009+ blob_generation = blob .generation
1010+
1011+ blob .delete () # Soft-delete the object
1012+ storage_list_soft_deleted_object_versions .list_soft_deleted_object_versions (
1013+ bucket_name , blob_name
1014+ )
1015+ out , _ = capsys .readouterr ()
1016+ assert f"Version ID: { blob_generation } " in out
1017+
1018+
1019+ def test_storage_restore_soft_deleted_object (test_soft_delete_enabled_bucket , capsys ):
1020+ bucket_name = test_soft_delete_enabled_bucket .name
1021+ blob_name = f"test-restore-sd-obj-{ uuid .uuid4 ().hex } .txt"
1022+ blob_content = "This object will be soft-deleted and restored."
1023+ blob = test_soft_delete_enabled_bucket .blob (blob_name )
1024+ blob .upload_from_string (blob_content )
1025+ blob_generation = blob .generation
1026+
1027+ blob .delete () # Soft-delete the object
1028+ storage_restore_object .restore_soft_deleted_object (
1029+ bucket_name , blob_name , blob_generation
1030+ )
1031+ out , _ = capsys .readouterr ()
1032+ assert (
1033+ f"Soft-deleted object { blob_name } is restored in the bucket { bucket_name } "
1034+ in out
1035+ )
1036+
1037+ # Verify the restoration
1038+ blob = test_soft_delete_enabled_bucket .get_blob (blob_name )
1039+ assert blob is not None
0 commit comments