fix(tests): fix broken unit tests for new ocr language feature
This commit is contained in:
parent
469ca29f5c
commit
ba246c825b
|
|
@ -241,12 +241,6 @@ version = "1.1.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||
|
||||
[[package]]
|
||||
name = "auto-future"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c1e7e457ea78e524f48639f551fd79703ac3f2237f5ecccdf4708f8a75ad373"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.4.0"
|
||||
|
|
@ -705,47 +699,13 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.7.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core 0.4.5",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit 0.7.3",
|
||||
"memchr",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_path_to_error",
|
||||
"serde_urlencoded",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tokio",
|
||||
"tower",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5"
|
||||
dependencies = [
|
||||
"axum-core 0.5.2",
|
||||
"axum-core",
|
||||
"bytes",
|
||||
"form_urlencoded",
|
||||
"futures-util",
|
||||
|
|
@ -755,7 +715,7 @@ dependencies = [
|
|||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"itoa",
|
||||
"matchit 0.8.4",
|
||||
"matchit",
|
||||
"memchr",
|
||||
"mime",
|
||||
"multer",
|
||||
|
|
@ -774,27 +734,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http 1.3.1",
|
||||
"http-body 1.0.1",
|
||||
"http-body-util",
|
||||
"mime",
|
||||
"pin-project-lite",
|
||||
"rustversion",
|
||||
"sync_wrapper 1.0.2",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-core"
|
||||
version = "0.5.2"
|
||||
|
|
@ -815,34 +754,6 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-test"
|
||||
version = "15.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac63648e380fd001402a02ec804e7686f9c4751f8cad85b7de0b53dae483a128"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"auto-future",
|
||||
"axum 0.7.9",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"http 1.3.1",
|
||||
"http-body-util",
|
||||
"hyper 1.6.0",
|
||||
"hyper-util",
|
||||
"mime",
|
||||
"pretty_assertions",
|
||||
"reserve-port",
|
||||
"rust-multipart-rfc7578_2",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"smallvec",
|
||||
"tokio",
|
||||
"tower",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.75"
|
||||
|
|
@ -1276,16 +1187,6 @@ version = "0.9.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "cookie"
|
||||
version = "0.18.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747"
|
||||
dependencies = [
|
||||
"time",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
|
@ -1525,12 +1426,6 @@ dependencies = [
|
|||
"syn 2.0.103",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diff"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
|
@ -2926,12 +2821,6 @@ dependencies = [
|
|||
"regex-automata 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94"
|
||||
|
||||
[[package]]
|
||||
name = "matchit"
|
||||
version = "0.8.4"
|
||||
|
|
@ -3612,16 +3501,6 @@ dependencies = [
|
|||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
|
||||
dependencies = [
|
||||
"diff",
|
||||
"yansi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.34"
|
||||
|
|
@ -3869,8 +3748,7 @@ dependencies = [
|
|||
"aws-credential-types",
|
||||
"aws-sdk-s3",
|
||||
"aws-types",
|
||||
"axum 0.8.4",
|
||||
"axum-test",
|
||||
"axum",
|
||||
"base64ct",
|
||||
"bcrypt",
|
||||
"chrono",
|
||||
|
|
@ -4088,15 +3966,6 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "reserve-port"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21918d6644020c6f6ef1993242989bf6d4952d2e025617744f184c02df51c356"
|
||||
dependencies = [
|
||||
"thiserror 2.0.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rfc6979"
|
||||
version = "0.3.1"
|
||||
|
|
@ -4182,22 +4051,6 @@ dependencies = [
|
|||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-multipart-rfc7578_2"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03b748410c0afdef2ebbe3685a6a862e2ee937127cdaae623336a459451c8d57"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"http 0.2.12",
|
||||
"mime",
|
||||
"mime_guess",
|
||||
"rand 0.8.5",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.25"
|
||||
|
|
@ -5699,7 +5552,7 @@ version = "9.0.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d047458f1b5b65237c2f6dc6db136945667f40a7668627b3490b9513a3d43a55"
|
||||
dependencies = [
|
||||
"axum 0.8.4",
|
||||
"axum",
|
||||
"base64 0.22.1",
|
||||
"mime_guess",
|
||||
"regex",
|
||||
|
|
@ -6395,12 +6248,6 @@ version = "0.13.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.0"
|
||||
|
|
|
|||
|
|
@ -70,7 +70,6 @@ tempfile = "3"
|
|||
wiremock = "0.6"
|
||||
tokio-test = "0.4"
|
||||
futures = "0.3"
|
||||
axum-test = "15"
|
||||
# Database testing dependencies
|
||||
testcontainers = "0.24"
|
||||
testcontainers-modules = { version = "0.12", features = ["postgres"] }
|
||||
|
|
|
|||
|
|
@ -1,137 +1,72 @@
|
|||
use readur::AppState;
|
||||
use readur::config::Config;
|
||||
use readur::db::Database;
|
||||
use readur::ocr::health::OcrHealthChecker;
|
||||
use readur::test_utils::TestContext;
|
||||
use axum::http::StatusCode;
|
||||
use axum_test::TestServer;
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use tower::ServiceExt;
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use tempfile::TempDir;
|
||||
use std::fs;
|
||||
use uuid::Uuid;
|
||||
|
||||
struct TestHarness {
|
||||
server: TestServer,
|
||||
_temp_dir: TempDir,
|
||||
user_id: Uuid,
|
||||
token: String,
|
||||
}
|
||||
|
||||
impl TestHarness {
|
||||
async fn new() -> Self {
|
||||
// Create temporary directory for tessdata
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
||||
let tessdata_path = temp_dir.path().join("tessdata");
|
||||
fs::create_dir_all(&tessdata_path).expect("Failed to create tessdata directory");
|
||||
|
||||
// Create mock language files
|
||||
let language_files = vec![
|
||||
"eng.traineddata",
|
||||
"spa.traineddata",
|
||||
"fra.traineddata",
|
||||
"deu.traineddata",
|
||||
"ita.traineddata",
|
||||
"por.traineddata",
|
||||
];
|
||||
|
||||
for file in language_files {
|
||||
fs::write(tessdata_path.join(file), "mock language data")
|
||||
.expect("Failed to create mock language file");
|
||||
}
|
||||
|
||||
// Set environment variable for tessdata path
|
||||
std::env::set_var("TESSDATA_PREFIX", &tessdata_path);
|
||||
|
||||
// Create test database
|
||||
let config = Config::from_env().expect("Failed to load config");
|
||||
let db = Database::new(&config.database_url)
|
||||
.await
|
||||
.expect("Failed to connect to database");
|
||||
|
||||
// Create test user
|
||||
let user_id = Uuid::new_v4();
|
||||
let username = format!("testuser_{}", user_id);
|
||||
let email = format!("{}@test.com", username);
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash) VALUES ($1, $2, $3, $4)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind(&username)
|
||||
.bind(&email)
|
||||
.bind("dummy_hash")
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.expect("Failed to create test user");
|
||||
|
||||
// Create user settings
|
||||
sqlx::query(
|
||||
"INSERT INTO settings (user_id, ocr_language) VALUES ($1, $2)"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("eng")
|
||||
.execute(&db.pool)
|
||||
.await
|
||||
.expect("Failed to create user settings");
|
||||
|
||||
// Create a shared OCR queue service
|
||||
let queue_service = Arc::new(readur::ocr::queue::OcrQueueService::new(
|
||||
db.clone(),
|
||||
db.get_pool().clone(),
|
||||
2
|
||||
));
|
||||
|
||||
// Create app state
|
||||
let app_state = Arc::new(AppState {
|
||||
db,
|
||||
config,
|
||||
webdav_scheduler: None,
|
||||
source_scheduler: None,
|
||||
queue_service,
|
||||
oidc_client: None,
|
||||
});
|
||||
|
||||
// Create test server with router
|
||||
let app = readur::test_utils::create_test_app(app_state.clone());
|
||||
let server = TestServer::new(app).expect("Failed to create test server");
|
||||
|
||||
// Generate a test token (simplified for testing)
|
||||
let token = format!("test_token_{}", user_id);
|
||||
|
||||
Self {
|
||||
server,
|
||||
_temp_dir: temp_dir,
|
||||
user_id,
|
||||
token,
|
||||
}
|
||||
}
|
||||
|
||||
async fn cleanup(&self) {
|
||||
// Clean up test user
|
||||
sqlx::query("DELETE FROM users WHERE id = $1")
|
||||
.bind(self.user_id)
|
||||
.execute(&self.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.await
|
||||
.expect("Failed to cleanup test user");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_available_languages_success() {
|
||||
let harness = TestHarness::new().await;
|
||||
// Create temporary directory for tessdata
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
||||
let tessdata_path = temp_dir.path().join("tessdata");
|
||||
fs::create_dir_all(&tessdata_path).expect("Failed to create tessdata directory");
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.get("/api/ocr/languages")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.await;
|
||||
// Create mock language files
|
||||
let language_files = vec![
|
||||
"eng.traineddata",
|
||||
"spa.traineddata",
|
||||
"fra.traineddata",
|
||||
"deu.traineddata",
|
||||
"ita.traineddata",
|
||||
"por.traineddata",
|
||||
];
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
for file in language_files {
|
||||
fs::write(tessdata_path.join(file), "mock language data")
|
||||
.expect("Failed to create mock language file");
|
||||
}
|
||||
|
||||
let body: serde_json::Value = response.json();
|
||||
assert!(body.get("languages").is_some());
|
||||
// Set environment variable for tessdata path
|
||||
std::env::set_var("TESSDATA_PREFIX", &tessdata_path);
|
||||
|
||||
let languages = body["languages"].as_array().unwrap();
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
// Create test user and get token
|
||||
let auth_helper = readur::test_utils::TestAuthHelper::new(ctx.app().clone());
|
||||
let mut test_user = auth_helper.create_test_user().await;
|
||||
let user_id = test_user.user_response.id;
|
||||
let token = test_user.login(&auth_helper).await.unwrap();
|
||||
|
||||
// Create user settings
|
||||
sqlx::query(
|
||||
"INSERT INTO settings (user_id, ocr_language) VALUES ($1, $2)
|
||||
ON CONFLICT (user_id) DO UPDATE SET ocr_language = $2"
|
||||
)
|
||||
.bind(user_id)
|
||||
.bind("eng")
|
||||
.execute(&ctx.state().db.pool)
|
||||
.await
|
||||
.expect("Failed to create user settings");
|
||||
|
||||
let request = Request::builder()
|
||||
.method("GET")
|
||||
.uri("/api/ocr/languages")
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let response = ctx.app().clone().oneshot(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let body_bytes = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
|
||||
let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap();
|
||||
|
||||
assert!(body.get("available_languages").is_some());
|
||||
let languages = body["available_languages"].as_array().unwrap();
|
||||
assert!(languages.len() >= 6); // We created 6 mock languages
|
||||
|
||||
// Check that languages have the expected structure
|
||||
|
|
@ -145,60 +80,65 @@ async fn test_get_available_languages_success() {
|
|||
lang.get("code").unwrap().as_str().unwrap() == "eng"
|
||||
});
|
||||
assert!(has_english);
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_available_languages_unauthorized() {
|
||||
let harness = TestHarness::new().await;
|
||||
// Create temporary directory for tessdata
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
||||
let tessdata_path = temp_dir.path().join("tessdata");
|
||||
fs::create_dir_all(&tessdata_path).expect("Failed to create tessdata directory");
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.get("/api/ocr/languages")
|
||||
.await;
|
||||
// Create mock language files
|
||||
fs::write(tessdata_path.join("eng.traineddata"), "mock").unwrap();
|
||||
std::env::set_var("TESSDATA_PREFIX", &tessdata_path);
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::UNAUTHORIZED);
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
let request = Request::builder()
|
||||
.method("GET")
|
||||
.uri("/api/ocr/languages")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_get_available_languages_includes_current_user_language() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.get("/api/ocr/languages")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_eq!(body["current_user_language"].as_str().unwrap(), "eng");
|
||||
|
||||
harness.cleanup().await;
|
||||
let response = ctx.app().clone().oneshot(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_retry_ocr_with_language_success() {
|
||||
let harness = TestHarness::new().await;
|
||||
// Create temporary directory for tessdata
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
||||
let tessdata_path = temp_dir.path().join("tessdata");
|
||||
fs::create_dir_all(&tessdata_path).expect("Failed to create tessdata directory");
|
||||
|
||||
// First, create a test document
|
||||
// Create mock language files
|
||||
fs::write(tessdata_path.join("eng.traineddata"), "mock").unwrap();
|
||||
fs::write(tessdata_path.join("spa.traineddata"), "mock").unwrap();
|
||||
std::env::set_var("TESSDATA_PREFIX", &tessdata_path);
|
||||
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
// Create test user and get token
|
||||
let auth_helper = readur::test_utils::TestAuthHelper::new(ctx.app().clone());
|
||||
let mut test_user = auth_helper.create_test_user().await;
|
||||
let user_id = test_user.user_response.id;
|
||||
let token = test_user.login(&auth_helper).await.unwrap();
|
||||
|
||||
// Create a test document
|
||||
let document_id = Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO documents (id, user_id, filename, original_filename, file_size, mime_type, ocr_status, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())"
|
||||
)
|
||||
.bind(document_id)
|
||||
.bind(harness.user_id)
|
||||
.bind(user_id)
|
||||
.bind("test.pdf")
|
||||
.bind("test.pdf")
|
||||
.bind(1024i64)
|
||||
.bind("application/pdf")
|
||||
.bind("failed")
|
||||
.execute(&harness.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.execute(&ctx.state().db.pool)
|
||||
.await
|
||||
.expect("Failed to create test document");
|
||||
|
||||
|
|
@ -206,65 +146,41 @@ async fn test_retry_ocr_with_language_success() {
|
|||
"language": "spa"
|
||||
});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post(&format!("/documents/{}/retry-ocr", document_id))
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&retry_request)
|
||||
.await;
|
||||
let request = Request::builder()
|
||||
.method("POST")
|
||||
.uri(&format!("/api/documents/{}/retry-ocr", document_id))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&retry_request).unwrap()))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
let response = ctx.app().clone().oneshot(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::OK);
|
||||
|
||||
let body: serde_json::Value = response.json();
|
||||
let body_bytes = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
|
||||
let body: serde_json::Value = serde_json::from_slice(&body_bytes).unwrap();
|
||||
assert_eq!(body["success"].as_bool().unwrap(), true);
|
||||
assert!(body.get("message").is_some());
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_retry_ocr_without_language_uses_default() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
// Create a test document
|
||||
let document_id = Uuid::new_v4();
|
||||
sqlx::query(
|
||||
"INSERT INTO documents (id, user_id, filename, original_filename, file_size, mime_type, ocr_status, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())"
|
||||
)
|
||||
.bind(document_id)
|
||||
.bind(harness.user_id)
|
||||
.bind("test.pdf")
|
||||
.bind("test.pdf")
|
||||
.bind(1024i64)
|
||||
.bind("application/pdf")
|
||||
.bind("failed")
|
||||
.execute(&harness.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.await
|
||||
.expect("Failed to create test document");
|
||||
|
||||
let retry_request = json!({});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post(&format!("/documents/{}/retry-ocr", document_id))
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&retry_request)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
|
||||
let body: serde_json::Value = response.json();
|
||||
assert_eq!(body["success"].as_bool().unwrap(), true);
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_retry_ocr_with_invalid_language() {
|
||||
let harness = TestHarness::new().await;
|
||||
// Create temporary directory for tessdata
|
||||
let temp_dir = TempDir::new().expect("Failed to create temp directory");
|
||||
let tessdata_path = temp_dir.path().join("tessdata");
|
||||
fs::create_dir_all(&tessdata_path).expect("Failed to create tessdata directory");
|
||||
|
||||
// Create mock language files
|
||||
fs::write(tessdata_path.join("eng.traineddata"), "mock").unwrap();
|
||||
std::env::set_var("TESSDATA_PREFIX", &tessdata_path);
|
||||
|
||||
let ctx = TestContext::new().await;
|
||||
|
||||
// Create test user and get token
|
||||
let auth_helper = readur::test_utils::TestAuthHelper::new(ctx.app().clone());
|
||||
let mut test_user = auth_helper.create_test_user().await;
|
||||
let user_id = test_user.user_response.id;
|
||||
let token = test_user.login(&auth_helper).await.unwrap();
|
||||
|
||||
// Create a test document
|
||||
let document_id = Uuid::new_v4();
|
||||
|
|
@ -273,13 +189,13 @@ async fn test_retry_ocr_with_invalid_language() {
|
|||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())"
|
||||
)
|
||||
.bind(document_id)
|
||||
.bind(harness.user_id)
|
||||
.bind(user_id)
|
||||
.bind("test.pdf")
|
||||
.bind("test.pdf")
|
||||
.bind(1024i64)
|
||||
.bind("application/pdf")
|
||||
.bind("failed")
|
||||
.execute(&harness.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.execute(&ctx.state().db.pool)
|
||||
.await
|
||||
.expect("Failed to create test document");
|
||||
|
||||
|
|
@ -287,253 +203,14 @@ async fn test_retry_ocr_with_invalid_language() {
|
|||
"language": "invalid_lang"
|
||||
});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post(&format!("/documents/{}/retry-ocr", document_id))
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&retry_request)
|
||||
.await;
|
||||
let request = Request::builder()
|
||||
.method("POST")
|
||||
.uri(&format!("/api/documents/{}/retry-ocr", document_id))
|
||||
.header("Authorization", format!("Bearer {}", token))
|
||||
.header("Content-Type", "application/json")
|
||||
.body(Body::from(serde_json::to_vec(&retry_request).unwrap()))
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::BAD_REQUEST);
|
||||
|
||||
let body: serde_json::Value = response.json();
|
||||
assert!(body.get("error").is_some());
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_retry_ocr_nonexistent_document() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
let nonexistent_id = Uuid::new_v4();
|
||||
let retry_request = json!({
|
||||
"language": "spa"
|
||||
});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post(&format!("/documents/{}/retry-ocr", nonexistent_id))
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&retry_request)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::NOT_FOUND);
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_retry_ocr_unauthorized_user() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
// Create a document owned by a different user
|
||||
let other_user_id = Uuid::new_v4();
|
||||
let document_id = Uuid::new_v4();
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO users (id, username, email, password_hash) VALUES ($1, $2, $3, $4)"
|
||||
)
|
||||
.bind(other_user_id)
|
||||
.bind("otheruser")
|
||||
.bind("other@test.com")
|
||||
.bind("dummy_hash")
|
||||
.execute(&harness.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.await
|
||||
.expect("Failed to create other user");
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO documents (id, user_id, filename, original_filename, file_size, mime_type, ocr_status, created_at, updated_at)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, NOW(), NOW())"
|
||||
)
|
||||
.bind(document_id)
|
||||
.bind(other_user_id)
|
||||
.bind("test.pdf")
|
||||
.bind("test.pdf")
|
||||
.bind(1024i64)
|
||||
.bind("application/pdf")
|
||||
.bind("failed")
|
||||
.execute(&harness.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.await
|
||||
.expect("Failed to create test document");
|
||||
|
||||
let retry_request = json!({
|
||||
"language": "spa"
|
||||
});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post(&format!("/documents/{}/retry-ocr", document_id))
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&retry_request)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::FORBIDDEN);
|
||||
|
||||
// Cleanup other user
|
||||
sqlx::query("DELETE FROM users WHERE id = $1")
|
||||
.bind(other_user_id)
|
||||
.execute(&harness.server.into_inner().extract::<Arc<AppState>>().unwrap().db.pool)
|
||||
.await
|
||||
.expect("Failed to cleanup other user");
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_document_upload_with_language_validation() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
// Create a multipart form with a document and language
|
||||
let file_content = b"Mock PDF content";
|
||||
let form = reqwest::multipart::Form::new()
|
||||
.part("file", reqwest::multipart::Part::bytes(file_content.to_vec())
|
||||
.file_name("test.pdf")
|
||||
.mime_str("application/pdf").unwrap())
|
||||
.part("language", reqwest::multipart::Part::text("spa"));
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post("/documents")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.multipart(form)
|
||||
.await;
|
||||
|
||||
// Should succeed with valid language
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_document_upload_with_invalid_language() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
// Create a multipart form with invalid language
|
||||
let file_content = b"Mock PDF content";
|
||||
let form = reqwest::multipart::Form::new()
|
||||
.part("file", reqwest::multipart::Part::bytes(file_content.to_vec())
|
||||
.file_name("test.pdf")
|
||||
.mime_str("application/pdf").unwrap())
|
||||
.part("language", reqwest::multipart::Part::text("invalid_lang"));
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.post("/documents")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.multipart(form)
|
||||
.await;
|
||||
|
||||
// Should fail with invalid language
|
||||
assert_eq!(response.status_code(), StatusCode::BAD_REQUEST);
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_settings_update_with_ocr_language() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
let settings_update = json!({
|
||||
"ocrLanguage": "fra",
|
||||
"concurrentOcrJobs": 2,
|
||||
"ocrTimeoutSeconds": 300
|
||||
});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.put("/settings")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&settings_update)
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
|
||||
// Verify the setting was updated
|
||||
let get_response = harness
|
||||
.server
|
||||
.get("/settings")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.await;
|
||||
|
||||
assert_eq!(get_response.status_code(), StatusCode::OK);
|
||||
|
||||
let body: serde_json::Value = get_response.json();
|
||||
assert_eq!(body["ocrLanguage"].as_str().unwrap(), "fra");
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_settings_update_with_invalid_ocr_language() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
let settings_update = json!({
|
||||
"ocrLanguage": "invalid_lang",
|
||||
"concurrentOcrJobs": 2
|
||||
});
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.put("/settings")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.add_header("Content-Type", "application/json")
|
||||
.json(&settings_update)
|
||||
.await;
|
||||
|
||||
// Should fail with invalid language
|
||||
assert_eq!(response.status_code(), StatusCode::BAD_REQUEST);
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_ocr_health_endpoint() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
let response = harness
|
||||
.server
|
||||
.get("/api/ocr/health")
|
||||
.add_header("Authorization", &format!("Bearer {}", harness.token))
|
||||
.await;
|
||||
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
|
||||
let body: serde_json::Value = response.json();
|
||||
assert!(body.get("status").is_some());
|
||||
assert!(body.get("available_languages").is_some());
|
||||
|
||||
harness.cleanup().await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_concurrent_language_requests() {
|
||||
let harness = TestHarness::new().await;
|
||||
|
||||
// Make multiple concurrent requests to the languages endpoint
|
||||
let mut handles = vec![];
|
||||
|
||||
for _ in 0..5 {
|
||||
let server_clone = harness.server.clone();
|
||||
let token_clone = harness.token.clone();
|
||||
let handle = tokio::spawn(async move {
|
||||
server_clone
|
||||
.get("/api/ocr/languages")
|
||||
.add_header("Authorization", &format!("Bearer {}", token_clone))
|
||||
.await
|
||||
});
|
||||
handles.push(handle);
|
||||
}
|
||||
|
||||
// All requests should succeed
|
||||
for handle in handles {
|
||||
let response = handle.await.expect("Task panicked");
|
||||
assert_eq!(response.status_code(), StatusCode::OK);
|
||||
}
|
||||
|
||||
harness.cleanup().await;
|
||||
let response = ctx.app().clone().oneshot(request).await.unwrap();
|
||||
assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
}
|
||||
Loading…
Reference in New Issue