1use anyhow::Result;
2use std::path::PathBuf;
3use tempfile::{TempDir, NamedTempFile};
4use crate::db::{Database, initialize_database};
5
6pub struct TestContext {
8 pub temp_dir: TempDir,
9 pub db_path: PathBuf,
10 pub database: Database,
11 _temp_file: NamedTempFile, }
13
14impl TestContext {
15 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 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 pub fn connection(&self) -> &rusqlite::Connection {
34 &self.database.connection
35 }
36
37 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 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 let git_meta = git_dir.join(".git");
51 std::fs::create_dir_all(&git_meta)?;
52
53 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 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 std::fs::write(tempo_dir.join(".tempo"), "")?;
67
68 Ok(tempo_dir)
69 }
70}
71
72pub 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
81pub 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 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 let project_dir = ctx.create_temp_project_dir()?;
137 assert!(project_dir.exists());
138 Ok(())
139 }).await;
140 }
141}