Files
ImageApi/src/testhelpers.rs
Cameron 0d05033b38 Add comprehensive testing for preview clip and status handling
- Implement unit tests for PreviewClipRequest/PreviewStatusRequest serialization and deserialization.
- Add tests for PreviewDao (insert, update, batch retrieval, and status-based queries).
- Extend Actix-web integration tests for `/video/preview/status` endpoint scenarios.
- Introduce in-memory TestPreviewDao for mock database interactions.
- Update README with new config parameters for preview clips.
2026-02-26 10:06:21 -05:00

171 lines
4.5 KiB
Rust

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<Vec<User>>,
}
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<User> {
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<User> {
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<BoxBody> {
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<HashMap<String, VideoPreviewClip>>,
next_id: StdMutex<i32>,
}
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<f32>,
size: Option<i32>,
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<Option<VideoPreviewClip>, DbError> {
Ok(self.clips.lock().unwrap().get(file_path_val).cloned())
}
fn get_previews_batch(
&mut self,
_context: &opentelemetry::Context,
file_paths: &[String],
) -> Result<Vec<VideoPreviewClip>, 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<Vec<VideoPreviewClip>, DbError> {
let clips = self.clips.lock().unwrap();
Ok(clips
.values()
.filter(|c| c.status == status_val)
.cloned()
.collect())
}
}