Files
mcp-docx/tests/docx_handler_tests.rs
T
2025-08-11 14:31:51 +08:00

314 lines
9.6 KiB
Rust

use anyhow::Result;
use docx_mcp::docx_handler::{DocxHandler, DocxStyle, TableData};
use tempfile::TempDir;
use std::path::PathBuf;
use pretty_assertions::assert_eq;
use rstest::*;
use chrono::Utc;
fn setup_test_handler() -> (DocxHandler, TempDir) {
let temp_dir = TempDir::new().unwrap();
let handler = DocxHandler::new_with_temp_dir(temp_dir.path()).unwrap();
(handler, temp_dir)
}
#[fixture]
fn handler_and_doc() -> (DocxHandler, String, TempDir) {
let (mut handler, temp_dir) = setup_test_handler();
let doc_id = handler.create_document().unwrap();
(handler, doc_id, temp_dir)
}
#[test]
fn test_create_document() {
let (mut handler, _temp_dir) = setup_test_handler();
let doc_id = handler.create_document().unwrap();
assert!(!doc_id.is_empty());
// Document should be in the handler's registry
assert!(handler.documents.contains_key(&doc_id));
let metadata = handler.get_metadata(&doc_id).unwrap();
assert_eq!(metadata.id, doc_id);
assert!(metadata.path.exists());
}
#[test]
fn test_add_paragraph() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
let result = handler.add_paragraph(&doc_id, "Test paragraph", None);
assert!(result.is_ok());
// Verify content was added by extracting text
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("Test paragraph"));
}
#[test]
fn test_add_paragraph_with_style() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
let style = DocxStyle {
font_family: Some("Arial".to_string()),
font_size: Some(14),
bold: Some(true),
italic: Some(false),
underline: Some(false),
color: Some("#FF0000".to_string()),
alignment: Some("center".to_string()),
line_spacing: Some(1.5),
};
let result = handler.add_paragraph(&doc_id, "Styled paragraph", Some(style));
assert!(result.is_ok());
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("Styled paragraph"));
}
#[test]
fn test_add_heading() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
for level in 1..=6 {
let heading_text = format!("Heading Level {}", level);
let result = handler.add_heading(&doc_id, &heading_text, level);
assert!(result.is_ok(), "Failed to add heading level {}", level);
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains(&heading_text));
}
}
#[test]
fn test_add_table() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
let table_data = TableData {
rows: vec![
vec!["Name".to_string(), "Age".to_string(), "City".to_string()],
vec!["John".to_string(), "30".to_string(), "NYC".to_string()],
vec!["Jane".to_string(), "25".to_string(), "LA".to_string()],
],
headers: Some(vec!["Name".to_string(), "Age".to_string(), "City".to_string()]),
border_style: Some("single".to_string()),
};
let result = handler.add_table(&doc_id, table_data);
assert!(result.is_ok());
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("John"));
assert!(text.contains("Jane"));
assert!(text.contains("NYC"));
}
#[test]
fn test_add_list() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
let items = vec![
"First item".to_string(),
"Second item".to_string(),
"Third item".to_string(),
];
// Test unordered list
let result = handler.add_list(&doc_id, items.clone(), false);
assert!(result.is_ok());
// Test ordered list
let result = handler.add_list(&doc_id, items.clone(), true);
assert!(result.is_ok());
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("First item"));
assert!(text.contains("Second item"));
assert!(text.contains("Third item"));
}
#[test]
fn test_set_header_footer() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
let header_result = handler.set_header(&doc_id, "Document Header");
assert!(header_result.is_ok());
let footer_result = handler.set_footer(&doc_id, "Document Footer");
assert!(footer_result.is_ok());
}
#[test]
fn test_add_page_break() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
handler.add_paragraph(&doc_id, "Before page break", None).unwrap();
let result = handler.add_page_break(&doc_id);
assert!(result.is_ok());
handler.add_paragraph(&doc_id, "After page break", None).unwrap();
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("Before page break"));
assert!(text.contains("After page break"));
}
#[test]
fn test_extract_text_empty_document() {
let (handler, doc_id, _temp_dir) = handler_and_doc();
let text = handler.extract_text(&doc_id).unwrap();
// Empty document might have some default content or be truly empty
assert!(text.is_empty() || text.trim().is_empty());
}
#[test]
fn test_save_and_close_document() {
let (mut handler, doc_id, temp_dir) = handler_and_doc();
handler.add_paragraph(&doc_id, "Test content", None).unwrap();
let save_path = temp_dir.path().join("test_output.docx");
let save_result = handler.save_document(&doc_id, &save_path);
assert!(save_result.is_ok());
assert!(save_path.exists());
let close_result = handler.close_document(&doc_id);
assert!(close_result.is_ok());
assert!(!handler.documents.contains_key(&doc_id));
}
#[test]
fn test_open_existing_document() {
let (mut handler, doc_id, temp_dir) = handler_and_doc();
// Create and save a document
handler.add_paragraph(&doc_id, "Original content", None).unwrap();
let save_path = temp_dir.path().join("existing.docx");
handler.save_document(&doc_id, &save_path).unwrap();
handler.close_document(&doc_id).unwrap();
// Open the saved document
let opened_doc_id = handler.open_document(&save_path).unwrap();
assert_ne!(opened_doc_id, doc_id); // Should be a new ID
let text = handler.extract_text(&opened_doc_id).unwrap();
assert!(text.contains("Original content"));
}
#[test]
fn test_list_documents() {
let (mut handler, _temp_dir) = setup_test_handler();
// Initially should be empty
let docs = handler.list_documents();
let initial_count = docs.len();
// Create some documents
let _doc1 = handler.create_document().unwrap();
let _doc2 = handler.create_document().unwrap();
let _doc3 = handler.create_document().unwrap();
let docs = handler.list_documents();
assert_eq!(docs.len(), initial_count + 3);
}
#[test]
fn test_document_not_found_error() {
let (handler, _temp_dir) = setup_test_handler();
let fake_id = "nonexistent-document-id";
let result = handler.extract_text(fake_id);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Document not found"));
}
#[test]
fn test_get_metadata() {
let (handler, doc_id, _temp_dir) = handler_and_doc();
let metadata = handler.get_metadata(&doc_id).unwrap();
assert_eq!(metadata.id, doc_id);
assert!(metadata.path.exists());
assert!(metadata.created_at <= Utc::now());
assert!(metadata.modified_at <= Utc::now());
assert_eq!(metadata.page_count, Some(1));
assert_eq!(metadata.word_count, Some(0));
}
#[test]
fn test_concurrent_document_operations() {
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
let (handler, _temp_dir) = setup_test_handler();
let handler = Arc::new(Mutex::new(handler));
let handles: Vec<_> = (0..5).map(|i| {
let handler = Arc::clone(&handler);
thread::spawn(move || {
let doc_id = {
let mut h = handler.lock().unwrap();
h.create_document().unwrap()
};
{
let mut h = handler.lock().unwrap();
h.add_paragraph(&doc_id, &format!("Thread {} content", i), None).unwrap();
}
{
let h = handler.lock().unwrap();
let text = h.extract_text(&doc_id).unwrap();
assert!(text.contains(&format!("Thread {} content", i)));
}
doc_id
})
}).collect();
let doc_ids: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
// All documents should be different
let mut unique_ids = doc_ids.clone();
unique_ids.sort();
unique_ids.dedup();
assert_eq!(unique_ids.len(), doc_ids.len());
}
#[test]
fn test_large_document_creation() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
// Add many paragraphs to test performance
for i in 0..100 {
let content = format!("Paragraph number {} with some content to make it realistic", i);
handler.add_paragraph(&doc_id, &content, None).unwrap();
}
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("Paragraph number 0"));
assert!(text.contains("Paragraph number 99"));
// Verify word count
let words: Vec<&str> = text.split_whitespace().collect();
assert!(words.len() > 1000); // Should have many words
}
#[test]
fn test_special_characters_in_content() {
let (mut handler, doc_id, _temp_dir) = handler_and_doc();
let special_content = "Special chars: éñüñdéd, 中文, русский, العربية, 🚀📝✨";
handler.add_paragraph(&doc_id, special_content, None).unwrap();
let text = handler.extract_text(&doc_id).unwrap();
assert!(text.contains("éñüñdéd"));
assert!(text.contains("🚀📝✨"));
}