//! Unit tests for snooze options command. use crate::commands::*; use chrono::{Datelike, Local, Timelike, Utc, Weekday}; #[test] fn returns_expected_option_keys() { let response = get_snooze_options(); // Should have 3-4 options (laterToday may be omitted if too late) assert!( response.options.len() >= 3, "Should have at least 3 options" ); assert!( response.options.len() <= 4, "Should have at most 4 options" ); // Check that expected keys are present let keys: Vec<&str> = response.options.iter().map(|o| o.key.as_str()).collect(); assert!(keys.contains(&"tomorrow"), "Should have tomorrow option"); assert!(keys.contains(&"weekend"), "Should have weekend option"); assert!(keys.contains(&"nextWeek"), "Should have nextWeek option"); } #[test] fn all_times_are_in_the_future() { let response = get_snooze_options(); let now = Local::now().with_timezone(&Utc); for option in &response.options { assert!( option.time > now, "Option '{}' time should be in the future: {:?}", option.key, option.time ); } } #[test] fn min_custom_is_current_time() { let before = Local::now().with_timezone(&Utc); let response = get_snooze_options(); let after = Local::now().with_timezone(&Utc); assert!( response.min_custom >= before && response.min_custom <= after, "min_custom should be approximately current time" ); } #[test] fn options_have_valid_labels() { let response = get_snooze_options(); for option in &response.options { assert!(!option.label.is_empty(), "Label should not be empty"); assert!( !option.formatted.is_empty(), "Formatted time should not be empty" ); } // Check specific labels let labels: Vec<&str> = response.options.iter().map(|o| o.label.as_str()).collect(); assert!(labels.contains(&"Tomorrow")); assert!(labels.contains(&"This Weekend")); assert!(labels.contains(&"Next Week")); } #[test] fn formatted_time_contains_expected_components() { let response = get_snooze_options(); for option in &response.options { // Should contain day abbreviation (Mon, Tue, etc.) let has_day = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] .iter() .any(|d| option.formatted.contains(d)); assert!( has_day, "Formatted time should contain day: {}", option.formatted ); // Should contain AM or PM assert!( option.formatted.contains("AM") || option.formatted.contains("PM"), "Formatted time should contain AM/PM: {}", option.formatted ); } } #[test] fn tomorrow_is_next_day_at_9am() { let response = get_snooze_options(); let tomorrow_option = response .options .iter() .find(|o| o.key == "tomorrow") .expect("Should have tomorrow option"); let local_time = tomorrow_option.time.with_timezone(&Local); assert_eq!(local_time.hour(), 9, "Tomorrow should be at 9am"); assert_eq!(local_time.minute(), 0, "Tomorrow should be at exactly :00"); } #[test] fn weekend_is_saturday_at_10am() { let response = get_snooze_options(); let weekend_option = response .options .iter() .find(|o| o.key == "weekend") .expect("Should have weekend option"); let local_time = weekend_option.time.with_timezone(&Local); assert_eq!( local_time.weekday(), Weekday::Sat, "Weekend should be Saturday" ); assert_eq!(local_time.hour(), 10, "Weekend should be at 10am"); assert_eq!(local_time.minute(), 0, "Weekend should be at exactly :00"); } #[test] fn next_week_is_monday_at_9am() { let response = get_snooze_options(); let next_week_option = response .options .iter() .find(|o| o.key == "nextWeek") .expect("Should have nextWeek option"); let local_time = next_week_option.time.with_timezone(&Local); assert_eq!( local_time.weekday(), Weekday::Mon, "Next week should be Monday" ); assert_eq!(local_time.hour(), 9, "Next week should be at 9am"); assert_eq!(local_time.minute(), 0, "Next week should be at exactly :00"); } #[test] fn options_are_ordered_chronologically() { let response = get_snooze_options(); for window in response.options.windows(2) { assert!( window[0].time < window[1].time, "Options should be in chronological order: {:?} should be before {:?}", window[0].key, window[1].key ); } }