#![cfg_attr(coverage_nightly, coverage(off))]
use crate::cli::commands::AnnotateOutputFormat;
use crate::cli::commands::WorkPriority;
use crate::models::roadmap::{ItemStatus, Priority, Roadmap, RoadmapItem};
use crate::services::roadmap_service::RoadmapService;
use std::path::PathBuf;
use tempfile::TempDir;
fn setup_temp_roadmap() -> (TempDir, PathBuf) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let roadmap_dir = temp_dir.path().join("docs/roadmaps");
std::fs::create_dir_all(&roadmap_dir).expect("Failed to create roadmap dir");
let roadmap_path = roadmap_dir.join("roadmap.yaml");
(temp_dir, roadmap_path)
}
fn write_valid_roadmap(path: &std::path::Path) {
let content = r#"roadmap_version: '1.0'
github_enabled: true
github_repo: test/repo
roadmap:
- id: PMAT-001
title: "First ticket"
status: planned
priority: medium
created: "2024-01-01T00:00:00Z"
updated: "2024-01-01T00:00:00Z"
acceptance_criteria:
- Test criterion 1
- id: PMAT-002
title: "Second ticket"
status: inprogress
priority: high
created: "2024-01-01T00:00:00Z"
updated: "2024-01-01T00:00:00Z"
acceptance_criteria: []
"#;
std::fs::write(path, content).expect("Failed to write roadmap");
}
#[test]
fn test_extract_line_from_yaml_error_with_line_info() {
let error = "expected a mapping value at line 5 column 3";
let result = extract_line_from_yaml_error_test(error);
assert_eq!(result, Some(5));
}
#[test]
fn test_extract_line_from_yaml_error_without_line_info() {
let error = "invalid type: expected string";
let result = extract_line_from_yaml_error_test(error);
assert_eq!(result, None);
}
#[test]
fn test_extract_line_from_yaml_error_edge_case() {
let error = "at line 42column 1";
let result = extract_line_from_yaml_error_test(error);
assert!(result.is_none() || result == Some(42));
}
#[test]
fn test_extract_line_from_yaml_error_at_line_0() {
let error = "at line 0 column 1";
let result = extract_line_from_yaml_error_test(error);
assert_eq!(result, Some(0));
}
#[test]
fn test_extract_line_from_yaml_error_large_line_number() {
let error = "at line 9999 column 1";
let result = extract_line_from_yaml_error_test(error);
assert_eq!(result, Some(9999));
}
fn extract_line_from_yaml_error_test(error: &str) -> Option<usize> {
if let Some(pos) = error.find("at line ") {
let rest = &error[pos + 8..];
if let Some(end) = rest.find(' ') {
return rest[..end].parse().ok();
}
}
None
}
#[test]
fn test_generate_next_id_empty_roadmap() {
let roadmap = Roadmap::new(None);
let next_id = generate_next_id_test(&roadmap);
assert_eq!(next_id, "PMAT-001");
}
#[test]
fn test_generate_next_id_with_existing_items() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new("PMAT-001".to_string(), "Test".to_string()));
roadmap.upsert_item(RoadmapItem::new("PMAT-005".to_string(), "Test".to_string()));
let next_id = generate_next_id_test(&roadmap);
assert_eq!(next_id, "PMAT-006");
}
#[test]
fn test_generate_next_id_with_mixed_prefixes() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new("GH-100".to_string(), "Test".to_string()));
roadmap.upsert_item(RoadmapItem::new("PMAT-003".to_string(), "Test".to_string()));
roadmap.upsert_item(RoadmapItem::new("TASK-050".to_string(), "Test".to_string()));
let next_id = generate_next_id_test(&roadmap);
assert_eq!(next_id, "PMAT-101");
}
#[test]
fn test_generate_next_id_with_non_numeric_ids() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new(
"no-number".to_string(),
"Test".to_string(),
));
roadmap.upsert_item(RoadmapItem::new(
"also-no-number".to_string(),
"Test".to_string(),
));
let next_id = generate_next_id_test(&roadmap);
assert_eq!(next_id, "PMAT-001");
}
fn generate_next_id_test(roadmap: &Roadmap) -> String {
let mut max_num = 0u32;
for item in &roadmap.roadmap {
if let Some(num_str) = item.id.split('-').next_back() {
if let Ok(num) = num_str.parse::<u32>() {
max_num = max_num.max(num);
}
}
}
format!("PMAT-{:03}", max_num + 1)
}
#[test]
fn test_tdg_severity_label_excellent() {
assert_eq!(tdg_severity_label_test(0.5), "Excellent");
assert_eq!(tdg_severity_label_test(1.0), "Excellent");
}
#[test]
fn test_tdg_severity_label_good() {
assert_eq!(tdg_severity_label_test(1.5), "Good");
assert_eq!(tdg_severity_label_test(2.0), "Good");
}
#[test]
fn test_tdg_severity_label_moderate() {
assert_eq!(tdg_severity_label_test(2.5), "Moderate");
assert_eq!(tdg_severity_label_test(3.0), "Moderate");
}
#[test]
fn test_tdg_severity_label_critical() {
assert_eq!(tdg_severity_label_test(3.5), "Critical");
assert_eq!(tdg_severity_label_test(5.0), "Critical");
}
fn tdg_severity_label_test(score: f64) -> &'static str {
if score <= 1.0 {
"Excellent"
} else if score <= 2.0 {
"Good"
} else if score <= 3.0 {
"Moderate"
} else {
"Critical"
}
}
#[test]
fn test_calculate_spec_score_empty_spec() {
let spec = TestParsedSpec {
issue_refs: vec![],
code_examples: vec![],
acceptance_criteria: vec![],
claims: vec![],
title: String::new(),
test_requirements: vec![],
};
assert_eq!(calculate_spec_score_test(&spec), 0.0);
}
#[test]
fn test_calculate_spec_score_with_issue_refs() {
let spec = TestParsedSpec {
issue_refs: vec!["GH-123".to_string()],
code_examples: vec![],
acceptance_criteria: vec![],
claims: vec![],
title: String::new(),
test_requirements: vec![],
};
assert_eq!(calculate_spec_score_test(&spec), 10.0);
}
#[test]
fn test_calculate_spec_score_with_title() {
let spec = TestParsedSpec {
issue_refs: vec![],
code_examples: vec![],
acceptance_criteria: vec![],
claims: vec![],
title: "Test Title".to_string(),
test_requirements: vec![],
};
assert_eq!(calculate_spec_score_test(&spec), 5.0);
}
#[test]
fn test_calculate_spec_score_max_code_examples() {
let spec = TestParsedSpec {
issue_refs: vec![],
code_examples: vec!["ex".to_string(); 10], acceptance_criteria: vec![],
claims: vec![],
title: String::new(),
test_requirements: vec![],
};
assert_eq!(calculate_spec_score_test(&spec), 20.0);
}
#[test]
fn test_calculate_spec_score_max_score() {
let spec = TestParsedSpec {
issue_refs: vec!["GH-1".to_string()], code_examples: vec!["ex".to_string(); 5], acceptance_criteria: vec!["ac".to_string(); 10], claims: vec!["cl".to_string(); 20], title: "Title".to_string(), test_requirements: vec!["tr".to_string(); 5], };
assert_eq!(calculate_spec_score_test(&spec), 100.0);
}
#[test]
fn test_calculate_spec_score_capped_at_100() {
let spec = TestParsedSpec {
issue_refs: vec!["GH-1".to_string()],
code_examples: vec!["ex".to_string(); 20], acceptance_criteria: vec!["ac".to_string(); 50],
claims: vec!["cl".to_string(); 100],
title: "Title".to_string(),
test_requirements: vec!["tr".to_string(); 50],
};
assert_eq!(calculate_spec_score_test(&spec), 100.0);
}
struct TestParsedSpec {
issue_refs: Vec<String>,
code_examples: Vec<String>,
acceptance_criteria: Vec<String>,
claims: Vec<String>,
title: String,
test_requirements: Vec<String>,
}
fn calculate_spec_score_test(spec: &TestParsedSpec) -> f64 {
let mut score = 0.0;
if !spec.issue_refs.is_empty() {
score += 10.0;
}
score += (spec.code_examples.len().min(5) * 4) as f64;
score += (spec.acceptance_criteria.len().min(10) * 3) as f64;
score += (spec.claims.len().min(20)) as f64;
if !spec.title.is_empty() {
score += 5.0;
}
score += (spec.test_requirements.len().min(5) * 3) as f64;
score.min(100.0)
}
#[test]
fn test_work_priority_to_roadmap_priority_low() {
let wp = WorkPriority::Low;
assert_eq!(wp.to_roadmap_priority(), Priority::Low);
}
#[test]
fn test_work_priority_to_roadmap_priority_medium() {
let wp = WorkPriority::Medium;
assert_eq!(wp.to_roadmap_priority(), Priority::Medium);
}
#[test]
fn test_work_priority_to_roadmap_priority_high() {
let wp = WorkPriority::High;
assert_eq!(wp.to_roadmap_priority(), Priority::High);
}
#[test]
fn test_work_priority_to_roadmap_priority_critical() {
let wp = WorkPriority::Critical;
assert_eq!(wp.to_roadmap_priority(), Priority::Critical);
}
#[test]
fn test_work_priority_default() {
let wp = WorkPriority::default();
assert_eq!(wp, WorkPriority::Medium);
}
#[test]
fn test_annotate_output_format_default() {
let fmt = AnnotateOutputFormat::default();
assert_eq!(format!("{:?}", fmt), "Text");
}
#[test]
fn test_annotate_output_format_variants() {
let text = AnnotateOutputFormat::Text;
let json = AnnotateOutputFormat::Json;
let md = AnnotateOutputFormat::Markdown;
assert_eq!(format!("{:?}", text), "Text");
assert_eq!(format!("{:?}", json), "Json");
assert_eq!(format!("{:?}", md), "Markdown");
}
#[test]
fn test_roadmap_service_exists() {
let (temp_dir, roadmap_path) = setup_temp_roadmap();
let service = RoadmapService::new(&roadmap_path);
assert!(!service.exists());
write_valid_roadmap(&roadmap_path);
assert!(service.exists());
drop(temp_dir);
}
#[test]
fn test_roadmap_service_load_valid() {
let (temp_dir, roadmap_path) = setup_temp_roadmap();
write_valid_roadmap(&roadmap_path);
let service = RoadmapService::new(&roadmap_path);
let roadmap = service.load().expect("Failed to load roadmap");
assert_eq!(roadmap.roadmap_version, "1.0");
assert!(roadmap.github_enabled);
assert_eq!(roadmap.github_repo, Some("test/repo".to_string()));
assert_eq!(roadmap.roadmap.len(), 2);
drop(temp_dir);
}
#[test]
fn test_roadmap_service_upsert_and_find() {
let (temp_dir, roadmap_path) = setup_temp_roadmap();
let service = RoadmapService::new(&roadmap_path);
let item = RoadmapItem::new("TEST-001".to_string(), "Test ticket".to_string());
service.upsert_item(item.clone()).expect("Failed to upsert");
let found = service
.find_item("TEST-001")
.expect("Failed to find")
.expect("Item not found");
assert_eq!(found.id, "TEST-001");
assert_eq!(found.title, "Test ticket");
drop(temp_dir);
}
#[test]
fn test_roadmap_service_remove() {
let (temp_dir, roadmap_path) = setup_temp_roadmap();
let service = RoadmapService::new(&roadmap_path);
let item = RoadmapItem::new("TEST-001".to_string(), "Test ticket".to_string());
service.upsert_item(item).expect("Failed to upsert");
let removed = service.remove_item("TEST-001").expect("Failed to remove");
assert!(removed.is_some());
assert_eq!(removed.unwrap().id, "TEST-001");
let not_found = service.find_item("TEST-001").expect("Failed to find");
assert!(not_found.is_none());
drop(temp_dir);
}
#[test]
fn test_fuzzy_matching_exact() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new("PMAT-001".to_string(), "Test".to_string()));
let found = roadmap.find_item("PMAT-001");
assert!(found.is_some());
assert_eq!(found.unwrap().id, "PMAT-001");
}
#[test]
fn test_fuzzy_matching_case_insensitive() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new("PMAT-001".to_string(), "Test".to_string()));
let found = roadmap.find_item("pmat-001");
assert!(found.is_some());
assert_eq!(found.unwrap().id, "PMAT-001");
}
#[test]
fn test_fuzzy_matching_prefix() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new("PMAT-001".to_string(), "Test".to_string()));
let found = roadmap.find_item("PMAT-0");
assert!(found.is_some());
assert_eq!(found.unwrap().id, "PMAT-001");
}
#[test]
fn test_fuzzy_matching_contains() {
let mut roadmap = Roadmap::new(None);
roadmap.upsert_item(RoadmapItem::new("PMAT-001".to_string(), "Test".to_string()));
let found = roadmap.find_item("001");
assert!(found.is_some());
assert_eq!(found.unwrap().id, "PMAT-001");
}
include!("coverage_boost_ticket_handlers_part2.rs");
include!("coverage_boost_ticket_handlers_part3.rs");