use caldav_sync::{Config, CalDavResult}; #[cfg(test)] mod config_tests { use super::*; #[test] fn test_default_config() -> CalDavResult<()> { let config = Config::default(); assert_eq!(config.server.url, "https://caldav.example.com"); assert_eq!(config.calendar.name, "calendar"); assert_eq!(config.sync.interval, 300); config.validate()?; Ok(()) } #[test] fn test_config_validation() -> CalDavResult<()> { let mut config = Config::default(); // Should fail with empty credentials assert!(config.validate().is_err()); config.server.username = "test_user".to_string(); config.server.password = "test_pass".to_string(); // Should succeed now assert!(config.validate().is_ok()); Ok(()) } } #[cfg(test)] mod error_tests { use caldav_sync::{CalDavError, CalDavResult}; #[test] fn test_error_retryable() { let network_error = CalDavError::Network( reqwest::Error::from(std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "test")) ); assert!(network_error.is_retryable()); let auth_error = CalDavError::Authentication("Invalid credentials".to_string()); assert!(!auth_error.is_retryable()); let config_error = CalDavError::Config("Missing URL".to_string()); assert!(!config_error.is_retryable()); } #[test] fn test_error_classification() { let auth_error = CalDavError::Authentication("Invalid".to_string()); assert!(auth_error.is_auth_error()); let config_error = CalDavError::Config("Invalid".to_string()); assert!(config_error.is_config_error()); } } #[cfg(test)] mod event_tests { use caldav_sync::event::{Event, EventStatus, EventType}; use chrono::{DateTime, Utc, NaiveDate}; #[test] fn test_event_creation() { let start = Utc::now(); let end = start + chrono::Duration::hours(1); let event = Event::new("Test Event".to_string(), start, end); assert_eq!(event.summary, "Test Event"); assert_eq!(event.start, start); assert_eq!(event.end, end); assert!(!event.all_day); assert_eq!(event.status, EventStatus::Confirmed); assert_eq!(event.event_type, EventType::Public); } #[test] fn test_all_day_event() { let date = NaiveDate::from_ymd_opt(2023, 12, 25).unwrap(); let event = Event::new_all_day("Christmas".to_string(), date); assert_eq!(event.summary, "Christmas"); assert!(event.all_day); assert!(event.occurs_on(date)); } #[test] fn test_event_to_ical() -> caldav_sync::CalDavResult<()> { let event = Event::new( "Meeting".to_string(), DateTime::from_naive_utc_and_offset( chrono::NaiveDateTime::parse_from_str("20231225T100000", "%Y%m%dT%H%M%S").unwrap(), Utc ), DateTime::from_naive_utc_and_offset( chrono::NaiveDateTime::parse_from_str("20231225T110000", "%Y%m%dT%H%M%S").unwrap(), Utc ), ); let ical = event.to_ical()?; assert!(ical.contains("SUMMARY:Meeting")); assert!(ical.contains("DTSTART:20231225T100000Z")); assert!(ical.contains("DTEND:20231225T110000Z")); assert!(ical.contains("BEGIN:VCALENDAR")); assert!(ical.contains("END:VCALENDAR")); Ok(()) } } #[cfg(test)] mod timezone_tests { use caldav_sync::timezone::TimezoneHandler; #[test] fn test_timezone_handler_creation() -> CalDavResult<()> { let handler = TimezoneHandler::new("UTC")?; assert_eq!(handler.default_timezone(), "UTC"); Ok(()) } #[test] fn test_timezone_validation() { assert!(TimezoneHandler::validate_timezone("UTC")); assert!(TimezoneHandler::validate_timezone("America/New_York")); assert!(TimezoneHandler::validate_timezone("Europe/London")); assert!(!TimezoneHandler::validate_timezone("Invalid/Timezone")); } #[test] fn test_ical_formatting() -> CalDavResult<()> { let handler = TimezoneHandler::default(); let dt = DateTime::from_naive_utc_and_offset( chrono::NaiveDateTime::parse_from_str("20231225T100000", "%Y%m%dT%H%M%S").unwrap(), Utc ); let ical_utc = handler.format_ical_datetime(dt, false)?; assert_eq!(ical_utc, "20231225T100000Z"); let ical_date = handler.format_ical_date(dt); assert_eq!(ical_date, "20231225"); Ok(()) } } #[cfg(test)] mod filter_tests { use caldav_sync::calendar_filter::{ CalendarFilter, FilterRule, DateRangeFilter, KeywordFilter, EventTypeFilter, EventStatusFilter, FilterBuilder }; use caldav_sync::event::{Event, EventStatus, EventType}; use chrono::{DateTime, Utc}; #[test] fn test_date_range_filter() { let start = DateTime::from_naive_utc_and_offset( chrono::NaiveDateTime::parse_from_str("20231225T000000", "%Y%m%dT%H%M%S").unwrap(), Utc ); let end = DateTime::from_naive_utc_and_offset( chrono::NaiveDateTime::parse_from_str("20231225T235959", "%Y%m%dT%H%M%S").unwrap(), Utc ); let filter = DateRangeFilter::new(start, end); let event_start = DateTime::from_naive_utc_and_offset( chrono::NaiveDateTime::parse_from_str("20231225T100000", "%Y%m%dT%H%M%S").unwrap(), Utc ); let event = Event::new("Test".to_string(), event_start, event_start + chrono::Duration::hours(1)); assert!(filter.matches_event(&event)); let event_outside = Event::new( "Test".to_string(), start - chrono::Duration::days(1), start - chrono::Duration::hours(23), ); assert!(!filter_outside.matches_event(&event_outside)); } #[test] fn test_keyword_filter() { let filter = KeywordFilter::new(vec!["meeting".to_string(), "important".to_string()], false); let event1 = Event::new("Team Meeting".to_string(), Utc::now(), Utc::now()); assert!(filter.matches_event(&event1)); let event2 = Event::new("Lunch".to_string(), Utc::now(), Utc::now()); assert!(!filter.matches_event(&event2)); } #[test] fn test_calendar_filter() { let mut filter = CalendarFilter::new(true); // OR logic filter.add_rule(FilterRule::Keywords(KeywordFilter::new(vec!["meeting".to_string()], false))); filter.add_rule(FilterRule::EventStatus(EventStatusFilter::new(vec![EventStatus::Cancelled]))); let event1 = Event::new("Team Meeting".to_string(), Utc::now(), Utc::now()); assert!(filter.matches_event(&event1)); // Matches keyword let mut event2 = Event::new("Holiday".to_string(), Utc::now(), Utc::now()); event2.status = EventStatus::Cancelled; assert!(filter.matches_event(&event2)); // Matches status let event3 = Event::new("Lunch".to_string(), Utc::now(), Utc::now()); assert!(!filter.matches_event(&event3)); // Matches neither } #[test] fn test_filter_builder() { let filter = FilterBuilder::new() .match_any(false) // AND logic .keywords(vec!["meeting".to_string()]) .event_types(vec![EventType::Public]) .build(); let event = Event::new("Team Meeting".to_string(), Utc::now(), Utc::now()); assert!(filter.matches_event(&event)); // Matches both conditions } } #[cfg(test)] mod integration_tests { use super::*; #[test] fn test_library_initialization() -> CalDavResult<()> { caldav_sync::init()?; Ok(()) } #[test] fn test_version() { assert!(!caldav_sync::VERSION.is_empty()); } #[test] fn test_full_workflow() -> CalDavResult<()> { // Initialize library caldav_sync::init()?; // Create configuration let config = Config::default(); // Validate configuration config.validate()?; // Create some test events let event1 = caldav_sync::event::Event::new( "Test Meeting".to_string(), Utc::now(), Utc::now() + chrono::Duration::hours(1), ); let event2 = caldav_sync::event::Event::new_all_day( "Test Holiday".to_string(), chrono::NaiveDate::from_ymd_opt(2023, 12, 25).unwrap(), ); // Test event serialization let ical1 = event1.to_ical()?; let ical2 = event2.to_ical()?; assert!(!ical1.is_empty()); assert!(!ical2.is_empty()); assert!(ical1.contains("SUMMARY:Test Meeting")); assert!(ical2.contains("SUMMARY:Test Holiday")); // Test filtering let filter = caldav_sync::calendar_filter::FilterBuilder::new() .keywords(vec!["test".to_string()]) .build(); assert!(filter.matches_event(&event1)); assert!(filter.matches_event(&event2)); Ok(()) } }