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.
This commit is contained in:
Cameron
2026-02-26 10:06:21 -05:00
parent 842ed4ed66
commit 0d05033b38
6 changed files with 505 additions and 2 deletions

View File

@@ -3,9 +3,12 @@ use actix_web::{
body::{BoxBody, MessageBody},
};
use crate::database::{UserDao, models::User};
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>>,
@@ -62,3 +65,106 @@ impl BodyReader for HttpResponse<BoxBody> {
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())
}
}