tempo_cli/
test_utils.rs

1use crate::db::{initialize_database, Database};
2use anyhow::Result;
3use std::path::PathBuf;
4use tempfile::{NamedTempFile, TempDir};
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(
56            git_meta.join("config"),
57            "[core]\n\trepositoryformatversion = 0\n",
58        )?;
59
60        Ok(git_dir)
61    }
62
63    /// Create a temporary Tempo-tracked project directory  
64    pub fn create_temp_tempo_project(&self) -> Result<PathBuf> {
65        let tempo_dir = self.temp_dir.path().join("tempo_project");
66        std::fs::create_dir_all(&tempo_dir)?;
67
68        // Create .tempo marker file
69        std::fs::write(tempo_dir.join(".tempo"), "")?;
70
71        Ok(tempo_dir)
72    }
73}
74
75/// Helper for testing database operations
76pub fn with_test_db<F>(test_fn: F)
77where
78    F: FnOnce(&TestContext) -> Result<()>,
79{
80    let ctx = TestContext::new().expect("Failed to create test context");
81    test_fn(&ctx).expect("Test function failed");
82}
83
84/// Helper for async tests with database
85pub async fn with_test_db_async<F, Fut>(test_fn: F)
86where
87    F: FnOnce(TestContext) -> Fut,
88    Fut: std::future::Future<Output = Result<()>>,
89{
90    let ctx = TestContext::new().expect("Failed to create test context");
91    test_fn(ctx).await.expect("Async test function failed");
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_context_creation() {
100        let ctx = TestContext::new().unwrap();
101        assert!(ctx.db_path.exists());
102        assert!(ctx.temp_dir.path().exists());
103    }
104
105    #[test]
106    fn test_temp_directories() {
107        let ctx = TestContext::new().unwrap();
108
109        let project_dir = ctx.create_temp_project_dir().unwrap();
110        assert!(project_dir.exists());
111        assert!(project_dir.is_dir());
112
113        let git_dir = ctx.create_temp_git_repo().unwrap();
114        assert!(git_dir.exists());
115        assert!(git_dir.join(".git").exists());
116
117        let tempo_dir = ctx.create_temp_tempo_project().unwrap();
118        assert!(tempo_dir.exists());
119        assert!(tempo_dir.join(".tempo").exists());
120    }
121
122    #[test]
123    fn test_with_test_db() {
124        with_test_db(|ctx| {
125            // Test database connection
126            let result = ctx
127                .connection()
128                .execute("CREATE TABLE test_table (id INTEGER PRIMARY KEY)", []);
129            assert!(result.is_ok());
130            Ok(())
131        });
132    }
133
134    #[tokio::test]
135    async fn test_with_test_db_async() {
136        with_test_db_async(|ctx| async move {
137            // Test async operations
138            let project_dir = ctx.create_temp_project_dir()?;
139            assert!(project_dir.exists());
140            Ok(())
141        })
142        .await;
143    }
144}