An AI agent built to do Ralph loops - plan mode for planning and ralph mode for implementing.
1use chrono::Utc;
2use rustagent::spec::{Spec, Task, TaskStatus};
3use tempfile::TempDir;
4
5#[test]
6fn test_spec_serialization() {
7 let spec = Spec {
8 name: "test-feature".to_string(),
9 description: "A test feature".to_string(),
10 branch_name: "feature/test".to_string(),
11 created_at: Utc::now(),
12 tasks: vec![Task {
13 id: "task-1".to_string(),
14 title: "Implement X".to_string(),
15 description: "Description".to_string(),
16 acceptance_criteria: vec!["Criterion 1".to_string()],
17 status: TaskStatus::Pending,
18 blocked_reason: None,
19 completed_at: None,
20 }],
21 learnings: vec![],
22 };
23
24 let json = serde_json::to_string_pretty(&spec).unwrap();
25 assert!(json.contains("test-feature"));
26 assert!(json.contains("pending"));
27}
28
29#[test]
30fn test_spec_save_and_load() {
31 let temp = TempDir::new().unwrap();
32 let spec_path = temp.path().join("test.json");
33
34 let spec = Spec {
35 name: "test".to_string(),
36 description: "Test".to_string(),
37 branch_name: "feature/test".to_string(),
38 created_at: Utc::now(),
39 tasks: vec![],
40 learnings: vec![],
41 };
42
43 spec.save(&spec_path).unwrap();
44 let loaded = Spec::load(&spec_path).unwrap();
45
46 assert_eq!(loaded.name, "test");
47}
48
49#[test]
50fn test_spec_uses_datetime_types() {
51 let now = Utc::now();
52 let spec = Spec {
53 name: "test".to_string(),
54 description: "test spec".to_string(),
55 branch_name: "feature/test".to_string(),
56 created_at: now,
57 tasks: vec![],
58 learnings: vec![],
59 };
60
61 // Should serialize to RFC3339 format
62 let json = serde_json::to_string(&spec).unwrap();
63 // Chrono serializes with 'Z' suffix, which is valid RFC3339
64 assert!(json.contains(&format!("{}Z", now.format("%Y-%m-%dT%H:%M:%S%.f"))));
65}
66
67#[test]
68fn test_task_completion_timestamp() {
69 let completed = Utc::now();
70 let task = Task {
71 id: "task-1".to_string(),
72 title: "Test".to_string(),
73 description: "desc".to_string(),
74 acceptance_criteria: vec![],
75 status: TaskStatus::Complete,
76 blocked_reason: None,
77 completed_at: Some(completed),
78 };
79
80 let json = serde_json::to_string(&task).unwrap();
81 // Chrono serializes with 'Z' suffix, which is valid RFC3339
82 assert!(json.contains(&format!("{}Z", completed.format("%Y-%m-%dT%H:%M:%S%.f"))));
83}
84
85#[test]
86fn test_spec_save_creates_parent_directories() {
87 let dir = tempfile::tempdir().unwrap();
88 let nested_path = dir
89 .path()
90 .join("level1")
91 .join("level2")
92 .join("level3")
93 .join("spec.json");
94
95 let spec = Spec {
96 name: "test".to_string(),
97 description: "test spec".to_string(),
98 branch_name: "feature/test".to_string(),
99 created_at: Utc::now(),
100 tasks: vec![],
101 learnings: vec![],
102 };
103
104 // Should create all parent directories
105 spec.save(&nested_path).unwrap();
106 assert!(nested_path.exists());
107
108 // Should be able to load it back
109 let loaded = Spec::load(&nested_path).unwrap();
110 assert_eq!(loaded.name, "test");
111}