use caldav_sync::Config; use caldav_sync::minicaldav_client::RealCalDavClient; use caldav_sync::event::Event; use chrono::{DateTime, Utc, Duration}; use tokio; use std::path::PathBuf; /// Test basic CRUD operations on the import calendar using the test configuration #[tokio::test] async fn test_create_update_delete_event() -> Result<(), Box> { println!("๐Ÿงช Starting CRUD test with import calendar..."); // Load test configuration let config_path = PathBuf::from("config-test-import.toml"); let config = Config::from_file(&config_path)?; // Validate configuration config.validate()?; // Create CalDAV client for target server (Nextcloud) let import_config = config.get_import_config().ok_or("No import configuration found")?; let target_client = RealCalDavClient::new( &import_config.target_server.url, &import_config.target_server.username, &import_config.target_server.password, ).await?; // Build target calendar URL let target_calendar_url = format!("{}/", import_config.target_server.url.trim_end_matches('/')); // Validate target calendar let is_valid = target_client.validate_target_calendar(&target_calendar_url).await?; assert!(is_valid, "Target calendar should be accessible"); println!("โœ… Target calendar is accessible"); // Create test event for today let now = Utc::now(); let today_start = now.date_naive().and_hms_opt(10, 0, 0).unwrap().and_utc(); let today_end = today_start + Duration::hours(1); let test_uid = format!("test-event-{}", now.timestamp()); let mut test_event = Event::new( format!("Test Event {}", test_uid), today_start, today_end, ); test_event.uid = test_uid.clone(); test_event.description = Some("This is a test event for CRUD operations".to_string()); test_event.location = Some("Test Location".to_string()); println!("๐Ÿ“ Creating test event: {}", test_event.summary); // Convert event to iCalendar format let ical_data = test_event.to_ical()?; // Test 1: Create event let create_result = target_client.put_event( &target_calendar_url, &test_uid, &ical_data, None // No ETag for creation ).await; match create_result { Ok(_) => println!("โœ… Event created successfully"), Err(e) => { println!("โŒ Failed to create event: {}", e); return Err(e.into()); } } // Wait a moment to ensure the event is processed tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; // Test 2: Verify event exists println!("๐Ÿ” Verifying event exists..."); let etag_result = target_client.get_event_etag(&target_calendar_url, &test_uid).await; let original_etag = match etag_result { Ok(Some(etag)) => { println!("โœ… Event verified, ETag: {}", etag); etag } Ok(None) => { println!("โŒ Event not found after creation"); return Err("Event not found after creation".into()); } Err(e) => { println!("โŒ Failed to verify event: {}", e); return Err(e.into()); } }; // Test 3: Update event (change date to tomorrow) println!("๐Ÿ“ Updating event for tomorrow..."); let tomorrow_start = today_start + Duration::days(1); let tomorrow_end = tomorrow_start + Duration::hours(1); test_event.start = tomorrow_start; test_event.end = tomorrow_end; test_event.summary = format!("Test Event {} (Updated for Tomorrow)", test_uid); test_event.description = Some("This event has been updated to tomorrow".to_string()); test_event.sequence += 1; // Increment sequence for update // Convert updated event to iCalendar format let updated_ical_data = test_event.to_ical()?; let update_result = target_client.put_event( &target_calendar_url, &test_uid, &updated_ical_data, Some(&original_etag) // Use ETag for update ).await; match update_result { Ok(_) => println!("โœ… Event updated successfully"), Err(e) => { println!("โŒ Failed to update event: {}", e); return Err(e.into()); } } // Wait a moment to ensure the update is processed tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; // Test 4: Verify event was updated (ETag should change) println!("๐Ÿ” Verifying event update..."); let new_etag_result = target_client.get_event_etag(&target_calendar_url, &test_uid).await; match new_etag_result { Ok(Some(new_etag)) => { if new_etag != original_etag { println!("โœ… Event updated, new ETag: {}", new_etag); } else { println!("โš ๏ธ Event ETag didn't change after update"); } } Ok(None) => { println!("โŒ Event not found after update"); return Err("Event not found after update".into()); } Err(e) => { println!("โŒ Failed to verify updated event: {}", e); return Err(e.into()); } } // Test 5: Delete event println!("๐Ÿ—‘๏ธ Deleting event..."); let delete_result = target_client.delete_event( &target_calendar_url, &test_uid, None // No ETag for deletion (let server handle it) ).await; match delete_result { Ok(_) => println!("โœ… Event deleted successfully"), Err(e) => { println!("โŒ Failed to delete event: {}", e); return Err(e.into()); } } // Wait a moment to ensure the deletion is processed tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; // Test 6: Verify event was deleted println!("๐Ÿ” Verifying event deletion..."); let final_check = target_client.get_event_etag(&target_calendar_url, &test_uid).await; match final_check { Ok(None) => { println!("โœ… Event successfully deleted"); } Ok(Some(etag)) => { println!("โŒ Event still exists after deletion, ETag: {}", etag); return Err("Event still exists after deletion".into()); } Err(e) => { // Check if it's a 404 error, which indicates successful deletion if e.to_string().contains("404") || e.to_string().contains("Not Found") { println!("โœ… Event successfully deleted (confirmed by 404)"); } else { println!("โŒ Failed to verify deletion: {}", e); return Err(e.into()); } } } println!("๐ŸŽ‰ All CRUD operations completed successfully!"); Ok(()) } /// Test HTTP error handling by attempting to delete a non-existent event #[tokio::test] async fn test_delete_nonexistent_event() -> Result<(), Box> { println!("๐Ÿงช Testing deletion of non-existent event..."); // Load test configuration let config_path = PathBuf::from("config-test-import.toml"); let config = Config::from_file(&config_path)?; // Create CalDAV client for target server let import_config = config.get_import_config().ok_or("No import configuration found")?; let target_client = RealCalDavClient::new( &import_config.target_server.url, &import_config.target_server.username, &import_config.target_server.password, ).await?; // Build target calendar URL let target_calendar_url = format!("{}/", import_config.target_server.url.trim_end_matches('/')); // Try to delete a non-existent event let fake_uid = "non-existent-event-12345"; println!("๐Ÿ—‘๏ธ Testing deletion of non-existent event: {}", fake_uid); let delete_result = target_client.delete_event( &target_calendar_url, fake_uid, None ).await; match delete_result { Ok(_) => { println!("โœ… Non-existent event deletion handled gracefully (idempotent)"); Ok(()) } Err(e) => { println!("โŒ Failed to handle non-existent event deletion gracefully: {}", e); Err(e.into()) } } } /// Test event existence checking #[tokio::test] async fn test_event_existence_check() -> Result<(), Box> { println!("๐Ÿงช Testing event existence check..."); // Load test configuration let config_path = PathBuf::from("config-test-import.toml"); let config = Config::from_file(&config_path)?; // Create CalDAV client for target server let import_config = config.get_import_config().ok_or("No import configuration found")?; let target_client = RealCalDavClient::new( &import_config.target_server.url, &import_config.target_server.username, &import_config.target_server.password, ).await?; // Build target calendar URL let target_calendar_url = format!("{}/", import_config.target_server.url.trim_end_matches('/')); // Test non-existent event let fake_uid = "non-existent-event-67890"; let fake_event_url = format!("{}{}.ics", target_calendar_url, fake_uid); println!("๐Ÿ” Testing existence check for non-existent event: {}", fake_uid); let existence_result = target_client.check_event_exists(&fake_event_url).await; match existence_result { Ok(_) => { println!("โŒ Non-existent event reported as existing"); Err("Non-existent event reported as existing".into()) } Err(e) => { println!("โœ… Non-existent event correctly reported as missing: {}", e); Ok(()) } } }