batch_mode_batch_reconciliation/
batch_file_reconciliation_recommended_course_of_action.rs1crate::ix!();
3
4#[derive(Debug,Clone,PartialEq,Eq)]
5pub struct BatchFileReconciliationRecommendedCourseOfAction {
6 steps: Vec<BatchFileTripleReconciliationOperation>,
7}
8
9impl BatchFileReconciliationRecommendedCourseOfAction {
10 pub fn steps(&self) -> &[BatchFileTripleReconciliationOperation] {
11 &self.steps
12 }
13}
14
15impl From<Vec<BatchFileTripleReconciliationOperation>> for BatchFileReconciliationRecommendedCourseOfAction {
16
17 fn from(steps: Vec<BatchFileTripleReconciliationOperation>) -> Self {
18 Self { steps }
19 }
20}
21
22impl TryFrom<&BatchFileTriple> for BatchFileReconciliationRecommendedCourseOfAction {
23
24 type Error = BatchReconciliationError;
25
26 fn try_from(triple: &BatchFileTriple) -> Result<BatchFileReconciliationRecommendedCourseOfAction, BatchReconciliationError> {
32 if triple.all_are_none() {
33 return Err(BatchWorkspaceError::NoBatchFileTripleAtIndex {
34 index: triple.index().clone(),
35 }.into());
36 }
37
38 if triple.input().is_none() {
39 return Err(BatchReconciliationError::MissingBatchInputFileButOthersExist {
40 index: triple.index().clone(),
41 output: triple.output().clone(),
42 error: triple.error().clone(),
43 });
44 }
45
46 use BatchFileTripleReconciliationOperation::*;
47 let steps = match BatchFileState::from(triple) {
48 BatchFileState::InputOutputError => {
49 warn!("Both output and error files are present for batch {:?}", triple.index());
50 vec![
51 EnsureInputRequestIdsMatchOutputRequestIdsCombinedWithErrorRequestIds,
52 ProcessBatchErrorFile,
53 ProcessBatchOutputFile,
54 MoveBatchTripleToTheDoneDirectory,
55 ]
56 }
57 BatchFileState::InputOutput => {
58 vec![
59 EnsureInputRequestIdsMatchOutputRequestIds,
60 ProcessBatchOutputFile,
61 MoveBatchInputAndOutputToTheDoneDirectory,
62 ]
63 }
64 BatchFileState::InputError => {
65 warn!("Error file present but no output file for batch {:?}", triple.index());
66 vec![
67 EnsureInputRequestIdsMatchErrorRequestIds,
68 ProcessBatchErrorFile,
69 MoveBatchInputAndErrorToTheDoneDirectory,
70 ]
71
72 }
73 BatchFileState::InputOnly => {
74 warn!("Neither output nor error files are present for batch {:?}", triple.index());
75 vec![
76 CheckForBatchOutputAndErrorFileOnline,
77 RecalculateRecommendedCourseOfActionIfTripleChanged,
78 ]
79 }
80 };
81
82 Ok(BatchFileReconciliationRecommendedCourseOfAction::from(steps))
83 }
84}
85
86#[cfg(test)]
87mod batch_file_reconciliation_recommended_course_of_action_tests {
88 use super::*;
89
90 #[traced_test]
91 async fn test_try_from_triple_all_none() {
92
93 let mock_index = BatchIndex::from(123u64);
94
95 let workspace = BatchWorkspace::new_temp().await.expect("expected to get our workspace") as Arc<dyn BatchWorkspaceInterface>;
96
97 let triple = BatchFileTripleBuilder::default()
98 .index(mock_index.clone())
99 .workspace(workspace)
100 .build()
101 .unwrap();
102
103 let result = BatchFileReconciliationRecommendedCourseOfAction::try_from(&triple);
104 assert!(result.is_err(), "Expected error if all files are None");
105 match result {
106 Err(BatchReconciliationError::BatchWorkspaceError(e)) => {
107 match e {
108 BatchWorkspaceError::NoBatchFileTripleAtIndex { index } => {
109 pretty_assert_eq!(index, mock_index);
110 }
111 _ => panic!("Unexpected error variant for all_none scenario"),
112 }
113 }
114 other => panic!("Unexpected result: {:?}", other),
115 }
116 }
117
118 #[traced_test]
119 async fn test_try_from_triple_missing_input_but_has_output() {
120
121 let workspace = BatchWorkspace::new_temp().await.expect("expected to get our workspace") as Arc<dyn BatchWorkspaceInterface>;
122
123 let triple = BatchFileTripleBuilder::default()
124 .index(BatchIndex::from(9999u64))
125 .output::<PathBuf>("some_output.json".into())
126 .workspace(workspace)
127 .build()
128 .unwrap();
129
130 let result = BatchFileReconciliationRecommendedCourseOfAction::try_from(&triple);
131 assert!(result.is_err(), "Should fail if input is missing but output exists");
132 match result {
133 Err(BatchReconciliationError::MissingBatchInputFileButOthersExist { index, output, error }) => {
134 pretty_assert_eq!(index.as_u64(), Some(9999u64));
135 pretty_assert_eq!(output, Some("some_output.json".into()));
136 pretty_assert_eq!(error, None);
137 }
138 other => panic!("Unexpected error variant for missing input scenario: {:?}", other),
139 }
140 }
141
142 #[traced_test]
143 async fn test_try_from_triple_input_only() {
144
145 let workspace = BatchWorkspace::new_temp().await.expect("expected to get our workspace") as Arc<dyn BatchWorkspaceInterface>;
146
147 let triple = BatchFileTripleBuilder::default()
148 .index(BatchIndex::from(1000u64))
149 .input::<PathBuf>("input.json".into())
150 .workspace(workspace)
151 .build()
152 .unwrap();
153
154 let result = BatchFileReconciliationRecommendedCourseOfAction::try_from(&triple);
155 assert!(result.is_ok(), "Input-only scenario should be Ok");
156 let steps = result.unwrap().steps().to_vec();
157 pretty_assert_eq!(
159 steps,
160 vec![
161 BatchFileTripleReconciliationOperation::CheckForBatchOutputAndErrorFileOnline,
162 BatchFileTripleReconciliationOperation::RecalculateRecommendedCourseOfActionIfTripleChanged
163 ]
164 );
165 }
166
167 #[traced_test]
168 async fn test_try_from_triple_input_output() {
169
170 let workspace = BatchWorkspace::new_temp().await.expect("expected to get our workspace") as Arc<dyn BatchWorkspaceInterface>;
171
172 let triple = BatchFileTripleBuilder::default()
173 .index(BatchIndex::from(1u64))
174 .input::<PathBuf>("input.json".into())
175 .output::<PathBuf>("output.json".into())
176 .workspace(workspace)
177 .build()
178 .unwrap();
179
180
181 let result = BatchFileReconciliationRecommendedCourseOfAction::try_from(&triple);
182 assert!(result.is_ok(), "Input+Output scenario should be Ok");
183 let steps = result.unwrap().steps().to_vec();
184 pretty_assert_eq!(
186 steps,
187 vec![
188 BatchFileTripleReconciliationOperation::EnsureInputRequestIdsMatchOutputRequestIds,
189 BatchFileTripleReconciliationOperation::ProcessBatchOutputFile,
190 BatchFileTripleReconciliationOperation::MoveBatchInputAndOutputToTheDoneDirectory
191 ]
192 );
193 }
194
195 #[traced_test]
196 async fn test_try_from_triple_input_error() {
197
198 let workspace = BatchWorkspace::new_temp().await.expect("expected to get our workspace") as Arc<dyn BatchWorkspaceInterface>;
199
200 let triple = BatchFileTripleBuilder::default()
201 .index(BatchIndex::from(2u64))
202 .input::<PathBuf>("input.json".into())
203 .error::<PathBuf>("error.json".into())
204 .workspace(workspace)
205 .build()
206 .unwrap();
207
208 let result = BatchFileReconciliationRecommendedCourseOfAction::try_from(&triple);
209 assert!(result.is_ok(), "Input+Error scenario should be Ok");
210 let steps = result.unwrap().steps().to_vec();
211 pretty_assert_eq!(
213 steps,
214 vec![
215 BatchFileTripleReconciliationOperation::EnsureInputRequestIdsMatchErrorRequestIds,
216 BatchFileTripleReconciliationOperation::ProcessBatchErrorFile,
217 BatchFileTripleReconciliationOperation::MoveBatchInputAndErrorToTheDoneDirectory
218 ]
219 );
220 }
221
222 #[traced_test]
223 async fn test_try_from_triple_input_output_error() {
224
225 let workspace = BatchWorkspace::new_temp().await.expect("expected to get our workspace") as Arc<dyn BatchWorkspaceInterface>;
226
227 let triple = BatchFileTripleBuilder::default()
228 .index(BatchIndex::from(3u64))
229 .input::<PathBuf>("input.json".into())
230 .output::<PathBuf>("output.json".into())
231 .error::<PathBuf>("error.json".into())
232 .workspace(workspace)
233 .build()
234 .unwrap();
235
236 let result = BatchFileReconciliationRecommendedCourseOfAction::try_from(&triple);
237 assert!(result.is_ok(), "Input+Output+Error scenario should be Ok");
238 let steps = result.unwrap().steps().to_vec();
239 pretty_assert_eq!(
241 steps,
242 vec![
243 BatchFileTripleReconciliationOperation::EnsureInputRequestIdsMatchOutputRequestIdsCombinedWithErrorRequestIds,
244 BatchFileTripleReconciliationOperation::ProcessBatchErrorFile,
245 BatchFileTripleReconciliationOperation::ProcessBatchOutputFile,
246 BatchFileTripleReconciliationOperation::MoveBatchTripleToTheDoneDirectory
247 ]
248 );
249 }
250}