use cfait::model::{AlarmTrigger, Task};
use chrono::{Local, Timelike};
use std::collections::HashMap;
#[test]
fn test_mixed_text_and_reminder_syntax() {
let aliases = HashMap::new();
let t = Task::new("rem today rem:today 15:02", &aliases, None);
assert_eq!(
t.summary, "rem today",
"Bare 'rem' and 'today' should be text, not escaped"
);
assert_eq!(t.alarms.len(), 1, "Should have 1 alarm");
match t.alarms[0].trigger {
AlarmTrigger::Absolute(dt) => {
let local = dt.with_timezone(&Local);
assert_eq!(local.hour(), 15);
assert_eq!(local.minute(), 2);
assert_eq!(local.date_naive(), Local::now().date_naive());
}
_ => panic!("Expected absolute trigger"),
}
}
#[test]
fn test_double_hash_inline_tag() {
let aliases = HashMap::new();
let t = Task::new("Apply ##CDV suggestions on ##JDDC", &aliases, None);
assert_eq!(t.summary, "Apply CDV suggestions on JDDC");
assert_eq!(t.categories, vec!["CDV", "JDDC"]);
}
#[test]
fn test_triple_at_inline_location() {
let aliases = HashMap::new();
let t = Task::new("Fix @@@Office printer", &aliases, None);
assert_eq!(t.summary, "Fix Office printer");
assert_eq!(t.locations, vec!["Office".to_string()]);
}
#[test]
fn test_mixed_inline_and_regular_tags() {
let aliases = HashMap::new();
let t = Task::new("Apply ##CDV suggestions #urgent", &aliases, None);
assert_eq!(t.summary, "Apply CDV suggestions");
assert!(t.categories.contains(&"CDV".to_string()));
assert!(t.categories.contains(&"urgent".to_string()));
}
#[test]
fn test_mixed_inline_and_regular_locations() {
let aliases = HashMap::new();
let t = Task::new("Visit @@@Home and @@Office", &aliases, None);
assert_eq!(t.summary, "Visit Home and");
assert_eq!(t.locations, vec!["Home".to_string(), "Office".to_string()]);
}
#[test]
fn test_inline_tag_with_hierarchy() {
let aliases = HashMap::new();
let t = Task::new("Apply ##work:urgent task", &aliases, None);
assert_eq!(t.summary, "Apply work:urgent task");
assert_eq!(t.categories, vec!["work:urgent"]);
}
#[test]
fn test_inline_location_with_hierarchy() {
let aliases = HashMap::new();
let t = Task::new("Go to @@@home:garage", &aliases, None);
assert_eq!(t.summary, "Go to home:garage");
assert_eq!(t.locations, vec!["home:garage".to_string()]);
}
#[test]
fn test_single_hash_still_works() {
let aliases = HashMap::new();
let t = Task::new("Apply #CDV suggestions", &aliases, None);
assert_eq!(t.summary, "Apply suggestions");
assert_eq!(t.categories, vec!["CDV"]);
}
#[test]
fn test_double_at_still_works() {
let aliases = HashMap::new();
let t = Task::new("Fix @@Office printer", &aliases, None);
assert_eq!(t.summary, "Fix printer");
assert_eq!(t.locations, vec!["Office".to_string()]);
}
#[test]
fn test_empty_inline_tag() {
let aliases = HashMap::new();
let t = Task::new("Task ##", &aliases, None);
assert_eq!(t.summary, "Task");
assert!(t.categories.is_empty());
}
#[test]
fn test_empty_inline_location() {
let aliases = HashMap::new();
let t = Task::new("Task @@@", &aliases, None);
assert_eq!(t.summary, "Task @@@");
assert!(t.locations.is_empty());
}
#[test]
fn test_quoted_inline_tag() {
let aliases = HashMap::new();
let t = Task::new("Apply ##\"CDV\" suggestions", &aliases, None);
assert_eq!(t.summary, "Apply CDV suggestions");
assert_eq!(t.categories, vec!["CDV"]);
}
#[test]
fn test_quoted_inline_location() {
let aliases = HashMap::new();
let t = Task::new("Go to @@@\"My Office\"", &aliases, None);
assert_eq!(t.summary, "Go to My Office");
assert_eq!(t.locations, vec!["My Office".to_string()]);
}
#[test]
fn test_bare_keywords_not_escaped() {
let aliases = HashMap::new();
let t = Task::new("Meeting today or tomorrow", &aliases, None);
assert_eq!(t.summary, "Meeting today or tomorrow");
assert!(t.due.is_none());
let t2 = Task::new("Meeting @today", &aliases, None);
assert_eq!(t2.summary, "Meeting");
assert!(t2.due.is_some());
}
#[test]
fn test_reminder_date_and_time_roundtrip() {
use chrono::Duration;
let aliases = HashMap::new();
let t = Task::new("test rem:tomorrow 16:00", &aliases, None);
assert_eq!(t.alarms.len(), 1);
match t.alarms[0].trigger {
AlarmTrigger::Absolute(dt) => {
let local = dt.with_timezone(&Local);
let tomorrow = Local::now().date_naive() + Duration::days(1);
assert_eq!(local.date_naive(), tomorrow, "Date should be tomorrow");
assert_eq!(local.hour(), 16, "Time should be 16:00");
}
_ => panic!("Expected absolute trigger"),
}
let smart = t.to_smart_string();
assert!(
smart.contains("rem:tomorrow 16:00"),
"Should preserve 'tomorrow' keyword in roundtrip. Got: {}",
smart
);
}
#[test]
fn test_rem_in_syntax() {
let aliases = HashMap::new();
let t = Task::new("Pizza rem:in 5m", &aliases, None);
assert_eq!(t.summary, "Pizza");
assert_eq!(t.alarms.len(), 1);
match t.alarms[0].trigger {
AlarmTrigger::Absolute(dt) => {
let now = Local::now();
let diff = dt.with_timezone(&Local) - now;
assert!(
diff.num_seconds() > 290 && diff.num_seconds() <= 300,
"Should be ~5 mins from now"
);
}
_ => panic!("rem:in should create Absolute alarm"),
}
let t2 = Task::new("Long task rem: in 1 hour", &aliases, None);
assert_eq!(t2.alarms.len(), 1);
match t2.alarms[0].trigger {
AlarmTrigger::Absolute(dt) => {
let now = Local::now();
let diff = dt.with_timezone(&Local) - now;
assert!(diff.num_minutes() >= 59 && diff.num_minutes() <= 60);
}
_ => panic!(),
}
}