tempo_cli/
test_utils.rs

1use anyhow::Result;
2use std::path::PathBuf;
3use tempfile::{TempDir, NamedTempFile};
4use crate::db::{Database, initialize_database};
5
6/// Test utilities for setting up isolated test environments
7pub struct TestContext {
8    pub temp_dir: TempDir,
9    pub db_path: PathBuf,
10    pub database: Database,
11    _temp_file: NamedTempFile, // Keep the file alive
12}
13
14impl TestContext {
15    /// Create a new isolated test context with a temporary database
16    pub fn new() -> Result<Self> {
17        let temp_dir = tempfile::tempdir()?;
18        let temp_file = NamedTempFile::new_in(&temp_dir)?;
19        let db_path = temp_file.path().to_path_buf();
20        
21        // Initialize database with schema
22        let database = initialize_database(&db_path)?;
23        
24        Ok(Self {
25            temp_dir,
26            db_path,
27            database,
28            _temp_file: temp_file,
29        })
30    }
31
32    /// Get the database connection
33    pub fn connection(&self) -> &rusqlite::Connection {
34        &self.database.connection
35    }
36
37    /// Create a temporary project directory
38    pub fn create_temp_project_dir(&self) -> Result<PathBuf> {
39        let project_dir = self.temp_dir.path().join("test_project");
40        std::fs::create_dir_all(&project_dir)?;
41        Ok(project_dir)
42    }
43
44    /// Create a temporary Git repository for testing
45    pub fn create_temp_git_repo(&self) -> Result<PathBuf> {
46        let git_dir = self.temp_dir.path().join("git_project");
47        std::fs::create_dir_all(&git_dir)?;
48        
49        // Create .git directory to simulate Git repo
50        let git_meta = git_dir.join(".git");
51        std::fs::create_dir_all(&git_meta)?;
52        
53        // Create basic git files
54        std::fs::write(git_meta.join("HEAD"), "ref: refs/heads/main\n")?;
55        std::fs::write(git_meta.join("config"), "[core]\n\trepositoryformatversion = 0\n")?;
56        
57        Ok(git_dir)
58    }
59
60    /// Create a temporary Tempo-tracked project directory  
61    pub fn create_temp_tempo_project(&self) -> Result<PathBuf> {
62        let tempo_dir = self.temp_dir.path().join("tempo_project");
63        std::fs::create_dir_all(&tempo_dir)?;
64        
65        // Create .tempo marker file
66        std::fs::write(tempo_dir.join(".tempo"), "")?;
67        
68        Ok(tempo_dir)
69    }
70}
71
72/// Helper for testing database operations
73pub fn with_test_db<F>(test_fn: F) 
74where
75    F: FnOnce(&TestContext) -> Result<()>,
76{
77    let ctx = TestContext::new().expect("Failed to create test context");
78    test_fn(&ctx).expect("Test function failed");
79}
80
81/// Helper for async tests with database
82pub async fn with_test_db_async<F, Fut>(test_fn: F) 
83where
84    F: FnOnce(TestContext) -> Fut,
85    Fut: std::future::Future<Output = Result<()>>,
86{
87    let ctx = TestContext::new().expect("Failed to create test context");
88    test_fn(ctx).await.expect("Async test function failed");
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn test_context_creation() {
97        let ctx = TestContext::new().unwrap();
98        assert!(ctx.db_path.exists());
99        assert!(ctx.temp_dir.path().exists());
100    }
101
102    #[test]
103    fn test_temp_directories() {
104        let ctx = TestContext::new().unwrap();
105        
106        let project_dir = ctx.create_temp_project_dir().unwrap();
107        assert!(project_dir.exists());
108        assert!(project_dir.is_dir());
109        
110        let git_dir = ctx.create_temp_git_repo().unwrap();
111        assert!(git_dir.exists());
112        assert!(git_dir.join(".git").exists());
113        
114        let tempo_dir = ctx.create_temp_tempo_project().unwrap();
115        assert!(tempo_dir.exists());
116        assert!(tempo_dir.join(".tempo").exists());
117    }
118
119    #[test]
120    fn test_with_test_db() {
121        with_test_db(|ctx| {
122            // Test database connection
123            let result = ctx.connection().execute(
124                "CREATE TABLE test_table (id INTEGER PRIMARY KEY)",
125                [],
126            );
127            assert!(result.is_ok());
128            Ok(())
129        });
130    }
131
132    #[tokio::test]
133    async fn test_with_test_db_async() {
134        with_test_db_async(|ctx| async move {
135            // Test async operations
136            let project_dir = ctx.create_temp_project_dir()?;
137            assert!(project_dir.exists());
138            Ok(())
139        }).await;
140    }
141}