472 lines
17 KiB
Rust
472 lines
17 KiB
Rust
use anyhow::Result;
|
|
use std::sync::Arc;
|
|
use uuid::Uuid;
|
|
|
|
use readur::{
|
|
AppState,
|
|
db::Database,
|
|
config::Config,
|
|
models::{CreateUser, UserRole, CreateIgnoredFile},
|
|
};
|
|
|
|
async fn create_test_app_state() -> Result<Arc<AppState>> {
|
|
let config = Config::from_env().unwrap_or_else(|_| {
|
|
let database_url = std::env::var("DATABASE_URL")
|
|
.or_else(|_| std::env::var("TEST_DATABASE_URL"))
|
|
.unwrap_or_else(|_| "postgresql://readur:readur@localhost:5432/readur".to_string());
|
|
|
|
Config {
|
|
database_url,
|
|
server_address: "127.0.0.1:0".to_string(),
|
|
jwt_secret: "test_secret".to_string(),
|
|
upload_path: "./test_uploads".to_string(),
|
|
watch_folder: "./test_watch".to_string(),
|
|
user_watch_base_dir: "./user_watch".to_string(),
|
|
enable_per_user_watch: false,
|
|
allowed_file_types: vec!["pdf".to_string(), "txt".to_string()],
|
|
watch_interval_seconds: Some(30),
|
|
file_stability_check_ms: Some(500),
|
|
max_file_age_hours: None,
|
|
ocr_language: "eng".to_string(),
|
|
concurrent_ocr_jobs: 1,
|
|
ocr_timeout_seconds: 30,
|
|
max_file_size_mb: 10,
|
|
memory_limit_mb: 256,
|
|
cpu_priority: "normal".to_string(),
|
|
oidc_enabled: false,
|
|
oidc_client_id: None,
|
|
oidc_client_secret: None,
|
|
oidc_issuer_url: None,
|
|
oidc_redirect_uri: None,
|
|
s3_enabled: false,
|
|
s3_config: None,
|
|
}
|
|
});
|
|
|
|
let db = Database::new(&config.database_url).await?;
|
|
|
|
// Create file service
|
|
let storage_config = readur::storage::StorageConfig::Local { upload_path: config.upload_path.clone() };
|
|
let storage_backend = readur::storage::factory::create_storage_backend(storage_config).await?;
|
|
let file_service = Arc::new(readur::services::file_service::FileService::with_storage(config.upload_path.clone(), storage_backend));
|
|
|
|
let queue_service = Arc::new(readur::ocr::queue::OcrQueueService::new(db.clone(), db.pool.clone(), 1, file_service.clone()));
|
|
|
|
Ok(Arc::new(AppState {
|
|
db: db.clone(),
|
|
config,
|
|
file_service,
|
|
webdav_scheduler: None,
|
|
source_scheduler: None,
|
|
queue_service,
|
|
oidc_client: None,
|
|
sync_progress_tracker: std::sync::Arc::new(readur::services::sync_progress_tracker::SyncProgressTracker::new()),
|
|
user_watch_service: None,
|
|
}))
|
|
}
|
|
|
|
fn create_test_user_with_suffix(suffix: &str) -> CreateUser {
|
|
CreateUser {
|
|
username: format!("testuser_{}", suffix),
|
|
email: format!("test_{}@example.com", suffix),
|
|
password: "test_password".to_string(),
|
|
role: Some(UserRole::User),
|
|
}
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ignored_files_crud_operations() -> Result<()> {
|
|
let state = create_test_app_state().await?;
|
|
let user = create_test_user_with_suffix(&format!("ignored_{}", Uuid::new_v4().simple()));
|
|
|
|
// Create user in database
|
|
let created_user = state.db.create_user(user).await?;
|
|
let user_id = created_user.id;
|
|
|
|
// Test creating an ignored file
|
|
let ignored_file = CreateIgnoredFile {
|
|
file_hash: "test_hash_123".to_string(),
|
|
filename: "test_file.pdf".to_string(),
|
|
original_filename: "original_test_file.pdf".to_string(),
|
|
file_path: "/test/path/test_file.pdf".to_string(),
|
|
file_size: 1024,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some("webdav".to_string()),
|
|
source_path: Some("/webdav/test_file.pdf".to_string()),
|
|
source_identifier: Some("test-webdav".to_string()),
|
|
ignored_by: user_id,
|
|
reason: Some("test deletion".to_string()),
|
|
};
|
|
|
|
// Test that we can create and retrieve ignored files
|
|
let created = readur::db::ignored_files::create_ignored_file(&state.db.pool, ignored_file).await?;
|
|
assert_eq!(created.file_hash, "test_hash_123");
|
|
assert_eq!(created.filename, "test_file.pdf");
|
|
assert_eq!(created.ignored_by, user_id);
|
|
|
|
// Test listing ignored files
|
|
let query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: None,
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: None,
|
|
};
|
|
|
|
let ignored_files = readur::db::ignored_files::list_ignored_files(&state.db.pool, user_id, &query).await?;
|
|
assert_eq!(ignored_files.len(), 1);
|
|
assert_eq!(ignored_files[0].file_hash, "test_hash_123");
|
|
|
|
// Test is_file_ignored function
|
|
let is_ignored = readur::db::ignored_files::is_file_ignored(
|
|
&state.db.pool,
|
|
"test_hash_123",
|
|
Some("webdav"),
|
|
Some("/webdav/test_file.pdf")
|
|
).await?;
|
|
assert!(is_ignored);
|
|
|
|
// Test deleting ignored file
|
|
let deleted = readur::db::ignored_files::delete_ignored_file(&state.db.pool, created.id, user_id).await?;
|
|
assert!(deleted);
|
|
|
|
// Verify it's deleted
|
|
let ignored_files_after = readur::db::ignored_files::list_ignored_files(&state.db.pool, user_id, &query).await?;
|
|
assert_eq!(ignored_files_after.len(), 0);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ignored_files_filtering() -> Result<()> {
|
|
let state = create_test_app_state().await?;
|
|
let user = create_test_user_with_suffix(&format!("filter_{}", Uuid::new_v4().simple()));
|
|
|
|
// Create user in database
|
|
let created_user = state.db.create_user(user).await?;
|
|
let user_id = created_user.id;
|
|
|
|
// Create multiple ignored files with different properties
|
|
let ignored_files = vec![
|
|
CreateIgnoredFile {
|
|
file_hash: "hash1".to_string(),
|
|
filename: "webdav_file.pdf".to_string(),
|
|
original_filename: "webdav_file.pdf".to_string(),
|
|
file_path: "/path1/webdav_file.pdf".to_string(),
|
|
file_size: 1024,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some("webdav".to_string()),
|
|
source_path: Some("/webdav/file.pdf".to_string()),
|
|
source_identifier: Some("webdav-1".to_string()),
|
|
ignored_by: user_id,
|
|
reason: Some("deleted".to_string()),
|
|
},
|
|
CreateIgnoredFile {
|
|
file_hash: "hash2".to_string(),
|
|
filename: "s3_file.pdf".to_string(),
|
|
original_filename: "s3_file.pdf".to_string(),
|
|
file_path: "/path2/s3_file.pdf".to_string(),
|
|
file_size: 2048,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some("s3".to_string()),
|
|
source_path: Some("/s3/file.pdf".to_string()),
|
|
source_identifier: Some("s3-bucket".to_string()),
|
|
ignored_by: user_id,
|
|
reason: Some("deleted".to_string()),
|
|
}
|
|
];
|
|
|
|
// Create ignored files
|
|
for ignored_file in ignored_files {
|
|
readur::db::ignored_files::create_ignored_file(&state.db.pool, ignored_file).await?;
|
|
}
|
|
|
|
// Test filtering by source type
|
|
let webdav_query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: Some("webdav".to_string()),
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: None,
|
|
};
|
|
|
|
let webdav_files = readur::db::ignored_files::list_ignored_files(&state.db.pool, user_id, &webdav_query).await?;
|
|
assert_eq!(webdav_files.len(), 1);
|
|
assert_eq!(webdav_files[0].source_type, Some("webdav".to_string()));
|
|
|
|
// Test filtering by filename
|
|
let filename_query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: None,
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: Some("s3_file".to_string()),
|
|
};
|
|
|
|
let s3_files = readur::db::ignored_files::list_ignored_files(&state.db.pool, user_id, &filename_query).await?;
|
|
assert_eq!(s3_files.len(), 1);
|
|
assert!(s3_files[0].filename.contains("s3_file"));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ignored_files_user_isolation() -> Result<()> {
|
|
let state = create_test_app_state().await?;
|
|
|
|
// Create two different users
|
|
let user1 = create_test_user_with_suffix(&format!("user1_{}", Uuid::new_v4().simple()));
|
|
let user2 = create_test_user_with_suffix(&format!("user2_{}", Uuid::new_v4().simple()));
|
|
|
|
let created_user1 = state.db.create_user(user1).await?;
|
|
let created_user2 = state.db.create_user(user2).await?;
|
|
|
|
let user1_id = created_user1.id;
|
|
let user2_id = created_user2.id;
|
|
|
|
// Create ignored file for user1
|
|
let ignored_file1 = CreateIgnoredFile {
|
|
file_hash: "user1_hash".to_string(),
|
|
filename: "user1_file.pdf".to_string(),
|
|
original_filename: "user1_file.pdf".to_string(),
|
|
file_path: "/user1/file.pdf".to_string(),
|
|
file_size: 1024,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some("webdav".to_string()),
|
|
source_path: Some("/webdav/user1_file.pdf".to_string()),
|
|
source_identifier: Some("webdav-1".to_string()),
|
|
ignored_by: user1_id,
|
|
reason: Some("deleted by user1".to_string()),
|
|
};
|
|
|
|
// Create ignored file for user2
|
|
let ignored_file2 = CreateIgnoredFile {
|
|
file_hash: "user2_hash".to_string(),
|
|
filename: "user2_file.pdf".to_string(),
|
|
original_filename: "user2_file.pdf".to_string(),
|
|
file_path: "/user2/file.pdf".to_string(),
|
|
file_size: 2048,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some("s3".to_string()),
|
|
source_path: Some("/s3/user2_file.pdf".to_string()),
|
|
source_identifier: Some("s3-bucket".to_string()),
|
|
ignored_by: user2_id,
|
|
reason: Some("deleted by user2".to_string()),
|
|
};
|
|
|
|
readur::db::ignored_files::create_ignored_file(&state.db.pool, ignored_file1).await?;
|
|
readur::db::ignored_files::create_ignored_file(&state.db.pool, ignored_file2).await?;
|
|
|
|
let query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: None,
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: None,
|
|
};
|
|
|
|
// User1 should only see their own ignored files
|
|
let user1_files = readur::db::ignored_files::list_ignored_files(&state.db.pool, user1_id, &query).await?;
|
|
assert_eq!(user1_files.len(), 1);
|
|
assert_eq!(user1_files[0].ignored_by, user1_id);
|
|
assert_eq!(user1_files[0].filename, "user1_file.pdf");
|
|
|
|
// User2 should only see their own ignored files
|
|
let user2_files = readur::db::ignored_files::list_ignored_files(&state.db.pool, user2_id, &query).await?;
|
|
assert_eq!(user2_files.len(), 1);
|
|
assert_eq!(user2_files[0].ignored_by, user2_id);
|
|
assert_eq!(user2_files[0].filename, "user2_file.pdf");
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ignored_files_bulk_operations() -> Result<()> {
|
|
let state = create_test_app_state().await?;
|
|
let user = create_test_user_with_suffix(&format!("bulk_{}", Uuid::new_v4().simple()));
|
|
|
|
// Create user in database
|
|
let created_user = state.db.create_user(user).await?;
|
|
let user_id = created_user.id;
|
|
|
|
// Create multiple ignored files
|
|
let mut created_ids = Vec::new();
|
|
for i in 0..3 {
|
|
let ignored_file = CreateIgnoredFile {
|
|
file_hash: format!("bulk_hash_{}", i),
|
|
filename: format!("bulk_file_{}.pdf", i),
|
|
original_filename: format!("bulk_file_{}.pdf", i),
|
|
file_path: format!("/bulk/file_{}.pdf", i),
|
|
file_size: 1024 * (i + 1) as i64,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some("webdav".to_string()),
|
|
source_path: Some(format!("/webdav/bulk_{}.pdf", i)),
|
|
source_identifier: Some("webdav-bulk".to_string()),
|
|
ignored_by: user_id,
|
|
reason: Some("bulk test".to_string()),
|
|
};
|
|
|
|
let created = readur::db::ignored_files::create_ignored_file(&state.db.pool, ignored_file).await?;
|
|
created_ids.push(created.id);
|
|
}
|
|
|
|
// Test bulk delete
|
|
let deleted_count = readur::db::ignored_files::bulk_delete_ignored_files(
|
|
&state.db.pool,
|
|
created_ids.clone(),
|
|
user_id
|
|
).await?;
|
|
|
|
assert_eq!(deleted_count, 3);
|
|
|
|
// Verify all are deleted
|
|
let query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: None,
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: None,
|
|
};
|
|
|
|
let remaining_files = readur::db::ignored_files::list_ignored_files(&state.db.pool, user_id, &query).await?;
|
|
assert_eq!(remaining_files.len(), 0);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_create_ignored_file_from_document() -> Result<()> {
|
|
let state = create_test_app_state().await?;
|
|
let user = create_test_user_with_suffix(&format!("doc_{}", Uuid::new_v4().simple()));
|
|
|
|
// Create user in database
|
|
let created_user = state.db.create_user(user).await?;
|
|
let user_id = created_user.id;
|
|
|
|
// Create a test document first
|
|
let document = readur::models::Document {
|
|
id: Uuid::new_v4(),
|
|
filename: "test_document.pdf".to_string(),
|
|
original_filename: "test_document.pdf".to_string(),
|
|
file_path: "/uploads/test_document.pdf".to_string(),
|
|
file_size: 1024000,
|
|
mime_type: "application/pdf".to_string(),
|
|
content: Some("Test document content".to_string()),
|
|
ocr_text: Some("OCR text".to_string()),
|
|
ocr_confidence: Some(95.5),
|
|
ocr_word_count: Some(150),
|
|
ocr_processing_time_ms: Some(1200),
|
|
ocr_status: Some("completed".to_string()),
|
|
ocr_error: None,
|
|
ocr_completed_at: Some(chrono::Utc::now()),
|
|
ocr_retry_count: None,
|
|
ocr_failure_reason: None,
|
|
tags: vec!["test".to_string()],
|
|
created_at: chrono::Utc::now(),
|
|
updated_at: chrono::Utc::now(),
|
|
user_id,
|
|
file_hash: Some("document_hash_123".to_string()),
|
|
original_created_at: None,
|
|
original_modified_at: None,
|
|
source_path: None,
|
|
source_type: None,
|
|
source_id: None,
|
|
file_permissions: None,
|
|
file_owner: None,
|
|
file_group: None,
|
|
source_metadata: None,
|
|
};
|
|
|
|
// Insert document into database
|
|
let _document_id = state.db.create_document(document.clone()).await?;
|
|
|
|
// Test creating ignored file from document
|
|
let ignored_file = readur::db::ignored_files::create_ignored_file_from_document(
|
|
&state.db.pool,
|
|
document.id,
|
|
user_id,
|
|
Some("deleted by user".to_string()),
|
|
Some("webdav".to_string()),
|
|
Some("/webdav/test_document.pdf".to_string()),
|
|
Some("webdav-server".to_string()),
|
|
).await?;
|
|
|
|
assert!(ignored_file.is_some());
|
|
let ignored_file = ignored_file.unwrap();
|
|
assert_eq!(ignored_file.filename, document.filename);
|
|
assert_eq!(ignored_file.file_size, document.file_size);
|
|
assert_eq!(ignored_file.mime_type, document.mime_type);
|
|
assert_eq!(ignored_file.ignored_by, user_id);
|
|
assert_eq!(ignored_file.source_type, Some("webdav".to_string()));
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_ignored_files_count_functionality() -> Result<()> {
|
|
let state = create_test_app_state().await?;
|
|
let user = create_test_user_with_suffix(&format!("count_{}", Uuid::new_v4().simple()));
|
|
|
|
// Create user in database
|
|
let created_user = state.db.create_user(user).await?;
|
|
let user_id = created_user.id;
|
|
|
|
// Initially should have no ignored files
|
|
let query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: None,
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: None,
|
|
};
|
|
|
|
let count = readur::db::ignored_files::count_ignored_files(&state.db.pool, user_id, &query).await?;
|
|
assert_eq!(count, 0);
|
|
|
|
// Create some ignored files
|
|
for i in 0..5 {
|
|
let ignored_file = CreateIgnoredFile {
|
|
file_hash: format!("count_hash_{}", i),
|
|
filename: format!("count_file_{}.pdf", i),
|
|
original_filename: format!("count_file_{}.pdf", i),
|
|
file_path: format!("/count/file_{}.pdf", i),
|
|
file_size: 1024,
|
|
mime_type: "application/pdf".to_string(),
|
|
source_type: Some(if i % 2 == 0 { "webdav" } else { "s3" }.to_string()),
|
|
source_path: Some(format!("/source/file_{}.pdf", i)),
|
|
source_identifier: Some("test-source".to_string()),
|
|
ignored_by: user_id,
|
|
reason: Some("count test".to_string()),
|
|
};
|
|
|
|
readur::db::ignored_files::create_ignored_file(&state.db.pool, ignored_file).await?;
|
|
}
|
|
|
|
// Count should now be 5
|
|
let count = readur::db::ignored_files::count_ignored_files(&state.db.pool, user_id, &query).await?;
|
|
assert_eq!(count, 5);
|
|
|
|
// Test filtered count (only webdav)
|
|
let webdav_query = readur::models::IgnoredFilesQuery {
|
|
limit: Some(10),
|
|
offset: Some(0),
|
|
source_type: Some("webdav".to_string()),
|
|
source_identifier: None,
|
|
ignored_by: None,
|
|
filename: None,
|
|
};
|
|
|
|
let webdav_count = readur::db::ignored_files::count_ignored_files(&state.db.pool, user_id, &webdav_query).await?;
|
|
assert_eq!(webdav_count, 3); // Files 0, 2, 4 are webdav
|
|
|
|
Ok(())
|
|
}
|
|
|
|
|