1use crate::db::{initialize_database, Database};
2use anyhow::Result;
3use std::path::PathBuf;
4use tempfile::{NamedTempFile, TempDir};
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(
56 git_meta.join("config"),
57 "[core]\n\trepositoryformatversion = 0\n",
58 )?;
59
60 Ok(git_dir)
61 }
62
63 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 std::fs::write(tempo_dir.join(".tempo"), "")?;
70
71 Ok(tempo_dir)
72 }
73}
74
75pub 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
84pub 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 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 let project_dir = ctx.create_temp_project_dir()?;
139 assert!(project_dir.exists());
140 Ok(())
141 })
142 .await;
143 }
144}