toasty_driver_integration_suite/
instrumented_driver.rs1use async_trait::async_trait;
2use std::{
3 borrow::Cow,
4 collections::VecDeque,
5 fmt,
6 sync::{
7 Arc, Mutex,
8 atomic::{AtomicBool, Ordering},
9 },
10};
11use toasty_core::{
12 Result, Schema,
13 driver::{Capability, ConnectContext, Connection, Driver, ExecResponse, Operation, Rows},
14 schema::{
15 db::{AppliedMigration, Migration},
16 diff,
17 },
18};
19
20#[derive(Debug, Clone)]
25pub enum Fault {
26 OperationFailed,
29
30 ConnectionLost,
36}
37
38#[derive(Debug)]
39pub struct DriverOp {
40 pub operation: Operation,
41 pub response: ExecResponse,
42}
43
44#[derive(Clone, Default)]
49pub struct InstrumentedHandle {
50 inner: Arc<InstrumentedState>,
51}
52
53#[derive(Default)]
54struct InstrumentedState {
55 ops_log: Mutex<Vec<DriverOp>>,
56 faults: Mutex<VecDeque<Fault>>,
57}
58
59impl InstrumentedHandle {
60 pub fn len(&self) -> usize {
62 self.inner.ops_log.lock().unwrap().len()
63 }
64
65 pub fn is_empty(&self) -> bool {
67 self.inner.ops_log.lock().unwrap().is_empty()
68 }
69
70 pub fn clear(&self) {
72 self.inner.ops_log.lock().unwrap().clear();
73 }
74
75 #[track_caller]
77 pub fn pop(&self) -> (Operation, ExecResponse) {
78 let mut ops = self.inner.ops_log.lock().unwrap();
79 if ops.is_empty() {
80 panic!("no operations in log");
81 }
82 let driver_op = ops.remove(0);
83 (driver_op.operation, driver_op.response)
84 }
85
86 #[track_caller]
87 pub fn pop_op(&self) -> Operation {
88 self.pop().0
89 }
90
91 #[track_caller]
93 pub fn pop_last(&self) -> (Operation, ExecResponse) {
94 let mut ops = self.inner.ops_log.lock().unwrap();
95 let Some(driver_op) = ops.pop() else {
96 panic!("no operations in log");
97 };
98 (driver_op.operation, driver_op.response)
99 }
100
101 #[track_caller]
102 pub fn pop_last_op(&self) -> Operation {
103 self.pop_last().0
104 }
105
106 pub fn inject_fault(&self, fault: Fault) {
109 self.inner
110 .faults
111 .lock()
112 .expect("Failed to acquire faults lock")
113 .push_back(fault);
114 }
115}
116
117impl fmt::Debug for InstrumentedHandle {
118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119 let ops = self.inner.ops_log.lock().unwrap();
120 f.debug_struct("InstrumentedHandle")
121 .field("ops", &*ops)
122 .finish()
123 }
124}
125
126#[derive(Debug)]
130pub struct InstrumentedDriver {
131 inner: Box<dyn Driver>,
132 handle: InstrumentedHandle,
133 capability: Option<&'static Capability>,
135}
136
137impl InstrumentedDriver {
138 pub fn new(driver: Box<dyn Driver>) -> Self {
139 Self {
140 inner: driver,
141 handle: InstrumentedHandle::default(),
142 capability: None,
143 }
144 }
145
146 pub fn with_capability(mut self, capability: &'static Capability) -> Self {
150 self.capability = Some(capability);
151 self
152 }
153
154 pub fn handle(&self) -> InstrumentedHandle {
157 self.handle.clone()
158 }
159}
160
161#[async_trait]
162impl Driver for InstrumentedDriver {
163 fn url(&self) -> Cow<'_, str> {
164 self.inner.url()
165 }
166
167 fn capability(&self) -> &'static Capability {
168 self.capability.unwrap_or_else(|| self.inner.capability())
169 }
170
171 async fn connect(&self, cx: &ConnectContext) -> Result<Box<dyn Connection>> {
172 Ok(Box::new(InstrumentedConnection {
173 inner: self.inner.connect(cx).await?,
174 handle: self.handle.clone(),
175 valid: AtomicBool::new(true),
176 }))
177 }
178
179 fn generate_migration(&self, schema_diff: &diff::Schema<'_>) -> Migration {
180 self.inner.generate_migration(schema_diff)
181 }
182
183 async fn reset_db(&self) -> Result<()> {
184 self.inner.reset_db().await
185 }
186}
187
188#[derive(Debug)]
191pub struct InstrumentedConnection {
192 inner: Box<dyn Connection>,
194
195 handle: InstrumentedHandle,
197
198 valid: AtomicBool,
203}
204
205#[async_trait]
206impl Connection for InstrumentedConnection {
207 async fn exec(&mut self, schema: &Arc<Schema>, operation: Operation) -> Result<ExecResponse> {
208 let fault = self
211 .handle
212 .inner
213 .faults
214 .lock()
215 .expect("Failed to acquire faults lock")
216 .pop_front();
217 if let Some(fault) = fault {
218 match fault {
219 Fault::OperationFailed => {
220 return Err(toasty_core::Error::driver_operation_failed(
221 std::io::Error::other("injected operation failure"),
222 ));
223 }
224 Fault::ConnectionLost => {
225 self.valid.store(false, Ordering::Release);
226 return Err(toasty_core::Error::connection_lost(std::io::Error::other(
227 "injected connection-lost fault",
228 )));
229 }
230 }
231 }
232
233 let operation_clone = operation.clone();
235
236 let mut response = self.inner.exec(schema, operation).await?;
238
239 let duplicated_response = duplicate_response_mut(&mut response).await?;
241
242 let driver_op = DriverOp {
244 operation: operation_clone,
245 response: duplicated_response,
246 };
247
248 self.handle
249 .inner
250 .ops_log
251 .lock()
252 .expect("Failed to acquire ops log lock")
253 .push(driver_op);
254
255 Ok(response)
256 }
257
258 async fn push_schema(&mut self, schema: &Schema) -> Result<()> {
259 self.inner.push_schema(schema).await
260 }
261
262 async fn applied_migrations(&mut self) -> Result<Vec<AppliedMigration>> {
263 self.inner.applied_migrations().await
264 }
265
266 async fn apply_migration(&mut self, id: u64, name: &str, migration: &Migration) -> Result<()> {
267 self.inner.apply_migration(id, name, migration).await
268 }
269
270 fn is_valid(&self) -> bool {
271 self.valid.load(Ordering::Acquire) && self.inner.is_valid()
272 }
273
274 async fn ping(&mut self) -> Result<()> {
275 let fault = self
279 .handle
280 .inner
281 .faults
282 .lock()
283 .expect("Failed to acquire faults lock")
284 .pop_front();
285 if let Some(fault) = fault {
286 match fault {
287 Fault::OperationFailed => {
288 return Err(toasty_core::Error::driver_operation_failed(
289 std::io::Error::other("injected operation failure"),
290 ));
291 }
292 Fault::ConnectionLost => {
293 self.valid.store(false, Ordering::Release);
294 return Err(toasty_core::Error::connection_lost(std::io::Error::other(
295 "injected connection-lost fault",
296 )));
297 }
298 }
299 }
300 self.inner.ping().await
301 }
302}
303
304async fn duplicate_response_mut(response: &mut ExecResponse) -> Result<ExecResponse> {
307 let values = match &mut response.values {
308 Rows::Count(count) => Rows::Count(*count),
309 Rows::Value(_) => todo!(),
310 Rows::Stream(stream) => {
311 let duplicated_stream = stream.dup().await?;
313 Rows::Stream(duplicated_stream)
314 }
315 };
316
317 Ok(ExecResponse::from_rows(values))
318}