claude-utils 0.2.0

Cross-platform companion toolkit for Anthropic's Claude Code CLI
Documentation
#[cfg(test)]
mod tests {
    use super::super::*;
    use tempfile::tempdir;
    use tokio::fs;

    #[tokio::test]
    async fn test_text_staging() {
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(300),
            max_file_age: Duration::from_secs(900),
        };
        let manager = FileManager::new(config).await.unwrap();
        
        let test_text = "Hello, test!";
        let staged = manager.stage_text(test_text).await.unwrap();
        
        assert_eq!(staged.format, "txt");
        assert!(staged.path.exists());
        
        let content = fs::read_to_string(&staged.path).await.unwrap();
        assert_eq!(content, test_text);
    }

    #[tokio::test]
    async fn test_duplicate_file_deduplication() {
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(300),
            max_file_age: Duration::from_secs(900),
        };
        let manager = FileManager::new(config).await.unwrap();
        
        let test_text = "Duplicate content";
        
        let file1 = manager.stage_text(test_text).await.unwrap();
        let file2 = manager.stage_text(test_text).await.unwrap();
        
        // Should deduplicate to same file
        assert_eq!(file1.path, file2.path);
    }

    #[tokio::test]
    async fn test_image_staging() {
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(300),
            max_file_age: Duration::from_secs(900),
        };
        let manager = FileManager::new(config).await.unwrap();
        
        // Create a simple 1x1 PNG image
        let img = image::RgbImage::new(1, 1);
        let mut buffer = Vec::new();
        img.write_to(&mut std::io::Cursor::new(&mut buffer), image::ImageFormat::Png).unwrap();
        
        let staged = manager.stage_image(&buffer, "png").await.unwrap();
        
        assert!(staged.path.to_string_lossy().ends_with(".png"));
        assert!(staged.path.exists());
    }

    #[tokio::test]
    async fn test_cleanup_happens() {
        // Test that cleanup task is started
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(1), // Very short for testing
            max_file_age: Duration::from_secs(1),
        };
        let manager = FileManager::new(config).await.unwrap();
        
        // Stage a file
        let test_text = "Old file";
        let staged = manager.stage_text(test_text).await.unwrap();
        assert!(staged.path.exists());
        
        // Wait for cleanup to potentially run
        tokio::time::sleep(Duration::from_secs(2)).await;
        
        // File should still exist as it's not old enough
        // This just tests that cleanup doesn't crash
        assert!(staged.path.exists() || !staged.path.exists());
    }

    #[tokio::test]
    async fn test_staging_with_invalid_image() {
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(300),
            max_file_age: Duration::from_secs(900),
        };
        let manager = FileManager::new(config).await.unwrap();
        
        let invalid_data = b"Not an image";
        // stage_image should still succeed but without thumbnail
        let result = manager.stage_image(invalid_data, "png").await.unwrap();
        
        assert!(result.path.exists());
        assert!(result.thumbnail_path.is_none());
    }

    #[test]
    fn test_staged_file_properties() {
        let staged = StagedFile {
            path: PathBuf::from("/tmp/test.txt"),
            size: 1024,
            format: "txt".to_string(),
            created_at: SystemTime::now(),
            thumbnail_path: None,
        };
        
        assert_eq!(staged.size, 1024);
        assert_eq!(staged.format, "txt");
        assert!(staged.thumbnail_path.is_none());
    }

    #[tokio::test]
    async fn test_concurrent_staging() {
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(300),
            max_file_age: Duration::from_secs(900),
        };
        let manager = Arc::new(FileManager::new(config).await.unwrap());
        
        let handles: Vec<_> = (0..10)
            .map(|i| {
                let mgr = manager.clone();
                tokio::spawn(async move {
                    let text = format!("Content {}", i);
                    mgr.stage_text(&text).await
                })
            })
            .collect();
        
        let results: Vec<_> = futures::future::join_all(handles).await;
        
        // All operations should succeed
        for result in results {
            assert!(result.unwrap().is_ok());
        }
    }

    #[tokio::test]
    async fn test_hash_deduplication() {
        let temp_dir = tempdir().unwrap();
        let config = FileManagerConfig {
            staging_dir: temp_dir.path().to_path_buf(),
            cleanup_interval: Duration::from_secs(300),
            max_file_age: Duration::from_secs(900),
        };
        let manager = FileManager::new(config).await.unwrap();
        
        // Test that same content produces same hash and file
        let text = "Hello, deduplication!";
        let file1 = manager.stage_text(text).await.unwrap();
        let file2 = manager.stage_text(text).await.unwrap();
        
        assert_eq!(file1.path, file2.path);
    }
}