toasty_driver_integration_suite/
test.rs1use std::{
2 error::Error,
3 sync::{Arc, Mutex, RwLock},
4};
5
6use toasty::{Db, schema::ModelSet};
7use tokio::runtime::Runtime;
8
9use crate::{ExecLog, Isolate, LoggingDriver, Setup};
10
11static TEST_LOCK: RwLock<()> = RwLock::new(());
15
16pub struct Test {
20 setup: Arc<dyn Setup>,
22
23 isolate: Isolate,
25
26 runtime: Option<Runtime>,
28
29 exec_log: ExecLog,
30
31 tables: Vec<String>,
33
34 serial: bool,
36}
37
38impl Test {
39 pub fn new(setup: Arc<dyn Setup>) -> Self {
40 let runtime = tokio::runtime::Builder::new_current_thread()
41 .enable_all()
42 .build()
43 .expect("failed to create Tokio runtime");
44
45 Test {
46 setup,
47 isolate: Isolate::new(),
48 runtime: Some(runtime),
49 exec_log: ExecLog::new(Arc::new(Mutex::new(Vec::new()))),
50 tables: vec![],
51 serial: false,
52 }
53 }
54
55 pub async fn try_setup_db(&mut self, models: ModelSet) -> toasty::Result<Db> {
57 self.try_setup_db_with(models, |_| {}).await
58 }
59
60 pub async fn try_setup_db_with(
64 &mut self,
65 models: ModelSet,
66 customize: impl FnOnce(&mut toasty::db::Builder),
67 ) -> toasty::Result<Db> {
68 let mut builder = toasty::Db::builder();
69 builder.models(models);
70
71 builder.table_name_prefix(&self.isolate.table_prefix());
73
74 customize(&mut builder);
76
77 let logging_driver = LoggingDriver::new(self.setup.driver());
79 let ops_log = logging_driver.ops_log_handle();
80 self.exec_log = ExecLog::new(ops_log);
81
82 let db = builder.build(logging_driver).await?;
84 db.push_schema().await?;
85
86 for table in &db.schema().db.tables {
87 self.tables.push(table.name.clone());
88 }
89
90 Ok(db)
91 }
92
93 pub async fn setup_db(&mut self, models: ModelSet) -> Db {
95 self.try_setup_db(models).await.unwrap()
96 }
97
98 pub async fn setup_db_with(
101 &mut self,
102 models: ModelSet,
103 customize: impl FnOnce(&mut toasty::db::Builder),
104 ) -> Db {
105 self.try_setup_db_with(models, customize).await.unwrap()
106 }
107
108 pub fn capability(&self) -> &'static toasty_core::driver::Capability {
110 self.setup.driver().capability()
111 }
112
113 pub fn log(&mut self) -> &mut ExecLog {
115 &mut self.exec_log
116 }
117
118 pub fn set_serial(&mut self, serial: bool) {
120 self.serial = serial;
121 }
122
123 pub fn run<R>(&mut self, f: impl AsyncFn(&mut Test) -> R)
125 where
126 R: Into<TestResult>,
127 {
128 let _guard: Box<dyn std::any::Any> = if self.serial {
131 Box::new(TEST_LOCK.write().unwrap_or_else(|e| e.into_inner()))
132 } else {
133 Box::new(TEST_LOCK.read().unwrap_or_else(|e| e.into_inner()))
134 };
135
136 let runtime = self.runtime.take().expect("runtime already consumed");
138 let f: std::pin::Pin<Box<dyn std::future::Future<Output = R>>> = Box::pin(f(self));
139 let result = runtime.block_on(f).into();
140
141 for table in &self.tables {
143 runtime.block_on(self.setup.delete_table(table));
144 }
145
146 if let Some(error) = result.error {
147 panic!("Driver test returned an error: {error}");
148 }
149
150 self.runtime = Some(runtime);
151 }
152}
153
154pub struct TestResult {
155 error: Option<Box<dyn Error>>,
156}
157
158impl From<()> for TestResult {
159 fn from(_: ()) -> Self {
160 TestResult { error: None }
161 }
162}
163
164impl<O, E> From<Result<O, E>> for TestResult
165where
166 E: Into<Box<dyn Error>>,
167{
168 fn from(value: Result<O, E>) -> Self {
169 TestResult {
170 error: value.err().map(Into::into),
171 }
172 }
173}