batch_mode_batch_triple/
move_batch_files.rs

1// ---------------- [ File: batch-mode-batch-triple/src/move_batch_files.rs ]
2crate::ix!();
3
4impl BatchFileTriple {
5
6    pub async fn move_input_and_output_to_done(
7        &self,
8    ) -> Result<(), FileMoveError> {
9
10        let done_dir = self.get_done_directory();
11        self.maybe_move_input_to_done(&done_dir).await?;
12        self.maybe_move_output_to_done(&done_dir).await?;
13        self.maybe_move_metadata_to_done(&done_dir).await?;
14        Ok(())
15    }
16
17    pub async fn move_input_and_error_to_done(
18        &self,
19    ) -> Result<(), FileMoveError> {
20
21        let done_dir = self.get_done_directory();
22        self.maybe_move_input_to_done(&done_dir).await?;
23        self.maybe_move_error_to_done(&done_dir).await?;
24        self.maybe_move_metadata_to_done(&done_dir).await?;
25        Ok(())
26    }
27
28    pub async fn move_all_to_done(
29        &self,
30    ) -> Result<(), FileMoveError> {
31
32        let done_dir = self.get_done_directory();
33        self.maybe_move_input_to_done(&done_dir).await?;
34        self.maybe_move_output_to_done(&done_dir).await?;
35        self.maybe_move_error_to_done(&done_dir).await?;
36        self.maybe_move_metadata_to_done(&done_dir).await?;
37        Ok(())
38    }
39
40    //-----------------------------------------------------------
41    async fn maybe_move_input_to_done(
42        &self,
43        done_dir: impl AsRef<Path>,
44    ) -> Result<(), FileMoveError> {
45
46        // NEW: ensure done_dir exists before rename
47        tokio::fs::create_dir_all(done_dir.as_ref()).await.ok();
48
49        if let Some(input_path) = self.input() {
50            if !input_path.exists() {
51                if is_test_mode() {
52                    warn!(
53                        "Mock scenario (test-only): ignoring rename for missing input file at {:?}",
54                        input_path
55                    );
56                    return Ok(());
57                }
58            }
59            let dest = done_dir.as_ref().join(input_path.file_name().unwrap());
60            trace!("Renaming input_path: {:?} => {:?}", input_path, dest);
61            fs::rename(input_path, &dest).await?;
62            info!("moved batch input file to the done directory");
63        }
64        Ok(())
65    }
66
67    async fn maybe_move_output_to_done(
68        &self,
69        done_dir: impl AsRef<Path>,
70    ) -> Result<(), FileMoveError> {
71
72        // NEW: ensure done_dir exists before rename
73        tokio::fs::create_dir_all(done_dir.as_ref()).await.ok();
74
75        if let Some(output_path) = self.output() {
76            if !output_path.exists() {
77                if is_test_mode() {
78                    warn!(
79                        "Mock scenario (test-only): ignoring rename for missing output file at {:?}",
80                        output_path
81                    );
82                    return Ok(());
83                }
84            }
85            let dest = done_dir.as_ref().join(output_path.file_name().unwrap());
86            trace!("Renaming output_path: {:?} => {:?}", output_path, dest);
87            fs::rename(output_path, &dest).await?;
88            info!("moved batch output file to the done directory");
89        }
90        Ok(())
91    }
92
93    async fn maybe_move_error_to_done(
94        &self,
95        done_dir: impl AsRef<Path>,
96    ) -> Result<(), FileMoveError> {
97
98        // NEW: ensure done_dir exists before rename
99        tokio::fs::create_dir_all(done_dir.as_ref()).await.ok();
100
101        if let Some(error_path) = self.error() {
102            if !error_path.exists() {
103                if is_test_mode() {
104                    warn!(
105                        "Mock scenario (test-only): ignoring rename for missing error file at {:?}",
106                        error_path
107                    );
108                    return Ok(());
109                }
110            }
111            let dest = done_dir.as_ref().join(error_path.file_name().unwrap());
112            trace!("Renaming error_path: {:?} => {:?}", error_path, dest);
113            fs::rename(error_path, &dest).await?;
114            info!("moved batch error file to the done directory");
115        }
116        Ok(())
117    }
118
119    async fn maybe_move_metadata_to_done(
120        &self,
121        done_dir: impl AsRef<Path>,
122    ) -> Result<(), FileMoveError> {
123
124        // NEW: ensure done_dir exists before rename
125        tokio::fs::create_dir_all(done_dir.as_ref()).await.ok();
126
127        if let Some(metadata_path) = self.associated_metadata() {
128            if !metadata_path.exists() {
129                if is_test_mode() {
130                    warn!(
131                        "Mock scenario (test-only): ignoring rename for missing metadata file at {:?}",
132                        metadata_path
133                    );
134                    return Ok(());
135                }
136            }
137            let dest = done_dir.as_ref().join(metadata_path.file_name().unwrap());
138            trace!("Renaming metadata_path: {:?} => {:?}", metadata_path, dest);
139            fs::rename(metadata_path, &dest).await?;
140            info!("moved batch metadata file to the done directory");
141        }
142        Ok(())
143    }
144}
145
146#[cfg(test)]
147mod batch_file_triple_moving_files_exhaustive_tests {
148    use super::*;
149    use tempfile::{NamedTempFile, TempDir};
150    use std::io::Write;
151    use tokio::runtime::Runtime;
152    use tracing::*;
153
154    #[traced_test]
155    async fn move_input_and_output_to_done_moves_correct_files() {
156        trace!("===== BEGIN TEST: move_input_and_output_to_done_moves_correct_files =====");
157        info!("Starting test: move_input_and_output_to_done_moves_correct_files");
158
159        // Create a temp workspace that is fully sandboxed
160        let workspace = MockBatchWorkspace::default();
161        let batch_idx = BatchIndex::Usize(42);
162
163        // Write some fake files in the ephemeral workspace:
164        let input_path = workspace.input_filename(&batch_idx);
165        std::fs::write(&input_path, "fake input contents")
166            .expect("Failed to write to input file");
167
168        let output_path = workspace.output_filename(&batch_idx);
169        std::fs::write(&output_path, "fake output contents")
170            .expect("Failed to write to output file");
171
172        let metadata_path = workspace.metadata_filename(&batch_idx);
173        std::fs::write(&metadata_path, "fake metadata contents")
174            .expect("Failed to write to metadata file");
175
176        // Construct the triple referencing those ephemeral paths
177        let triple = BatchFileTriple::new_direct(
178            &batch_idx,
179            Some(input_path.clone()),
180            Some(output_path.clone()),
181            None,
182            Some(metadata_path.clone()),
183            Arc::new(workspace.clone()),
184        );
185
186        let result = triple.move_input_and_output_to_done().await;
187        debug!("Result of move_input_and_output_to_done: {:?}", result);
188        assert!(
189            result.is_ok(),
190            "Expected success moving input + output to done"
191        );
192
193        // Verify they are no longer in their original location:
194        assert!(!input_path.exists(), "Input file should have been moved away");
195        assert!(!output_path.exists(), "Output file should have been moved away");
196        assert!(
197            !metadata_path.exists(),
198            "Metadata file should have been moved away"
199        );
200
201        // Confirm they appear in the workspace's ephemeral done directory
202        let done_dir = workspace.get_done_directory();
203        trace!("Done directory is: {:?}", done_dir);
204
205        let done_input = done_dir.join(input_path.file_name().unwrap());
206        let done_output = done_dir.join(output_path.file_name().unwrap());
207        let done_metadata = done_dir.join(metadata_path.file_name().unwrap());
208
209        assert!(
210            done_input.exists(),
211            "Input file must be in done directory"
212        );
213        assert!(
214            done_output.exists(),
215            "Output file must be in done directory"
216        );
217        assert!(
218            done_metadata.exists(),
219            "Metadata file must be in done directory"
220        );
221
222        info!("Finished test: move_input_and_output_to_done_moves_correct_files");
223        trace!("===== END TEST: move_input_and_output_to_done_moves_correct_files =====");
224    }
225
226    #[traced_test]
227    async fn move_input_and_error_to_done_moves_correct_files() {
228        trace!("===== BEGIN TEST: move_input_and_error_to_done_moves_correct_files =====");
229        info!("Starting test: move_input_and_error_to_done_moves_correct_files");
230
231        let workspace = MockBatchWorkspace::default();
232        let batch_idx = BatchIndex::Usize(777);
233
234        let input_path = workspace.input_filename(&batch_idx);
235        std::fs::write(&input_path, "fake input contents")
236            .expect("Failed to write to input file");
237
238        let error_path = workspace.error_filename(&batch_idx);
239        std::fs::write(&error_path, "fake error contents")
240            .expect("Failed to write to error file");
241
242        let metadata_path = workspace.metadata_filename(&batch_idx);
243        std::fs::write(&metadata_path, "fake metadata contents")
244            .expect("Failed to write to metadata file");
245
246        let triple = BatchFileTriple::new_direct(
247            &batch_idx,
248            Some(input_path.clone()),
249            None,
250            Some(error_path.clone()),
251            Some(metadata_path.clone()),
252            Arc::new(workspace.clone()),
253        );
254
255        let result = triple.move_input_and_error_to_done().await;
256        debug!("Result of move_input_and_error_to_done: {:?}", result);
257        assert!(
258            result.is_ok(),
259            "Expected success moving input + error to done"
260        );
261
262        assert!(!input_path.exists(), "Input file should have been moved away");
263        assert!(!error_path.exists(), "Error file should have been moved away");
264        assert!(
265            !metadata_path.exists(),
266            "Metadata file should have been moved away"
267        );
268
269        let done_dir = workspace.get_done_directory();
270        trace!("Done directory is: {:?}", done_dir);
271
272        let done_input = done_dir.join(input_path.file_name().unwrap());
273        let done_error = done_dir.join(error_path.file_name().unwrap());
274        let done_metadata = done_dir.join(metadata_path.file_name().unwrap());
275
276        assert!(
277            done_input.exists(),
278            "Input file must be in done directory"
279        );
280        assert!(
281            done_error.exists(),
282            "Error file must be in done directory"
283        );
284        assert!(
285            done_metadata.exists(),
286            "Metadata file must be in done directory"
287        );
288
289        info!("Finished test: move_input_and_error_to_done_moves_correct_files");
290        trace!("===== END TEST: move_input_and_error_to_done_moves_correct_files =====");
291    }
292
293    #[traced_test]
294    async fn move_all_to_done_moves_input_output_error_and_metadata() {
295        trace!("===== BEGIN TEST: move_all_to_done_moves_input_output_error_and_metadata =====");
296        info!("Starting test: move_all_to_done_moves_input_output_error_and_metadata");
297
298        let workspace = MockBatchWorkspace::default();
299        let batch_idx = BatchIndex::Usize(5);
300
301        let input_path = workspace.input_filename(&batch_idx);
302        std::fs::write(&input_path, "some input content")
303            .expect("Failed to write to input file");
304
305        let output_path = workspace.output_filename(&batch_idx);
306        std::fs::write(&output_path, "some output content")
307            .expect("Failed to write to output file");
308
309        let error_path = workspace.error_filename(&batch_idx);
310        std::fs::write(&error_path, "some error content")
311            .expect("Failed to write to error file");
312
313        let metadata_path = workspace.metadata_filename(&batch_idx);
314        std::fs::write(&metadata_path, "some metadata content")
315            .expect("Failed to write to metadata file");
316
317        let triple = BatchFileTriple::new_direct(
318            &batch_idx,
319            Some(input_path.clone()),
320            Some(output_path.clone()),
321            Some(error_path.clone()),
322            Some(metadata_path.clone()),
323            Arc::new(workspace.clone()),
324        );
325
326        debug!("Constructed triple for test: {:?}", triple);
327
328        let result = triple.move_all_to_done().await;
329        debug!("Result of move_all_to_done: {:?}", result);
330        assert!(
331            result.is_ok(),
332            "Expected success moving all files to done"
333        );
334
335        assert!(!input_path.exists(), "Input file should be moved away");
336        assert!(!output_path.exists(), "Output file should be moved away");
337        assert!(!error_path.exists(), "Error file should be moved away");
338        assert!(
339            !metadata_path.exists(),
340            "Metadata file should be moved away"
341        );
342
343        let done_dir = workspace.get_done_directory();
344        trace!("Done directory is: {:?}", done_dir);
345
346        let done_input = done_dir.join(input_path.file_name().unwrap());
347        let done_output = done_dir.join(output_path.file_name().unwrap());
348        let done_error = done_dir.join(error_path.file_name().unwrap());
349        let done_metadata = done_dir.join(metadata_path.file_name().unwrap());
350
351        assert!(done_input.exists(), "Input must be in done directory now");
352        assert!(done_output.exists(), "Output must be in done directory now");
353        assert!(done_error.exists(), "Error must be in done directory now");
354        assert!(
355            done_metadata.exists(),
356            "Metadata must be in done directory now"
357        );
358
359        info!("Finished test: move_all_to_done_moves_input_output_error_and_metadata");
360        trace!("===== END TEST: move_all_to_done_moves_input_output_error_and_metadata =====");
361    }
362}