Files
readur/tests/migration_constraint_tests.rs

249 lines
8.7 KiB
Rust

use readur::test_utils::TestContext;
use uuid;
#[cfg(test)]
mod migration_constraint_tests {
use super::*;
#[tokio::test]
async fn test_failed_documents_constraint_validation() {
let ctx = TestContext::new().await;
let pool = ctx.state.db.get_pool();
// Create a test user first to avoid foreign key constraint violations
let user_id = uuid::Uuid::new_v4();
let unique_suffix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let username = format!("test_constraint_user_{}", unique_suffix);
let email = format!("test_constraint_{}@example.com", unique_suffix);
sqlx::query(
"INSERT INTO users (id, username, email, password_hash, role)
VALUES ($1, $2, $3, $4, $5)"
)
.bind(user_id)
.bind(&username)
.bind(&email)
.bind("hash")
.bind("user")
.execute(pool)
.await
.unwrap();
// Test that all allowed failure_reason values work
let valid_reasons = vec![
"duplicate_content", "duplicate_filename", "unsupported_format",
"file_too_large", "file_corrupted", "access_denied",
"low_ocr_confidence", "ocr_timeout", "ocr_memory_limit",
"pdf_parsing_error", "storage_quota_exceeded", "network_error",
"permission_denied", "virus_detected", "invalid_structure",
"policy_violation", "other"
];
for reason in valid_reasons {
let result = sqlx::query(
r#"
INSERT INTO failed_documents (
user_id, filename, failure_reason, failure_stage, ingestion_source
) VALUES (
$1, $2, $3, 'validation', 'test'
)
"#
)
.bind(user_id)
.bind(format!("test_file_{}.txt", reason))
.bind(reason)
.execute(pool)
.await;
assert!(result.is_ok(), "Valid failure_reason '{}' should be accepted", reason);
}
}
#[tokio::test]
async fn test_failed_documents_invalid_constraint_rejection() {
let ctx = TestContext::new().await;
let pool = ctx.state.db.get_pool();
// Create a test user first to avoid foreign key constraint violations
let user_id = uuid::Uuid::new_v4();
let unique_suffix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let username = format!("test_invalid_user_{}", unique_suffix);
let email = format!("test_invalid_{}@example.com", unique_suffix);
sqlx::query(
"INSERT INTO users (id, username, email, password_hash, role)
VALUES ($1, $2, $3, $4, $5)"
)
.bind(user_id)
.bind(&username)
.bind(&email)
.bind("hash")
.bind("user")
.execute(pool)
.await
.unwrap();
// Test that invalid failure_reason values are rejected
let invalid_reasons = vec![
"invalid_reason", "unknown", "timeout", "memory_limit",
"migration_completed", "corrupted", "unsupported"
];
for reason in invalid_reasons {
let result = sqlx::query(
r#"
INSERT INTO failed_documents (
user_id, filename, failure_reason, failure_stage, ingestion_source
) VALUES (
$1, $2, $3, 'validation', 'test'
)
"#
)
.bind(user_id)
.bind(format!("test_file_{}.txt", reason))
.bind(reason)
.execute(pool)
.await;
assert!(result.is_err(), "Invalid failure_reason '{}' should be rejected", reason);
}
}
#[tokio::test]
async fn test_failed_documents_stage_constraint_validation() {
let ctx = TestContext::new().await;
let pool = ctx.state.db.get_pool();
// Create a test user first to avoid foreign key constraint violations
let user_id = uuid::Uuid::new_v4();
let unique_suffix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let username = format!("test_stage_user_{}", unique_suffix);
let email = format!("test_stage_{}@example.com", unique_suffix);
sqlx::query(
"INSERT INTO users (id, username, email, password_hash, role)
VALUES ($1, $2, $3, $4, $5)"
)
.bind(user_id)
.bind(&username)
.bind(&email)
.bind("hash")
.bind("user")
.execute(pool)
.await
.unwrap();
// Test that all allowed failure_stage values work
let valid_stages = vec![
"ingestion", "validation", "ocr", "storage", "processing", "sync"
];
for stage in valid_stages {
let result = sqlx::query(
r#"
INSERT INTO failed_documents (
user_id, filename, failure_reason, failure_stage, ingestion_source
) VALUES (
$1, $2, 'other', $3, 'test'
)
"#
)
.bind(user_id)
.bind(format!("test_file_{}.txt", stage))
.bind(stage)
.execute(pool)
.await;
assert!(result.is_ok(), "Valid failure_stage '{}' should be accepted", stage);
}
}
#[tokio::test]
async fn test_migration_mapping_compatibility() {
let ctx = TestContext::new().await;
let pool = ctx.state.db.get_pool();
// Create a test user first to avoid foreign key constraint violations
let user_id = uuid::Uuid::new_v4();
let unique_suffix = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_nanos();
let username = format!("test_migration_user_{}", unique_suffix);
let email = format!("test_migration_{}@example.com", unique_suffix);
sqlx::query(
"INSERT INTO users (id, username, email, password_hash, role)
VALUES ($1, $2, $3, $4, $5)"
)
.bind(user_id)
.bind(&username)
.bind(&email)
.bind("hash")
.bind("user")
.execute(pool)
.await
.unwrap();
// Test that the migration mapping logic matches our constraints
let migration_mappings = vec![
("low_ocr_confidence", "low_ocr_confidence"),
("timeout", "ocr_timeout"),
("memory_limit", "ocr_memory_limit"),
("pdf_parsing_error", "pdf_parsing_error"),
("corrupted", "file_corrupted"),
("file_corrupted", "file_corrupted"),
("unsupported_format", "unsupported_format"),
("access_denied", "access_denied"),
("unknown_value", "other"), // fallback case
("", "other"), // empty case
];
for (input_reason, expected_output) in migration_mappings {
// Simulate the migration CASE logic
let mapped_reason = match input_reason {
"low_ocr_confidence" => "low_ocr_confidence",
"timeout" => "ocr_timeout",
"memory_limit" => "ocr_memory_limit",
"pdf_parsing_error" => "pdf_parsing_error",
"corrupted" | "file_corrupted" => "file_corrupted",
"unsupported_format" => "unsupported_format",
"access_denied" => "access_denied",
_ => "other",
};
assert_eq!(mapped_reason, expected_output,
"Migration mapping for '{}' should produce '{}'",
input_reason, expected_output);
// Test that the mapped value works in the database
let result = sqlx::query(
r#"
INSERT INTO failed_documents (
user_id, filename, failure_reason, failure_stage, ingestion_source
) VALUES (
$1, $2, $3, 'ocr', 'migration'
)
"#
)
.bind(user_id)
.bind(format!("migration_test_{}.txt", input_reason.replace("/", "_")))
.bind(mapped_reason)
.execute(pool)
.await;
assert!(result.is_ok(),
"Mapped failure_reason '{}' (from '{}') should be accepted by constraints",
mapped_reason, input_reason);
}
}
}