use actix_web::{ HttpResponse, body::{BoxBody, MessageBody}, }; use crate::database::models::{User, VideoPreviewClip}; use crate::database::{DbError, DbErrorKind, PreviewDao, UserDao}; use std::cell::RefCell; use std::collections::HashMap; use std::option::Option; use std::sync::Mutex as StdMutex; pub struct TestUserDao { pub user_map: RefCell>, } impl TestUserDao { pub fn new() -> Self { Self { user_map: RefCell::new(Vec::new()), } } } impl UserDao for TestUserDao { fn create_user(&mut self, username: &str, password: &str) -> Option { let u = User { id: (self.user_map.borrow().len() + 1) as i32, username: username.to_string(), password: password.to_string(), }; self.user_map.borrow_mut().push(u.clone()); Some(u) } fn get_user(&mut self, user: &str, pass: &str) -> Option { match self .user_map .borrow() .iter() .find(|&u| u.username == user && u.password == pass) { Some(u) => { let copy = (*u).clone(); Some(copy) } None => None, } } fn user_exists(&mut self, user: &str) -> bool { self.user_map.borrow().iter().any(|u| u.username == user) } } pub trait BodyReader { fn read_to_str(self) -> String; } impl BodyReader for HttpResponse { fn read_to_str(self) -> String { let body = self.into_body().try_into_bytes().unwrap(); std::str::from_utf8(&body).unwrap().to_string() } } pub struct TestPreviewDao { pub clips: StdMutex>, next_id: StdMutex, } impl TestPreviewDao { pub fn new() -> Self { Self { clips: StdMutex::new(HashMap::new()), next_id: StdMutex::new(1), } } } impl PreviewDao for TestPreviewDao { fn insert_preview( &mut self, _context: &opentelemetry::Context, file_path_val: &str, status_val: &str, ) -> Result<(), DbError> { let mut clips = self.clips.lock().unwrap(); // insert_or_ignore semantics: skip if key already exists if clips.contains_key(file_path_val) { return Ok(()); } let mut id = self.next_id.lock().unwrap(); let now = chrono::Utc::now().to_rfc3339(); clips.insert( file_path_val.to_string(), VideoPreviewClip { id: *id, file_path: file_path_val.to_string(), status: status_val.to_string(), duration_seconds: None, file_size_bytes: None, error_message: None, created_at: now.clone(), updated_at: now, }, ); *id += 1; Ok(()) } fn update_status( &mut self, _context: &opentelemetry::Context, file_path_val: &str, status_val: &str, duration: Option, size: Option, error: Option<&str>, ) -> Result<(), DbError> { let mut clips = self.clips.lock().unwrap(); if let Some(clip) = clips.get_mut(file_path_val) { clip.status = status_val.to_string(); clip.duration_seconds = duration; clip.file_size_bytes = size; clip.error_message = error.map(|s| s.to_string()); clip.updated_at = chrono::Utc::now().to_rfc3339(); Ok(()) } else { Err(DbError { kind: DbErrorKind::UpdateError, }) } } fn get_preview( &mut self, _context: &opentelemetry::Context, file_path_val: &str, ) -> Result, DbError> { Ok(self.clips.lock().unwrap().get(file_path_val).cloned()) } fn get_previews_batch( &mut self, _context: &opentelemetry::Context, file_paths: &[String], ) -> Result, DbError> { let clips = self.clips.lock().unwrap(); Ok(file_paths .iter() .filter_map(|p| clips.get(p).cloned()) .collect()) } fn get_by_status( &mut self, _context: &opentelemetry::Context, status_val: &str, ) -> Result, DbError> { let clips = self.clips.lock().unwrap(); Ok(clips .values() .filter(|c| c.status == status_val) .cloned() .collect()) } }