1use flow_core::{Feature, Task};
2
3pub fn feature_to_task(feature: &Feature, _session_id: &str) -> Task {
5 let status = if feature.passes {
6 "completed"
7 } else if feature.in_progress {
8 "in_progress"
9 } else {
10 "pending"
11 };
12
13 let active_form = if feature.in_progress {
14 format!("Working on: {}", feature.name)
15 } else {
16 String::new()
17 };
18
19 let blocked_by: Vec<String> = feature
21 .dependencies
22 .iter()
23 .map(std::string::ToString::to_string)
24 .collect();
25
26 Task {
27 id: feature.id.to_string(),
28 subject: feature.name.clone(),
29 description: feature.description.clone(),
30 active_form,
31 status: status.to_string(),
32 owner: "flow".to_string(),
33 blocks: vec![],
34 blocked_by,
35 }
36}
37
38pub fn task_to_feature(task: &Task) -> Feature {
41 let id = task.id.parse::<i64>().unwrap_or(0);
42
43 let passes = task.status == "completed";
44 let in_progress = task.status == "in_progress";
45
46 let dependencies: Vec<i64> = task
48 .blocked_by
49 .iter()
50 .filter_map(|s| s.parse::<i64>().ok())
51 .collect();
52
53 Feature {
54 id,
55 priority: 0, category: String::new(), name: task.subject.clone(),
58 description: task.description.clone(),
59 steps: vec![],
60 passes,
61 in_progress,
62 dependencies,
63 created_at: None,
64 updated_at: None,
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_feature_to_task() {
74 let feature = Feature {
75 id: 42,
76 priority: 1,
77 category: "Backend".to_string(),
78 name: "Implement Auth".to_string(),
79 description: "Add OAuth support".to_string(),
80 steps: vec!["Step 1".to_string()],
81 passes: false,
82 in_progress: true,
83 dependencies: vec![10, 20],
84 created_at: Some("2025-01-01".to_string()),
85 updated_at: Some("2025-01-02".to_string()),
86 };
87
88 let task = feature_to_task(&feature, "session-123");
89
90 assert_eq!(task.id, "42");
91 assert_eq!(task.subject, "Implement Auth");
92 assert_eq!(task.description, "Add OAuth support");
93 assert_eq!(task.status, "in_progress");
94 assert!(task.active_form.contains("Working on"));
95 assert_eq!(task.blocked_by, vec!["10", "20"]);
96 assert_eq!(task.owner, "flow");
97 }
98
99 #[test]
100 fn test_feature_to_task_completed() {
101 let feature = Feature {
102 id: 1,
103 priority: 1,
104 category: "".to_string(),
105 name: "Done".to_string(),
106 description: "".to_string(),
107 steps: vec![],
108 passes: true,
109 in_progress: false,
110 dependencies: vec![],
111 created_at: None,
112 updated_at: None,
113 };
114
115 let task = feature_to_task(&feature, "session");
116 assert_eq!(task.status, "completed");
117 assert_eq!(task.active_form, "");
118 }
119
120 #[test]
121 fn test_feature_to_task_pending() {
122 let feature = Feature {
123 id: 2,
124 priority: 1,
125 category: "".to_string(),
126 name: "Pending".to_string(),
127 description: "".to_string(),
128 steps: vec![],
129 passes: false,
130 in_progress: false,
131 dependencies: vec![],
132 created_at: None,
133 updated_at: None,
134 };
135
136 let task = feature_to_task(&feature, "session");
137 assert_eq!(task.status, "pending");
138 }
139
140 #[test]
141 fn test_task_to_feature() {
142 let task = Task {
143 id: "123".to_string(),
144 subject: "Build API".to_string(),
145 description: "REST API".to_string(),
146 active_form: "Working...".to_string(),
147 status: "in_progress".to_string(),
148 owner: "agent".to_string(),
149 blocks: vec![],
150 blocked_by: vec!["10".to_string(), "20".to_string()],
151 };
152
153 let feature = task_to_feature(&task);
154
155 assert_eq!(feature.id, 123);
156 assert_eq!(feature.name, "Build API");
157 assert_eq!(feature.description, "REST API");
158 assert!(feature.in_progress);
159 assert!(!feature.passes);
160 assert_eq!(feature.dependencies, vec![10, 20]);
161 }
162
163 #[test]
164 fn test_task_to_feature_completed() {
165 let task = Task {
166 id: "1".to_string(),
167 subject: "Done".to_string(),
168 description: "".to_string(),
169 active_form: "".to_string(),
170 status: "completed".to_string(),
171 owner: "".to_string(),
172 blocks: vec![],
173 blocked_by: vec![],
174 };
175
176 let feature = task_to_feature(&task);
177 assert!(feature.passes);
178 assert!(!feature.in_progress);
179 }
180
181 #[test]
182 fn test_roundtrip_conversion() {
183 let original_feature = Feature {
184 id: 5,
185 priority: 3,
186 category: "UI".to_string(),
187 name: "Dashboard".to_string(),
188 description: "User dashboard".to_string(),
189 steps: vec![],
190 passes: false,
191 in_progress: true,
192 dependencies: vec![1, 2],
193 created_at: None,
194 updated_at: None,
195 };
196
197 let task = feature_to_task(&original_feature, "session");
198 let converted_feature = task_to_feature(&task);
199
200 assert_eq!(converted_feature.id, original_feature.id);
202 assert_eq!(converted_feature.name, original_feature.name);
203 assert_eq!(converted_feature.description, original_feature.description);
204 assert_eq!(converted_feature.passes, original_feature.passes);
205 assert_eq!(converted_feature.in_progress, original_feature.in_progress);
206 assert_eq!(converted_feature.dependencies, original_feature.dependencies);
207
208 assert_eq!(converted_feature.priority, 0);
210 assert_eq!(converted_feature.category, "");
211 }
212}