deltacat 2.0.0b11__py3-none-any.whl → 2.0.0.post1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. deltacat/__init__.py +78 -3
  2. deltacat/api.py +122 -67
  3. deltacat/aws/constants.py +0 -23
  4. deltacat/aws/s3u.py +4 -631
  5. deltacat/benchmarking/conftest.py +0 -18
  6. deltacat/catalog/__init__.py +2 -0
  7. deltacat/catalog/delegate.py +445 -63
  8. deltacat/catalog/interface.py +188 -62
  9. deltacat/catalog/main/impl.py +2417 -271
  10. deltacat/catalog/model/catalog.py +49 -10
  11. deltacat/catalog/model/properties.py +38 -0
  12. deltacat/compute/compactor/compaction_session.py +97 -75
  13. deltacat/compute/compactor/model/compact_partition_params.py +75 -30
  14. deltacat/compute/compactor/model/compaction_session_audit_info.py +17 -0
  15. deltacat/compute/compactor/model/round_completion_info.py +16 -6
  16. deltacat/compute/compactor/repartition_session.py +8 -21
  17. deltacat/compute/compactor/steps/hash_bucket.py +5 -5
  18. deltacat/compute/compactor/steps/materialize.py +9 -7
  19. deltacat/compute/compactor/steps/repartition.py +12 -11
  20. deltacat/compute/compactor/utils/io.py +6 -5
  21. deltacat/compute/compactor/utils/round_completion_reader.py +117 -0
  22. deltacat/compute/compactor/utils/system_columns.py +3 -1
  23. deltacat/compute/compactor_v2/compaction_session.py +17 -14
  24. deltacat/compute/compactor_v2/constants.py +30 -1
  25. deltacat/compute/compactor_v2/model/evaluate_compaction_result.py +0 -1
  26. deltacat/compute/compactor_v2/model/hash_bucket_input.py +9 -3
  27. deltacat/compute/compactor_v2/model/merge_file_group.py +5 -2
  28. deltacat/compute/compactor_v2/model/merge_input.py +33 -8
  29. deltacat/compute/compactor_v2/private/compaction_utils.py +167 -68
  30. deltacat/compute/compactor_v2/steps/hash_bucket.py +5 -2
  31. deltacat/compute/compactor_v2/steps/merge.py +267 -55
  32. deltacat/compute/compactor_v2/utils/content_type_params.py +34 -6
  33. deltacat/compute/compactor_v2/utils/dedupe.py +1 -1
  34. deltacat/compute/compactor_v2/utils/delta.py +5 -3
  35. deltacat/compute/compactor_v2/utils/io.py +11 -4
  36. deltacat/compute/compactor_v2/utils/merge.py +15 -2
  37. deltacat/compute/compactor_v2/utils/primary_key_index.py +28 -4
  38. deltacat/compute/compactor_v2/utils/task_options.py +45 -33
  39. deltacat/compute/converter/converter_session.py +145 -32
  40. deltacat/compute/converter/model/convert_input.py +26 -19
  41. deltacat/compute/converter/model/convert_input_files.py +33 -16
  42. deltacat/compute/converter/model/convert_result.py +35 -16
  43. deltacat/compute/converter/model/converter_session_params.py +24 -21
  44. deltacat/compute/converter/pyiceberg/catalog.py +21 -18
  45. deltacat/compute/converter/pyiceberg/overrides.py +18 -9
  46. deltacat/compute/converter/pyiceberg/update_snapshot_overrides.py +148 -100
  47. deltacat/compute/converter/steps/convert.py +157 -50
  48. deltacat/compute/converter/steps/dedupe.py +24 -11
  49. deltacat/compute/converter/utils/convert_task_options.py +27 -12
  50. deltacat/compute/converter/utils/converter_session_utils.py +126 -60
  51. deltacat/compute/converter/utils/iceberg_columns.py +8 -8
  52. deltacat/compute/converter/utils/io.py +101 -12
  53. deltacat/compute/converter/utils/s3u.py +33 -27
  54. deltacat/compute/janitor.py +205 -0
  55. deltacat/compute/jobs/client.py +19 -8
  56. deltacat/compute/resource_estimation/delta.py +38 -6
  57. deltacat/compute/resource_estimation/model.py +8 -0
  58. deltacat/constants.py +44 -0
  59. deltacat/docs/autogen/schema/__init__.py +0 -0
  60. deltacat/docs/autogen/schema/inference/__init__.py +0 -0
  61. deltacat/docs/autogen/schema/inference/generate_type_mappings.py +687 -0
  62. deltacat/docs/autogen/schema/inference/parse_json_type_mappings.py +673 -0
  63. deltacat/examples/compactor/__init__.py +0 -0
  64. deltacat/examples/compactor/aws/__init__.py +1 -0
  65. deltacat/examples/compactor/bootstrap.py +863 -0
  66. deltacat/examples/compactor/compactor.py +373 -0
  67. deltacat/examples/compactor/explorer.py +473 -0
  68. deltacat/examples/compactor/gcp/__init__.py +1 -0
  69. deltacat/examples/compactor/job_runner.py +439 -0
  70. deltacat/examples/compactor/utils/__init__.py +1 -0
  71. deltacat/examples/compactor/utils/common.py +261 -0
  72. deltacat/examples/experimental/iceberg/converter/__init__.py +0 -0
  73. deltacat/examples/experimental/iceberg/converter/beam/__init__.py +0 -0
  74. deltacat/examples/experimental/iceberg/converter/beam/app.py +226 -0
  75. deltacat/examples/experimental/iceberg/converter/beam/main.py +133 -0
  76. deltacat/examples/experimental/iceberg/converter/beam/test_workflow.py +113 -0
  77. deltacat/examples/experimental/iceberg/converter/beam/utils/__init__.py +3 -0
  78. deltacat/examples/experimental/iceberg/converter/beam/utils/common.py +174 -0
  79. deltacat/examples/experimental/iceberg/converter/beam/utils/spark.py +263 -0
  80. deltacat/exceptions.py +66 -4
  81. deltacat/experimental/catalog/iceberg/impl.py +2 -2
  82. deltacat/experimental/compatibility/__init__.py +0 -0
  83. deltacat/experimental/compatibility/backfill_locator_to_id_mappings.py +201 -0
  84. deltacat/experimental/converter_agent/__init__.py +0 -0
  85. deltacat/experimental/converter_agent/beam/__init__.py +0 -0
  86. deltacat/experimental/converter_agent/beam/managed.py +173 -0
  87. deltacat/experimental/converter_agent/table_monitor.py +479 -0
  88. deltacat/experimental/storage/iceberg/iceberg_scan_planner.py +105 -4
  89. deltacat/experimental/storage/iceberg/impl.py +5 -3
  90. deltacat/experimental/storage/iceberg/model.py +7 -3
  91. deltacat/experimental/storage/iceberg/visitor.py +119 -0
  92. deltacat/experimental/storage/rivulet/dataset.py +0 -3
  93. deltacat/experimental/storage/rivulet/metastore/delta.py +0 -2
  94. deltacat/experimental/storage/rivulet/reader/dataset_metastore.py +3 -2
  95. deltacat/io/datasource/deltacat_datasource.py +0 -1
  96. deltacat/storage/__init__.py +20 -2
  97. deltacat/storage/interface.py +54 -32
  98. deltacat/storage/main/impl.py +1494 -541
  99. deltacat/storage/model/delta.py +27 -3
  100. deltacat/storage/model/locator.py +6 -12
  101. deltacat/storage/model/manifest.py +182 -6
  102. deltacat/storage/model/metafile.py +151 -78
  103. deltacat/storage/model/namespace.py +8 -1
  104. deltacat/storage/model/partition.py +117 -42
  105. deltacat/storage/model/schema.py +2427 -159
  106. deltacat/storage/model/sort_key.py +40 -0
  107. deltacat/storage/model/stream.py +9 -2
  108. deltacat/storage/model/table.py +12 -1
  109. deltacat/storage/model/table_version.py +11 -0
  110. deltacat/storage/model/transaction.py +1184 -208
  111. deltacat/storage/model/transform.py +81 -2
  112. deltacat/storage/model/types.py +48 -26
  113. deltacat/tests/_io/test_cloudpickle_bug_fix.py +8 -4
  114. deltacat/tests/aws/test_s3u.py +2 -31
  115. deltacat/tests/catalog/main/test_catalog_impl_table_operations.py +1606 -70
  116. deltacat/tests/catalog/test_catalogs.py +54 -11
  117. deltacat/tests/catalog/test_default_catalog_impl.py +12152 -71
  118. deltacat/tests/compute/compact_partition_test_cases.py +35 -8
  119. deltacat/tests/compute/compactor/steps/test_repartition.py +12 -12
  120. deltacat/tests/compute/compactor/utils/test_io.py +124 -120
  121. deltacat/tests/compute/compactor/utils/test_round_completion_reader.py +254 -0
  122. deltacat/tests/compute/compactor_v2/test_compaction_session.py +423 -312
  123. deltacat/tests/compute/compactor_v2/utils/test_content_type_params.py +266 -0
  124. deltacat/tests/compute/compactor_v2/utils/test_primary_key_index.py +45 -0
  125. deltacat/tests/compute/compactor_v2/utils/test_task_options.py +270 -1
  126. deltacat/tests/compute/conftest.py +8 -44
  127. deltacat/tests/compute/converter/test_convert_session.py +675 -490
  128. deltacat/tests/compute/converter/utils.py +15 -6
  129. deltacat/tests/compute/resource_estimation/test_delta.py +145 -79
  130. deltacat/tests/compute/test_compact_partition_incremental.py +103 -70
  131. deltacat/tests/compute/test_compact_partition_multiple_rounds.py +89 -66
  132. deltacat/tests/compute/test_compact_partition_params.py +13 -8
  133. deltacat/tests/compute/test_compact_partition_rebase.py +77 -62
  134. deltacat/tests/compute/test_compact_partition_rebase_then_incremental.py +263 -193
  135. deltacat/tests/compute/test_janitor.py +236 -0
  136. deltacat/tests/compute/test_util_common.py +716 -43
  137. deltacat/tests/compute/test_util_constant.py +0 -1
  138. deltacat/tests/{storage/conftest.py → conftest.py} +1 -1
  139. deltacat/tests/experimental/__init__.py +1 -0
  140. deltacat/tests/experimental/compatibility/__init__.py +1 -0
  141. deltacat/tests/experimental/compatibility/test_backfill_locator_to_id_mappings.py +582 -0
  142. deltacat/tests/storage/main/test_main_storage.py +6900 -95
  143. deltacat/tests/storage/model/test_metafile_io.py +78 -173
  144. deltacat/tests/storage/model/test_partition_scheme.py +85 -0
  145. deltacat/tests/storage/model/test_schema.py +171 -0
  146. deltacat/tests/storage/model/test_schema_update.py +1925 -0
  147. deltacat/tests/storage/model/test_sort_scheme.py +90 -0
  148. deltacat/tests/storage/model/test_transaction.py +393 -48
  149. deltacat/tests/storage/model/test_transaction_history.py +886 -0
  150. deltacat/tests/test_deltacat_api.py +988 -4
  151. deltacat/tests/test_exceptions.py +9 -5
  152. deltacat/tests/test_utils/pyarrow.py +52 -21
  153. deltacat/tests/test_utils/storage.py +23 -34
  154. deltacat/tests/types/__init__.py +0 -0
  155. deltacat/tests/types/test_tables.py +104 -0
  156. deltacat/tests/utils/exceptions.py +22 -0
  157. deltacat/tests/utils/main_deltacat_storage_mock.py +31 -0
  158. deltacat/tests/utils/ray_utils/test_dataset.py +123 -5
  159. deltacat/tests/utils/test_daft.py +121 -31
  160. deltacat/tests/utils/test_numpy.py +1193 -0
  161. deltacat/tests/utils/test_pandas.py +1106 -0
  162. deltacat/tests/utils/test_polars.py +1040 -0
  163. deltacat/tests/utils/test_pyarrow.py +1370 -89
  164. deltacat/types/media.py +221 -11
  165. deltacat/types/tables.py +2329 -59
  166. deltacat/utils/arguments.py +33 -1
  167. deltacat/utils/daft.py +411 -150
  168. deltacat/utils/filesystem.py +100 -0
  169. deltacat/utils/metafile_locator.py +2 -1
  170. deltacat/utils/numpy.py +118 -26
  171. deltacat/utils/pandas.py +577 -48
  172. deltacat/utils/polars.py +658 -27
  173. deltacat/utils/pyarrow.py +1258 -213
  174. deltacat/utils/ray_utils/dataset.py +101 -10
  175. deltacat/utils/reader_compatibility_mapping.py +3083 -0
  176. deltacat/utils/url.py +56 -15
  177. deltacat-2.0.0.post1.dist-info/METADATA +1163 -0
  178. {deltacat-2.0.0b11.dist-info → deltacat-2.0.0.post1.dist-info}/RECORD +183 -145
  179. {deltacat-2.0.0b11.dist-info → deltacat-2.0.0.post1.dist-info}/WHEEL +1 -1
  180. deltacat/compute/compactor/utils/round_completion_file.py +0 -97
  181. deltacat/compute/merge_on_read/__init__.py +0 -4
  182. deltacat/compute/merge_on_read/daft.py +0 -40
  183. deltacat/compute/merge_on_read/model/merge_on_read_params.py +0 -66
  184. deltacat/compute/merge_on_read/utils/delta.py +0 -42
  185. deltacat/tests/compute/compactor/utils/test_round_completion_file.py +0 -231
  186. deltacat/tests/compute/test_util_create_table_deltas_repo.py +0 -388
  187. deltacat/tests/local_deltacat_storage/__init__.py +0 -1236
  188. deltacat/tests/local_deltacat_storage/exceptions.py +0 -10
  189. deltacat/utils/s3fs.py +0 -21
  190. deltacat-2.0.0b11.dist-info/METADATA +0 -67
  191. /deltacat/{compute/merge_on_read/model → docs}/__init__.py +0 -0
  192. /deltacat/{compute/merge_on_read/utils → docs/autogen}/__init__.py +0 -0
  193. {deltacat-2.0.0b11.dist-info → deltacat-2.0.0.post1.dist-info/licenses}/LICENSE +0 -0
  194. {deltacat-2.0.0b11.dist-info → deltacat-2.0.0.post1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,236 @@
1
+ import os
2
+ import time
3
+ import posixpath
4
+ from pyarrow.fs import FileSelector
5
+
6
+ from deltacat.storage import (
7
+ Transaction,
8
+ TransactionOperation,
9
+ TransactionOperationType,
10
+ )
11
+
12
+ from deltacat.constants import (
13
+ TXN_DIR_NAME,
14
+ RUNNING_TXN_DIR_NAME,
15
+ FAILED_TXN_DIR_NAME,
16
+ TXN_PART_SEPARATOR,
17
+ SUCCESS_TXN_DIR_NAME,
18
+ )
19
+ from deltacat.utils.filesystem import resolve_path_and_filesystem
20
+ from deltacat.tests.test_utils.storage import (
21
+ create_test_namespace,
22
+ create_test_table,
23
+ )
24
+ from deltacat.compute.janitor import (
25
+ janitor_delete_timed_out_transaction,
26
+ janitor_remove_files_in_failed,
27
+ )
28
+
29
+
30
+ class TestJanitorJob:
31
+ def test_janitor_delete_timed_out_transaction(self, temp_dir):
32
+ # Set up test directories and filesystem
33
+ catalog_root, filesystem = resolve_path_and_filesystem(temp_dir)
34
+ running_txn_dir = posixpath.join(
35
+ catalog_root, TXN_DIR_NAME, RUNNING_TXN_DIR_NAME
36
+ )
37
+ failed_txn_dir = posixpath.join(catalog_root, TXN_DIR_NAME, FAILED_TXN_DIR_NAME)
38
+ os.makedirs(running_txn_dir, exist_ok=True)
39
+ os.makedirs(failed_txn_dir, exist_ok=True)
40
+
41
+ # Create a test transaction log that is already timed out.
42
+ # Note: The janitor expects the first token to be the intended end time.
43
+ start_time = time.time_ns() - 1_000_000_000 # 1 second in the past
44
+ txn_id = "test_transaction_id"
45
+ # The file name uses past_end_time as the first token so that it qualifies as timed out.
46
+ txn_filename = f"{start_time}{TXN_PART_SEPARATOR}{txn_id}{TXN_PART_SEPARATOR}{time.time_ns()}"
47
+ txn_path = posixpath.join(running_txn_dir, txn_filename)
48
+
49
+ # Create a mock transaction file in the running directory.
50
+ with open(txn_path, "w") as f:
51
+ f.write("mock transaction content")
52
+
53
+ # Create a test metafile that contains the transaction id to trigger the renaming logic.
54
+ test_metafile_path = posixpath.join(
55
+ catalog_root, f"test_metafile_{txn_id}.json"
56
+ )
57
+ with open(test_metafile_path, "w") as f:
58
+ f.write("mock metafile content")
59
+
60
+ assert os.path.exists(test_metafile_path), "Test metafile was not deleted."
61
+
62
+ assert os.path.exists(
63
+ txn_path
64
+ ), "Transaction file still exists in running directory."
65
+
66
+ # Run the janitor job that should:
67
+ # 1. Move the running txn file to the failed directory with TIMEOUT_TXN appended.
68
+ # 2. Invoke brute force search to deletes the metafiles and cleans up txn log files.
69
+ janitor_delete_timed_out_transaction(temp_dir)
70
+
71
+ new_txn_file_name = f"{txn_filename}"
72
+ new_failed_txn_path = posixpath.join(failed_txn_dir, new_txn_file_name)
73
+
74
+ # Verify that the renamed file exists in the failed directory.
75
+ assert os.path.exists(
76
+ new_failed_txn_path
77
+ ), f"Expected {new_failed_txn_path} to exist."
78
+ # Verify that the transaction file is no longer in the running directory.
79
+ assert not os.path.exists(
80
+ txn_path
81
+ ), "Transaction file still exists in running directory."
82
+ # Verify that the metafile was deleted.
83
+ assert not os.path.exists(test_metafile_path), "Test metafile was not deleted."
84
+
85
+ def test_janitor_handles_empty_directories(self, temp_dir):
86
+ # Set up test directories and filesystem
87
+ catalog_root, filesystem = resolve_path_and_filesystem(temp_dir)
88
+ running_txn_dir = posixpath.join(
89
+ catalog_root, TXN_DIR_NAME, RUNNING_TXN_DIR_NAME
90
+ )
91
+ failed_txn_dir = posixpath.join(catalog_root, TXN_DIR_NAME, FAILED_TXN_DIR_NAME)
92
+
93
+ # Ensure directories exist but are empty
94
+ os.makedirs(running_txn_dir, exist_ok=True)
95
+ os.makedirs(failed_txn_dir, exist_ok=True)
96
+
97
+ # Ensure directories are empty
98
+ assert not os.listdir(
99
+ running_txn_dir
100
+ ), "Running transaction directory is not empty."
101
+ assert not os.listdir(
102
+ failed_txn_dir
103
+ ), "Failed transaction directory is not empty."
104
+
105
+ # Run the janitor functions on the empty directories
106
+ try:
107
+ janitor_delete_timed_out_transaction(temp_dir)
108
+ janitor_remove_files_in_failed(temp_dir, filesystem)
109
+ except Exception as e:
110
+ assert (
111
+ False
112
+ ), f"Janitor functions should not fail on empty directories, but got exception: {e}"
113
+
114
+ # Verify that directories are still empty after running janitor functions
115
+ assert not os.listdir(
116
+ running_txn_dir
117
+ ), "Running transaction directory should still be empty."
118
+ assert not os.listdir(
119
+ failed_txn_dir
120
+ ), "Failed transaction directory should still be empty."
121
+
122
+ def test_janitor_handles_multiple_timed_out_transactions(self, temp_dir):
123
+ # Set up test directories and filesystem
124
+ catalog_root, filesystem = resolve_path_and_filesystem(temp_dir)
125
+ running_txn_dir = posixpath.join(
126
+ catalog_root, TXN_DIR_NAME, RUNNING_TXN_DIR_NAME
127
+ )
128
+ failed_txn_dir = posixpath.join(catalog_root, TXN_DIR_NAME, FAILED_TXN_DIR_NAME)
129
+ os.makedirs(running_txn_dir, exist_ok=True)
130
+ os.makedirs(failed_txn_dir, exist_ok=True)
131
+
132
+ # Create multiple timed-out transaction logs
133
+ txn_ids = ["txn_one", "txn_two", "txn_three"]
134
+ start_time = time.time_ns() - 1_000_000_000 # 1 second in the past
135
+
136
+ txn_filenames = []
137
+ for txn_id in txn_ids:
138
+ txn_filename = f"{start_time}{TXN_PART_SEPARATOR}{txn_id}{TXN_PART_SEPARATOR}{time.time_ns()}"
139
+ txn_path = posixpath.join(running_txn_dir, txn_filename)
140
+
141
+ # Create mock transaction files in the running directory
142
+ with open(txn_path, "w") as f:
143
+ f.write("mock transaction content")
144
+
145
+ # Optionally, create a metafile for each transaction if your janitor function processes these as well
146
+ test_metafile_path = posixpath.join(
147
+ catalog_root, f"test_metafile_{txn_id}.json"
148
+ )
149
+ with open(test_metafile_path, "w") as f:
150
+ f.write("mock metafile content")
151
+
152
+ txn_filenames.append((txn_filename, txn_path, test_metafile_path))
153
+
154
+ # Run the janitor function to move timed-out transactions to the failed directory
155
+ janitor_delete_timed_out_transaction(temp_dir)
156
+
157
+ # Verify that all transactions were moved to the failed directory
158
+ for txn_filename, txn_path, test_metafile_path in txn_filenames:
159
+ new_txn_filename = f"{txn_filename}"
160
+ new_failed_txn_path = posixpath.join(failed_txn_dir, new_txn_filename)
161
+
162
+ # Check if the renamed transaction file exists in the failed directory
163
+ assert os.path.exists(
164
+ new_failed_txn_path
165
+ ), f"Expected {new_failed_txn_path} to exist."
166
+
167
+ # Check if the transaction file is no longer in the running directory
168
+ assert not os.path.exists(
169
+ txn_path
170
+ ), f"Transaction file {txn_path} still exists in running directory."
171
+
172
+ # Check if the corresponding metafile was deleted (if applicable)
173
+ assert not os.path.exists(
174
+ test_metafile_path
175
+ ), f"Metafile {test_metafile_path} was not deleted."
176
+
177
+ def test_janitor_remove_files_failed(self, temp_dir):
178
+ # Set up test directories and filesystem
179
+ catalog_root, filesystem = resolve_path_and_filesystem(temp_dir)
180
+ txn_log_dir = posixpath.join(catalog_root, TXN_DIR_NAME)
181
+ failed_txn_dir = posixpath.join(txn_log_dir, FAILED_TXN_DIR_NAME)
182
+ running_txn_dir = posixpath.join(txn_log_dir, RUNNING_TXN_DIR_NAME)
183
+ success_txn_dir = posixpath.join(txn_log_dir, SUCCESS_TXN_DIR_NAME)
184
+
185
+ # Ensure all necessary directories exist
186
+ for dir_path in [failed_txn_dir, running_txn_dir]:
187
+ os.makedirs(dir_path, exist_ok=True)
188
+
189
+ # Create metadata for the transaction
190
+ meta_to_create = [
191
+ create_test_namespace(),
192
+ create_test_table(),
193
+ ]
194
+
195
+ txn_operations = [
196
+ TransactionOperation.of(
197
+ operation_type=TransactionOperationType.CREATE,
198
+ dest_metafile=meta,
199
+ )
200
+ for meta in meta_to_create
201
+ ]
202
+
203
+ transaction = Transaction.of(txn_operations)
204
+ write_paths, txn_log_path = transaction.commit(temp_dir)
205
+
206
+ # Get filename of committed transaction (from success directory)
207
+ success_file_dir = filesystem.get_file_info(
208
+ FileSelector(success_txn_dir, recursive=False)
209
+ )
210
+ success_files = filesystem.get_file_info(
211
+ FileSelector(success_file_dir[0].path, recursive=False)
212
+ )
213
+ filename = posixpath.basename(success_file_dir[0].path)
214
+
215
+ # Compute destination paths
216
+ failed_txn_path = posixpath.join(failed_txn_dir, filename)
217
+ running_txn_path = posixpath.join(running_txn_dir, filename)
218
+ # Move the file from success to failed to simulate a failed transactions
219
+
220
+ filesystem.copy_file(success_files[0].path, failed_txn_path)
221
+ filesystem.copy_file(success_files[0].path, running_txn_path)
222
+
223
+ # Verify that the write path files exist before cleanup.
224
+ for path in write_paths:
225
+ assert os.path.exists(
226
+ path
227
+ ), f"Expected write path {path} to exist before cleanup."
228
+
229
+ # Run the cleanup function.
230
+ janitor_remove_files_in_failed(temp_dir, filesystem)
231
+
232
+ # Check that all write paths have been deleted.
233
+ for path in write_paths:
234
+ assert not os.path.exists(
235
+ path
236
+ ), f"Write path {path} should have been deleted after cleanup."