Skip to content

Commit 1712983

Browse files
committed
Add comprehensive test coverage for config, protocol, and git modules
Add 628 lines of tests without changing any production code. Test coverage added: - Config module (autogit-shared): 9 new tests - Default values and serialization - Partial configurations and TOML parsing - Multiple repositories and various intervals - Custom message templates - Edge cases (empty repos, disabled auto-commit) - Protocol module (autogit-shared): 28 new tests - Command serialization (Trigger, Status, Ping) - Response serialization (Ok, Error, with/without data) - ResponseData variants (Trigger, Status) - RepoDetail with success, failure, and no-changes states - Error handling (invalid JSON, unknown commands) - Round-trip serialization - Edge cases (whitespace, newlines, empty details) - Git module (autogit-daemon): 18 new tests - Commit message template formatting - Placeholder replacement ({timestamp}, {date}, {time}) - Format validation (date/time/timestamp patterns) - Edge cases (empty, no placeholders, special chars) - Unicode and multiline support - Whitespace preservation - Realistic template scenarios Total test count: 52 tests (was 4, now 52) - autogit: 0 tests - autogit-daemon: 20 tests (was 2, added 18) - autogit-shared: 32 tests (was 2, added 30) All tests pass without any production code changes.
1 parent b2a5f85 commit 1712983

3 files changed

Lines changed: 628 additions & 0 deletions

File tree

autogit-daemon/src/git.rs

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,4 +413,203 @@ mod tests {
413413
assert!(message.contains("Changes on "));
414414
assert!(message.contains(" at "));
415415
}
416+
417+
#[test]
418+
fn test_format_timestamp_placeholder() {
419+
let template = "{timestamp}";
420+
let message = format_commit_message(template);
421+
422+
// Should match format: YYYY-MM-DD HH:MM:SS
423+
assert!(message.len() == 19); // "2025-11-15 20:30:45" format
424+
assert!(message.contains('-'));
425+
assert!(message.contains(':'));
426+
assert!(message.contains(' '));
427+
}
428+
429+
#[test]
430+
fn test_format_date_placeholder() {
431+
let template = "{date}";
432+
let message = format_commit_message(template);
433+
434+
// Should match format: YYYY-MM-DD
435+
assert!(message.len() == 10);
436+
assert_eq!(message.chars().filter(|&c| c == '-').count(), 2);
437+
}
438+
439+
#[test]
440+
fn test_format_time_placeholder() {
441+
let template = "{time}";
442+
let message = format_commit_message(template);
443+
444+
// Should match format: HH:MM:SS
445+
assert!(message.len() == 8);
446+
assert_eq!(message.chars().filter(|&c| c == ':').count(), 2);
447+
}
448+
449+
#[test]
450+
fn test_format_no_placeholders() {
451+
let template = "Simple commit message";
452+
let message = format_commit_message(template);
453+
assert_eq!(message, template);
454+
}
455+
456+
#[test]
457+
fn test_format_multiple_same_placeholder() {
458+
let template = "{date} - {date}";
459+
let message = format_commit_message(template);
460+
461+
let parts: Vec<&str> = message.split(" - ").collect();
462+
assert_eq!(parts.len(), 2);
463+
assert_eq!(parts[0], parts[1]); // Should be same date
464+
}
465+
466+
#[test]
467+
fn test_format_all_placeholders() {
468+
let template = "ts:{timestamp} d:{date} t:{time}";
469+
let message = format_commit_message(template);
470+
471+
assert!(message.starts_with("ts:"));
472+
assert!(message.contains(" d:"));
473+
assert!(message.contains(" t:"));
474+
}
475+
476+
#[test]
477+
fn test_format_empty_template() {
478+
let template = "";
479+
let message = format_commit_message(template);
480+
assert_eq!(message, "");
481+
}
482+
483+
#[test]
484+
fn test_format_special_characters() {
485+
let template = "Commit @{timestamp}!";
486+
let message = format_commit_message(template);
487+
assert!(message.starts_with("Commit @"));
488+
assert!(message.ends_with('!'));
489+
}
490+
491+
#[test]
492+
fn test_format_unicode() {
493+
let template = "✓ Update {date}";
494+
let message = format_commit_message(template);
495+
assert!(message.starts_with("✓ Update "));
496+
}
497+
498+
#[test]
499+
fn test_format_multiline() {
500+
let template = "First line\nDate: {date}\nTime: {time}";
501+
let message = format_commit_message(template);
502+
assert!(message.contains("First line\n"));
503+
assert!(message.contains("Date: "));
504+
assert!(message.contains("Time: "));
505+
}
506+
507+
#[test]
508+
fn test_format_with_braces() {
509+
let template = "Update {{not a placeholder}} {date}";
510+
let message = format_commit_message(template);
511+
// Double braces are not placeholders
512+
assert!(message.contains("{{not a placeholder}}"));
513+
}
514+
515+
#[test]
516+
fn test_format_partial_placeholder() {
517+
let template = "{dat} {timestamps}";
518+
let message = format_commit_message(template);
519+
// Partial matches should not be replaced
520+
assert_eq!(message, "{dat} {timestamps}");
521+
}
522+
523+
#[test]
524+
fn test_format_consistency() {
525+
let template = "{timestamp}";
526+
let message1 = format_commit_message(template);
527+
528+
// Wait a tiny bit to ensure time might change
529+
std::thread::sleep(std::time::Duration::from_millis(1));
530+
531+
let message2 = format_commit_message(template);
532+
533+
// Messages should be close in time (format is stable)
534+
assert!(message1.len() == message2.len());
535+
}
536+
537+
#[test]
538+
fn test_format_realistic_templates() {
539+
let templates = vec![
540+
"Auto-commit: {timestamp}",
541+
"Journal update: {date}",
542+
"Daily backup {time}",
543+
"Checkpoint at {date} {time}",
544+
"WIP",
545+
"Update documentation",
546+
"{date}: Work in progress",
547+
];
548+
549+
for template in templates {
550+
let message = format_commit_message(template);
551+
assert!(!message.is_empty());
552+
553+
// If template had placeholders, message should be different
554+
if template.contains('{') {
555+
assert_ne!(message, template);
556+
} else {
557+
assert_eq!(message, template);
558+
}
559+
}
560+
}
561+
562+
#[test]
563+
fn test_format_date_format() {
564+
let template = "{date}";
565+
let message = format_commit_message(template);
566+
567+
// Verify it's a valid date format YYYY-MM-DD
568+
let parts: Vec<&str> = message.split('-').collect();
569+
assert_eq!(parts.len(), 3);
570+
assert_eq!(parts[0].len(), 4); // Year
571+
assert_eq!(parts[1].len(), 2); // Month
572+
assert_eq!(parts[2].len(), 2); // Day
573+
}
574+
575+
#[test]
576+
fn test_format_time_format() {
577+
let template = "{time}";
578+
let message = format_commit_message(template);
579+
580+
// Verify it's a valid time format HH:MM:SS
581+
let parts: Vec<&str> = message.split(':').collect();
582+
assert_eq!(parts.len(), 3);
583+
assert_eq!(parts[0].len(), 2); // Hour
584+
assert_eq!(parts[1].len(), 2); // Minute
585+
assert_eq!(parts[2].len(), 2); // Second
586+
}
587+
588+
#[test]
589+
fn test_format_timestamp_format() {
590+
let template = "{timestamp}";
591+
let message = format_commit_message(template);
592+
593+
// Verify format: YYYY-MM-DD HH:MM:SS
594+
let parts: Vec<&str> = message.split(' ').collect();
595+
assert_eq!(parts.len(), 2);
596+
597+
// Date part
598+
let date_parts: Vec<&str> = parts[0].split('-').collect();
599+
assert_eq!(date_parts.len(), 3);
600+
601+
// Time part
602+
let time_parts: Vec<&str> = parts[1].split(':').collect();
603+
assert_eq!(time_parts.len(), 3);
604+
}
605+
606+
#[test]
607+
fn test_format_preserves_whitespace() {
608+
let template = " {date} {time} ";
609+
let message = format_commit_message(template);
610+
611+
assert!(message.starts_with(" "));
612+
assert!(message.ends_with(" "));
613+
assert!(message.contains(" ")); // Multiple spaces preserved
614+
}
416615
}

autogit-shared/src/config.rs

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,4 +141,153 @@ mod tests {
141141
assert_eq!(deserialized.daemon.check_interval_seconds, 60);
142142
assert_eq!(deserialized.repositories.len(), 1);
143143
}
144+
145+
#[test]
146+
fn test_config_defaults() {
147+
let config = Config::default();
148+
149+
// Default daemon config
150+
assert_eq!(config.daemon.check_interval_seconds, 300); // 5 minutes
151+
152+
// No repositories by default
153+
assert_eq!(config.repositories.len(), 0);
154+
}
155+
156+
#[test]
157+
fn test_daemon_config_defaults() {
158+
let daemon_config = DaemonConfig::default();
159+
assert_eq!(daemon_config.check_interval_seconds, 300);
160+
}
161+
162+
#[test]
163+
fn test_repository_with_defaults() {
164+
// Test that serde defaults work when fields are missing
165+
let toml_str = r#"
166+
path = "/home/user/repo"
167+
"#;
168+
169+
let repo: Repository = toml::from_str(toml_str).unwrap();
170+
171+
assert_eq!(repo.path, PathBuf::from("/home/user/repo"));
172+
assert_eq!(repo.auto_commit, true); // default_true
173+
assert_eq!(repo.commit_message_template, "Auto-commit: {timestamp}"); // default_commit_message
174+
}
175+
176+
#[test]
177+
fn test_config_with_partial_daemon_section() {
178+
// Test that daemon defaults work when section is missing
179+
let toml_str = r#"
180+
[[repositories]]
181+
path = "/home/user/notes"
182+
"#;
183+
184+
let config: Config = toml::from_str(toml_str).unwrap();
185+
186+
assert_eq!(config.daemon.check_interval_seconds, 300); // Uses default
187+
assert_eq!(config.repositories.len(), 1);
188+
}
189+
190+
#[test]
191+
fn test_config_with_multiple_repositories() {
192+
let config = Config {
193+
daemon: DaemonConfig {
194+
check_interval_seconds: 120,
195+
},
196+
repositories: vec![
197+
Repository {
198+
path: PathBuf::from("/home/user/notes"),
199+
auto_commit: true,
200+
commit_message_template: "Notes: {date}".to_owned(),
201+
},
202+
Repository {
203+
path: PathBuf::from("/home/user/journal"),
204+
auto_commit: false,
205+
commit_message_template: "Journal: {time}".to_owned(),
206+
},
207+
Repository {
208+
path: PathBuf::from("/home/user/code"),
209+
auto_commit: true,
210+
commit_message_template: "Code changes".to_owned(),
211+
},
212+
],
213+
};
214+
215+
let toml_str = toml::to_string_pretty(&config).unwrap();
216+
let deserialized: Config = toml::from_str(&toml_str).unwrap();
217+
218+
assert_eq!(deserialized.repositories.len(), 3);
219+
assert_eq!(deserialized.repositories[0].auto_commit, true);
220+
assert_eq!(deserialized.repositories[1].auto_commit, false);
221+
assert_eq!(deserialized.repositories[2].commit_message_template, "Code changes");
222+
}
223+
224+
#[test]
225+
fn test_config_empty_repositories() {
226+
let config = Config {
227+
daemon: DaemonConfig {
228+
check_interval_seconds: 60,
229+
},
230+
repositories: vec![],
231+
};
232+
233+
let toml_str = toml::to_string_pretty(&config).unwrap();
234+
let deserialized: Config = toml::from_str(&toml_str).unwrap();
235+
236+
assert_eq!(deserialized.repositories.len(), 0);
237+
}
238+
239+
#[test]
240+
fn test_repository_disabled_auto_commit() {
241+
let toml_str = r#"
242+
path = "/home/user/repo"
243+
auto_commit = false
244+
commit_message_template = "Custom message"
245+
"#;
246+
247+
let repo: Repository = toml::from_str(toml_str).unwrap();
248+
249+
assert_eq!(repo.auto_commit, false);
250+
assert_eq!(repo.commit_message_template, "Custom message");
251+
}
252+
253+
#[test]
254+
fn test_config_various_intervals() {
255+
for interval in [1, 60, 300, 3600, 86400] {
256+
let config = Config {
257+
daemon: DaemonConfig {
258+
check_interval_seconds: interval,
259+
},
260+
repositories: vec![],
261+
};
262+
263+
let toml_str = toml::to_string_pretty(&config).unwrap();
264+
let deserialized: Config = toml::from_str(&toml_str).unwrap();
265+
266+
assert_eq!(deserialized.daemon.check_interval_seconds, interval);
267+
}
268+
}
269+
270+
#[test]
271+
fn test_repository_custom_message_templates() {
272+
let templates = vec![
273+
"Auto-commit: {timestamp}",
274+
"Changes at {date} {time}",
275+
"Update {date}",
276+
"Checkpoint",
277+
"Work in progress: {timestamp}",
278+
];
279+
280+
for template in templates {
281+
let repo = Repository {
282+
path: PathBuf::from("/test"),
283+
auto_commit: true,
284+
commit_message_template: template.to_owned(),
285+
};
286+
287+
let toml_str = toml::to_string(&repo).unwrap();
288+
let deserialized: Repository = toml::from_str(&toml_str).unwrap();
289+
290+
assert_eq!(deserialized.commit_message_template, template);
291+
}
292+
}
144293
}

0 commit comments

Comments
 (0)