254 lines
8.8 KiB
Rust
254 lines
8.8 KiB
Rust
//! End-to-end integration test for DOCX metadata extraction
|
|
|
|
#![cfg(feature = "office")]
|
|
|
|
use kreuzberg::{ExtractionConfig, extract_file};
|
|
|
|
#[tokio::test]
|
|
async fn test_docx_full_metadata_extraction() {
|
|
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.expect("Operation failed")
|
|
.parent()
|
|
.expect("Operation failed");
|
|
let test_file = workspace_root.join("test_documents/docx/word_sample.docx");
|
|
|
|
if !test_file.exists() {
|
|
println!("Skipping test: Test file not found at {:?}", test_file);
|
|
return;
|
|
}
|
|
|
|
let result = extract_file(&test_file, None, &ExtractionConfig::default())
|
|
.await
|
|
.expect("Should extract DOCX successfully");
|
|
|
|
assert!(!result.content.is_empty(), "Content should not be empty");
|
|
assert!(
|
|
result.content.to_lowercase().contains("swim"),
|
|
"Content should contain 'swim'"
|
|
);
|
|
|
|
assert_eq!(
|
|
result.metadata.created_by.as_deref(),
|
|
Some("Christoph Auer"),
|
|
"Should have correct creator"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.modified_by.as_deref(),
|
|
Some("Maxim Lysak"),
|
|
"Should have correct last modified by"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.created_at.as_deref(),
|
|
Some("2024-10-09T12:43:00Z"),
|
|
"Should have correct creation date"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.additional.get("revision").and_then(|v| v.as_str()),
|
|
Some("7"),
|
|
"Should have revision number"
|
|
);
|
|
|
|
assert_eq!(
|
|
result.metadata.additional.get("page_count").and_then(|v| v.as_i64()),
|
|
Some(2),
|
|
"Should have 2 pages"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.additional.get("word_count").and_then(|v| v.as_i64()),
|
|
Some(108),
|
|
"Should have 108 words"
|
|
);
|
|
assert_eq!(
|
|
result
|
|
.metadata
|
|
.additional
|
|
.get("character_count")
|
|
.and_then(|v| v.as_i64()),
|
|
Some(620),
|
|
"Should have 620 characters"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.additional.get("line_count").and_then(|v| v.as_i64()),
|
|
Some(5),
|
|
"Should have 5 lines"
|
|
);
|
|
assert_eq!(
|
|
result
|
|
.metadata
|
|
.additional
|
|
.get("paragraph_count")
|
|
.and_then(|v| v.as_i64()),
|
|
Some(1),
|
|
"Should have 1 paragraph"
|
|
);
|
|
|
|
println!("✅ DOCX metadata extraction test passed!");
|
|
println!(" Found {} metadata fields", result.metadata.additional.len());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_docx_minimal_metadata_extraction() {
|
|
let workspace_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
|
|
.parent()
|
|
.expect("Operation failed")
|
|
.parent()
|
|
.expect("Operation failed");
|
|
let test_file = workspace_root.join("test_documents/docx/lorem_ipsum.docx");
|
|
|
|
if !test_file.exists() {
|
|
println!("Skipping test: Test file not found at {:?}", test_file);
|
|
return;
|
|
}
|
|
|
|
let result = extract_file(&test_file, None, &ExtractionConfig::default())
|
|
.await
|
|
.expect("Should extract DOCX successfully");
|
|
|
|
assert!(!result.content.is_empty(), "Content should not be empty");
|
|
|
|
assert_eq!(
|
|
result.metadata.additional.get("page_count").and_then(|v| v.as_i64()),
|
|
Some(1),
|
|
"Should have 1 page"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.additional.get("word_count").and_then(|v| v.as_i64()),
|
|
Some(520),
|
|
"Should have 520 words"
|
|
);
|
|
|
|
println!("✅ DOCX minimal metadata extraction test passed!");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_docx_keywords_extraction() {
|
|
// This test verifies that DOCX keywords metadata is properly parsed
|
|
// from comma-separated strings into Vec<String> in Metadata.keywords
|
|
//
|
|
// Addresses GitHub issue #309: DOCX keyword extraction was returning
|
|
// strings instead of parsed keyword lists, causing FunctionClauseError
|
|
// in the Elixir binding.
|
|
|
|
use std::io::Write;
|
|
use tempfile::NamedTempFile;
|
|
use zip::CompressionMethod;
|
|
use zip::write::{FileOptions, ZipWriter};
|
|
|
|
// Create a minimal DOCX with keywords metadata
|
|
let mut temp_file = NamedTempFile::new().expect("Failed to create temp file");
|
|
|
|
{
|
|
let mut zip = ZipWriter::new(&mut temp_file);
|
|
let options: FileOptions<()> = FileOptions::default().compression_method(CompressionMethod::Stored);
|
|
|
|
// Add [Content_Types].xml
|
|
zip.start_file("[Content_Types].xml", options)
|
|
.expect("Operation failed");
|
|
zip.write_all(br#"<?xml version="1.0" encoding="UTF-8"?>
|
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
<Default Extension="xml" ContentType="application/xml"/>
|
|
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
|
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
|
</Types>"#).expect("Operation failed");
|
|
|
|
// Add _rels/.rels
|
|
zip.start_file("_rels/.rels", options).expect("Operation failed");
|
|
zip.write_all(br#"<?xml version="1.0" encoding="UTF-8"?>
|
|
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
|
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
|
</Relationships>"#).expect("Operation failed");
|
|
|
|
// Add word/document.xml with simple content
|
|
zip.start_file("word/document.xml", options).expect("Operation failed");
|
|
zip.write_all(
|
|
br#"<?xml version="1.0" encoding="UTF-8"?>
|
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
<w:body>
|
|
<w:p>
|
|
<w:r>
|
|
<w:t>Test document for keyword extraction</w:t>
|
|
</w:r>
|
|
</w:p>
|
|
</w:body>
|
|
</w:document>"#,
|
|
)
|
|
.expect("Operation failed");
|
|
|
|
// Add docProps/core.xml with keywords (comma-separated string)
|
|
zip.start_file("docProps/core.xml", options).expect("Operation failed");
|
|
zip.write_all(
|
|
br#"<?xml version="1.0" encoding="UTF-8"?>
|
|
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties"
|
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
|
xmlns:dcterms="http://purl.org/dc/terms/">
|
|
<dc:title>Test Document</dc:title>
|
|
<dc:creator>Test Author</dc:creator>
|
|
<cp:keywords>rust, docx, extraction, metadata, test</cp:keywords>
|
|
<dc:subject>Testing keyword extraction</dc:subject>
|
|
</cp:coreProperties>"#,
|
|
)
|
|
.expect("Operation failed");
|
|
|
|
zip.finish().expect("Operation failed");
|
|
}
|
|
|
|
// Extract the DOCX file
|
|
let result = extract_file(
|
|
temp_file.path(),
|
|
Some("application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
|
|
&ExtractionConfig::default(),
|
|
)
|
|
.await
|
|
.expect("Should extract DOCX with keywords successfully");
|
|
|
|
// Verify content was extracted
|
|
assert!(!result.content.is_empty(), "Content should not be empty");
|
|
assert!(
|
|
result.content.contains("Test document for keyword extraction"),
|
|
"Content should match document text"
|
|
);
|
|
|
|
// Verify keywords were parsed into Vec<String> in Metadata.keywords
|
|
assert!(
|
|
result.metadata.keywords.is_some(),
|
|
"Keywords should be present in metadata.keywords"
|
|
);
|
|
|
|
let keywords = result.metadata.keywords.as_ref().expect("Operation failed");
|
|
assert_eq!(
|
|
keywords.len(),
|
|
5,
|
|
"Should have 5 keywords parsed from comma-separated string"
|
|
);
|
|
|
|
// Verify individual keywords were trimmed and parsed correctly
|
|
assert_eq!(keywords[0], "rust", "First keyword should be 'rust'");
|
|
assert_eq!(keywords[1], "docx", "Second keyword should be 'docx'");
|
|
assert_eq!(keywords[2], "extraction", "Third keyword should be 'extraction'");
|
|
assert_eq!(keywords[3], "metadata", "Fourth keyword should be 'metadata'");
|
|
assert_eq!(keywords[4], "test", "Fifth keyword should be 'test'");
|
|
|
|
// Verify other metadata was also extracted via typed fields
|
|
assert_eq!(
|
|
result.metadata.created_by.as_deref(),
|
|
Some("Test Author"),
|
|
"Should have correct creator"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.title.as_deref(),
|
|
Some("Test Document"),
|
|
"Should have correct title"
|
|
);
|
|
assert_eq!(
|
|
result.metadata.subject.as_deref(),
|
|
Some("Testing keyword extraction"),
|
|
"Should have correct subject"
|
|
);
|
|
|
|
println!("✅ DOCX keywords extraction test passed!");
|
|
println!(" Extracted keywords: {:?}", keywords);
|
|
}
|