use pdmt::models::todo::{Todo, TodoList, TodoPriority, TodoQualityConfig};
use pdmt::validators::todo::{IssueCategory, IssueSeverity, TodoValidator};
#[test]
fn test_validator_with_custom_config() {
let config = TodoQualityConfig {
max_todos_per_batch: Some(5),
min_task_detail_chars: Some(20),
max_task_detail_chars: Some(200),
max_complexity_per_task: Some(5),
require_time_estimates: true,
require_specific_actions: true,
require_dependency_graph: false,
prevent_circular_dependencies: false,
min_estimated_hours: Some(1.0),
max_estimated_hours: Some(20.0),
};
let validator = TodoValidator::with_config(config);
let mut todo_list = TodoList::new();
let mut bad_todo = Todo::new("stuff");
bad_todo.estimated_hours = Some(0.1); todo_list.add_todo(bad_todo);
let result = validator.validate_todo_list(&todo_list);
assert!(!result.is_valid);
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::Actionability));
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::Completeness));
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::TimeEstimate));
}
#[test]
fn test_validator_structure_checks() {
let validator = TodoValidator::new();
let mut todo_list = TodoList::new();
let mut todo1 = Todo::new("Task 1");
todo1.id = "duplicate_id".to_string();
let mut todo2 = Todo::new("Task 2");
todo2.id = "duplicate_id".to_string();
todo_list.add_todo(todo1);
todo_list.add_todo(todo2);
let result = validator.validate_todo_list(&todo_list);
assert!(
result
.issues
.iter()
.any(|i| i.category == IssueCategory::Structure
&& i.message.contains("Duplicate todo ID"))
);
}
#[test]
fn test_validator_dependency_checks() {
let validator = TodoValidator::new();
let mut todo_list = TodoList::new();
let mut todo1 = Todo::new("Implement feature");
todo1.id = "task1".to_string();
todo1.dependencies = vec!["non_existent".to_string()];
let mut todo2 = Todo::new("Test feature");
todo2.id = "task2".to_string();
todo2.dependencies = vec!["task2".to_string()];
todo_list.add_todo(todo1);
todo_list.add_todo(todo2);
let result = validator.validate_todo_list(&todo_list);
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::Dependencies && i.message.contains("not found")));
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::Dependencies
&& i.message.contains("depends on itself")));
}
#[test]
fn test_validator_generic_language_detection() {
let config = TodoQualityConfig {
require_specific_actions: true,
..TodoQualityConfig::default()
};
let validator = TodoValidator::with_config(config);
let mut todo_list = TodoList::new();
todo_list.add_todo(Todo::new("Fix stuff in the thing"));
todo_list.add_todo(Todo::new("Handle something"));
todo_list.add_todo(Todo::new("Do item processing"));
let result = validator.validate_todo_list(&todo_list);
let generic_issues: Vec<_> = result
.issues
.iter()
.filter(|i| i.message.contains("generic language"))
.collect();
assert!(!generic_issues.is_empty());
}
#[test]
fn test_validator_quality_score_calculation() {
let validator = TodoValidator::new();
let mut todo_list = TodoList::new();
let mut good_todo1 = Todo::new("Implement user authentication with OAuth2");
good_todo1.estimated_hours = Some(8.0);
good_todo1.priority = TodoPriority::High;
let mut good_todo2 = Todo::new("Create REST API endpoints for user management");
good_todo2.estimated_hours = Some(6.0);
good_todo2.dependencies = vec![good_todo1.id.clone()];
todo_list.add_todo(good_todo1);
todo_list.add_todo(good_todo2);
let result = validator.validate_todo_list(&todo_list);
assert_eq!(result.metrics.actionable_count, 2);
assert_eq!(result.metrics.estimated_count, 2);
assert!(result.metrics.total_estimated_hours > 0.0);
assert!(!result.metrics.dependency_metrics.has_cycles);
}
#[test]
fn test_validator_empty_list() {
let validator = TodoValidator::new();
let todo_list = TodoList::new();
let result = validator.validate_todo_list(&todo_list);
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::Structure && i.message.contains("empty")));
}
#[test]
fn test_validator_max_todos_limit() {
let config = TodoQualityConfig {
max_todos_per_batch: Some(3),
..TodoQualityConfig::default()
};
let validator = TodoValidator::with_config(config);
let mut todo_list = TodoList::new();
for i in 0..5 {
todo_list.add_todo(Todo::new(format!("Task {}", i)));
}
let result = validator.validate_todo_list(&todo_list);
assert!(result
.issues
.iter()
.any(|i| i.severity == IssueSeverity::Error && i.message.contains("exceeds maximum")));
}
#[test]
fn test_validator_suggestions() {
let validator = TodoValidator::new();
let mut todo_list = TodoList::new();
let mut todo = Todo::new("thing to do");
todo.estimated_hours = None;
todo_list.add_todo(todo);
let result = validator.validate_todo_list(&todo_list);
assert!(!result.suggestions.is_empty());
assert!(result.suggestions.iter().any(|s| s.contains("actionable")));
}
#[test]
fn test_validator_dependency_depth() {
let validator = TodoValidator::new();
let mut todo_list = TodoList::new();
let mut todo1 = Todo::new("Task 1");
todo1.id = "t1".to_string();
let mut todo2 = Todo::new("Task 2");
todo2.id = "t2".to_string();
todo2.dependencies = vec!["t1".to_string()];
let mut todo3 = Todo::new("Task 3");
todo3.id = "t3".to_string();
todo3.dependencies = vec!["t2".to_string()];
let mut todo4 = Todo::new("Task 4");
todo4.id = "t4".to_string();
todo4.dependencies = vec!["t3".to_string()];
todo_list.add_todo(todo1);
todo_list.add_todo(todo2);
todo_list.add_todo(todo3);
todo_list.add_todo(todo4);
let result = validator.validate_todo_list(&todo_list);
assert_eq!(result.metrics.dependency_metrics.max_depth, 4);
assert_eq!(result.metrics.dependency_metrics.critical_path_length, 4);
assert!(!result.metrics.dependency_metrics.has_cycles);
}
#[test]
fn test_validator_duplicate_content() {
let validator = TodoValidator::new();
let mut todo_list = TodoList::new();
todo_list.add_todo(Todo::new("Implement feature X"));
todo_list.add_todo(Todo::new(" implement FEATURE x "));
let result = validator.validate_todo_list(&todo_list);
assert!(result
.issues
.iter()
.any(|i| i.category == IssueCategory::Structure
&& i.message.contains("Duplicate todo content")));
}