batch_mode_batch_workspace_interface/
mock.rs

1// ---------------- [ File: batch-mode-batch-workspace-interface/src/mock.rs ]
2crate::ix!();
3
4/// A minimal item that could implement `GetTargetPathForAIExpansion`.
5/// We'll just call it `MockItem` for demonstration.
6#[derive(NamedItem,Debug, Clone)]
7pub struct MockItem {
8    pub name: String,
9}
10
11#[derive(Clone,Getters,Setters,Builder,Debug)]
12#[builder(setter(strip_option))]
13#[getset(get = "pub", set = "pub")]
14pub struct MockBatchWorkspace {
15    /// A temporary directory that is automatically
16    /// cleaned up when `MockBatchWorkspace` is dropped.
17    #[builder(default = "Arc::new(tempfile::tempdir().expect(\"Failed to create temp directory\"))")]
18    ephemeral_dir: Arc<tempfile::TempDir>,
19
20    /// The original user‐supplied subpath for “done” items. 
21    /// We now treat this as a relative subdirectory within `ephemeral_dir`.
22    #[builder(default = "PathBuf::from(\"mock_done_dir\")")]
23    done_dir: PathBuf,
24
25    #[builder(default = "PathBuf::from(\"mock_failed_json_repairs_dir\")")]
26    failed_json_repairs_dir: PathBuf,
27
28    #[builder(default = "PathBuf::from(\"mock_failed_items_dir\")")]
29    failed_items_dir: PathBuf,
30
31    #[builder(default = "PathBuf::from(\"mock_workdir\")")]
32    workdir: PathBuf,
33
34    #[builder(default = "\"text_storage_prefix\".to_string()")]
35    text_storage_prefix: String,
36
37    #[builder(default)]
38    input_ids: Vec<String>,
39
40    #[builder(default)]
41    output_ids: Vec<String>,
42
43    #[builder(default)]
44    error_ids: Vec<String>,
45
46    /// We must store a concrete `PathBuf` for the “done directory” specifically,
47    /// because its trait method returns `&PathBuf`, not `PathBuf`.
48    /// All other paths are returned by value, so they can be constructed on the fly.
49    #[builder(default = "PathBuf::new()")]
50    ephemeral_done_dir: PathBuf,
51}
52
53#[async_trait]
54impl LoadSeedByCustomId for MockBatchWorkspace {
55    async fn load_seed_by_custom_id(
56        &self,
57        custom_id: &CustomRequestId,
58    ) -> Result<Box<dyn Named + Send + Sync>, BatchWorkspaceError> {
59        // In tests we fabricate a dummy that satisfies Named
60        #[derive(Debug)]
61        struct Dummy(String);
62        impl Named for Dummy {
63            fn name(&self) -> std::borrow::Cow<'_, str> {
64                std::borrow::Cow::Borrowed(&self.0)
65            }
66        }
67        Ok(Box::new(Dummy(custom_id.as_str().to_owned())))
68    }
69}
70
71impl Default for MockBatchWorkspace {
72    fn default() -> Self {
73        let temp = tempfile::tempdir().expect("Could not create temp directory for MockBatchWorkspace");
74        info!("Created ephemeral directory for MockBatchWorkspace at: {:?}", temp.path());
75
76        // Pre‐compute the ephemeral “done” directory
77        // so we can return it by reference in the trait method.
78        let done_dir_path = temp.path().join("mock_done_dir");
79        if let Err(e) = std::fs::create_dir_all(&done_dir_path) {
80            warn!("Could not create ephemeral done directory: {} — continuing anyway", e);
81        }
82
83        Self {
84            ephemeral_dir: temp.into(),
85            done_dir: PathBuf::from("mock_done_dir"),
86            failed_json_repairs_dir: PathBuf::from("mock_failed_json_repairs_dir"),
87            failed_items_dir: PathBuf::from("mock_failed_items_dir"),
88            workdir: PathBuf::from("mock_workdir"),
89            text_storage_prefix: "text_storage_prefix".to_string(),
90            input_ids: vec![],
91            output_ids: vec![],
92            error_ids: vec![],
93            ephemeral_done_dir: done_dir_path,
94        }
95    }
96}
97
98impl GetInputFilenameAtIndex for MockBatchWorkspace {
99    fn input_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
100        let path = self
101            .ephemeral_dir
102            .path()
103            .join(format!("mock_input_{}.json", batch_idx));
104        trace!("Returning ephemeral input filename for batch {:?}: {:?}", batch_idx, path);
105        path
106    }
107}
108
109impl GetOutputFilenameAtIndex for MockBatchWorkspace {
110    fn output_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
111        let path = self
112            .ephemeral_dir
113            .path()
114            .join(format!("mock_output_{}.json", batch_idx));
115        trace!("Returning ephemeral output filename for batch {:?}: {:?}", batch_idx, path);
116        path
117    }
118}
119
120impl GetErrorFilenameAtIndex for MockBatchWorkspace {
121    fn error_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
122        let path = self
123            .ephemeral_dir
124            .path()
125            .join(format!("mock_error_{}.json", batch_idx));
126        trace!("Returning ephemeral error filename for batch {:?}: {:?}", batch_idx, path);
127        path
128    }
129}
130
131impl GetMetadataFilenameAtIndex for MockBatchWorkspace {
132    fn metadata_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
133        let path = self
134            .ephemeral_dir
135            .path()
136            .join(format!("mock_metadata_{}.json", batch_idx));
137        trace!("Returning ephemeral metadata filename for batch {:?}: {:?}", batch_idx, path);
138        path
139    }
140}
141
142impl GetSeedManifestFilenameAtIndex for MockBatchWorkspace {
143    fn seed_manifest_filename(&self, batch_idx: &BatchIndex) -> PathBuf {
144        let path = self
145            .ephemeral_dir
146            .path()
147            .join(format!("mock_seed_manifest_{}.json", batch_idx));
148        trace!("Returning ephemeral seed_manifest filename for batch {:?}: {:?}", batch_idx, path);
149        path
150    }
151}
152
153impl GetDoneDirectory for MockBatchWorkspace {
154    fn get_done_directory(&self) -> &PathBuf {
155        trace!(
156            "Returning ephemeral done directory for mock workspace: {:?}",
157            self.ephemeral_done_dir
158        );
159        &self.ephemeral_done_dir
160    }
161}
162
163impl GetFailedJsonRepairsDir for MockBatchWorkspace {
164    fn failed_json_repairs_dir(&self) -> PathBuf {
165        let path = self.ephemeral_dir.path().join(&self.failed_json_repairs_dir);
166        trace!("Returning ephemeral failed_json_repairs_dir: {:?}", path);
167        if let Err(e) = std::fs::create_dir_all(&path) {
168            warn!("Could not create ephemeral failed_json_repairs_dir: {} — continuing anyway", e);
169        }
170        path
171    }
172}
173
174impl GetFailedItemsDir for MockBatchWorkspace {
175    fn failed_items_dir(&self) -> PathBuf {
176        let path = self.ephemeral_dir.path().join(&self.failed_items_dir);
177        trace!("Returning ephemeral failed_items_dir: {:?}", path);
178        if let Err(e) = std::fs::create_dir_all(&path) {
179            warn!("Could not create ephemeral failed_items_dir: {} — continuing anyway", e);
180        }
181        path
182    }
183}
184
185impl GetTextStoragePath for MockBatchWorkspace {
186    fn text_storage_path(&self, batch_idx: &BatchIndex) -> PathBuf {
187        let path = if self.text_storage_prefix.is_empty() {
188            self.ephemeral_dir
189                .path()
190                .join(format!("text_storage_{}.txt", batch_idx))
191        } else {
192            self.ephemeral_dir
193                .path()
194                .join(&self.text_storage_prefix)
195                .join(format!("text_storage_{}.txt", batch_idx))
196        };
197        trace!("Returning ephemeral text storage path for batch {:?}: {:?}", batch_idx, path);
198        if let Some(parent) = path.parent() {
199            if let Err(e) = std::fs::create_dir_all(parent) {
200                warn!("Could not create parent dir for text storage: {} — continuing anyway", e);
201            }
202        }
203        path
204    }
205}
206
207impl GetWorkdir for MockBatchWorkspace {
208    fn workdir(&self) -> PathBuf {
209        let path = self.ephemeral_dir.path().join(&self.workdir);
210        trace!("Returning ephemeral workdir: {:?}", path);
211        if let Err(e) = std::fs::create_dir_all(&path) {
212            warn!("Could not create ephemeral workdir: {} — continuing anyway", e);
213        }
214        path
215    }
216}
217
218impl GetTargetPath for MockBatchWorkspace {
219    type Item = Arc<dyn GetTargetPathForAIExpansion + Send + Sync + 'static>;
220
221    fn target_path(
222        &self,
223        item: &Self::Item,
224        expected_content_type: &ExpectedContentType
225    ) -> PathBuf {
226        let subdir = match expected_content_type {
227            ExpectedContentType::Json      => "json_output",
228            ExpectedContentType::PlainText => "text_output",
229            ExpectedContentType::JsonLines => "json_lines_output",
230        };
231        let base = self.workdir().join(subdir);
232        trace!("Constructing ephemeral target path for subdir {:?} and item", base);
233        item.target_path_for_ai_json_expansion(base.as_path(), expected_content_type)
234    }
235}
236
237#[async_trait]
238impl BatchWorkspaceInterface for MockBatchWorkspace {}
239
240impl GetTargetDir for MockBatchWorkspace {
241
242    fn get_target_dir(&self) -> PathBuf {
243        todo!()
244    }
245}
246
247#[cfg(test)]
248mod test_mock_workspace_ephemeral {
249    use super::*;
250
251    #[traced_test]
252    fn test_ephemeral_cleanup() {
253        let ephemeral_done: PathBuf;
254        {
255            let workspace = MockBatchWorkspace::default();
256            ephemeral_done = workspace.get_done_directory().to_path_buf();
257            assert!(
258                !ephemeral_done.as_os_str().is_empty(),
259                "Ephemeral done directory path should not be empty."
260            );
261            // Make sure it exists
262            assert!(
263                ephemeral_done.exists(),
264                "Expected ephemeral directory to exist."
265            );
266        }
267        // After going out of scope, the temp dir should be destroyed.
268        // The done directory path should no longer exist.
269        assert!(
270            !ephemeral_done.exists(),
271            "Ephemeral directory should have been cleaned up."
272        );
273    }
274
275    #[traced_test]
276    fn test_mock_workspace_interface() {
277        let w = MockBatchWorkspace::default();
278        // We only test to the interface:
279        let done = w.get_done_directory();
280        let fail_json = w.failed_json_repairs_dir();
281        let fail_items = w.failed_items_dir();
282        let input = w.input_filename(&BatchIndex::from(0));
283        let output = w.output_filename(&BatchIndex::from(1));
284        let error = w.error_filename(&BatchIndex::from(2));
285        let meta = w.metadata_filename(&BatchIndex::from(3));
286        let text = w.text_storage_path(&BatchIndex::from(4));
287        let wd = w.workdir();
288
289        assert!(!done.as_os_str().is_empty());
290        assert!(!fail_json.as_os_str().is_empty());
291        assert!(!fail_items.as_os_str().is_empty());
292        assert!(!input.as_os_str().is_empty());
293        assert!(!output.as_os_str().is_empty());
294        assert!(!error.as_os_str().is_empty());
295        assert!(!meta.as_os_str().is_empty());
296        assert!(!text.as_os_str().is_empty());
297        assert!(!wd.as_os_str().is_empty());
298    }
299}