use spdf_core::SpdfParser;
use spdf_types::{ParseInput, SpdfError};
fn read_fixture(relative: &str) -> Option<Vec<u8>> {
let manifest = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let path = manifest.parent()?.parent()?.join("example").join(relative);
std::fs::read(path).ok()
}
#[test]
fn encrypted_pdf_requires_password() {
let Some(pdf) = read_fixture("corpus/encrypted.pdf") else {
return;
};
let parser = SpdfParser::builder().ocr_enabled(false).build();
match parser.parse(ParseInput::Bytes(pdf)) {
Err(SpdfError::PasswordRequired) | Err(SpdfError::InvalidPassword) => {}
Err(SpdfError::Pdf(msg)) => {
assert!(
msg.to_lowercase().contains("password"),
"Pdf error without password hint: {msg}"
);
}
other => panic!("expected password error, got {other:?}"),
}
}
#[test]
fn malformed_pdf_fails_cleanly() {
let Some(pdf) = read_fixture("corpus/malformed.pdf") else {
return;
};
let parser = SpdfParser::builder()
.ocr_enabled(false)
.max_pages(8)
.build();
let err = parser
.parse(ParseInput::Bytes(pdf))
.expect_err("truncated PDF must not parse");
let _: &SpdfError = &err;
}
#[test]
fn cjk_pdf_produces_text_items() {
let Some(pdf) = read_fixture("corpus/cjk-unicode-p1-2.pdf") else {
return;
};
let parser = SpdfParser::builder().ocr_enabled(false).build();
let result = parser
.parse(ParseInput::Bytes(pdf))
.expect("cjk pdf must parse");
assert!(!result.pages.is_empty());
let total_items: usize = result.pages.iter().map(|p| p.text_items.len()).sum();
assert!(total_items > 0, "no text extracted from CJK fixture");
}