Some checks failed
CI / Linux x86_64 (Forgejo) (push) Failing after 2s
Signed-off-by: flemming-it <sf@flemming.it>
135 lines
4.7 KiB
Rust
135 lines
4.7 KiB
Rust
//! End-to-end test for the DOCX path.
|
|
//!
|
|
//! Builds a minimal valid DOCX in memory, hands it to the
|
|
//! `invoke` entry point, and asserts the JSON output matches the
|
|
//! expected schema. The DOCX is a ZIP archive containing
|
|
//! `word/document.xml` plus the minimum `[Content_Types].xml`
|
|
//! that Office tools expect — that is the bare floor for a
|
|
//! parser to accept the file.
|
|
//!
|
|
//! The PDF path is covered by unit tests on the page splitter;
|
|
//! end-to-end PDF coverage will land once the platform's hub
|
|
//! integration test loads the wasm component and feeds it a
|
|
//! real PDF fixture.
|
|
|
|
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
|
|
use std::io::Write;
|
|
|
|
use chain_module_sdk::prelude::*;
|
|
use serde::Deserialize;
|
|
use text_extract::invoke;
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct ExtractedDocument {
|
|
engine: String,
|
|
engine_version: String,
|
|
pages: Vec<Page>,
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
struct Page {
|
|
number: u32,
|
|
text: String,
|
|
}
|
|
|
|
const DOCX_MIME: &str = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
|
|
|
|
const CONTENT_TYPES_XML: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
<Default Extension="xml" ContentType="application/xml"/>
|
|
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
|
</Types>"#;
|
|
|
|
const RELS_XML: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<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"/>
|
|
</Relationships>"#;
|
|
|
|
const DOCUMENT_XML: &str = r#"<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
|
|
<w:body>
|
|
<w:p><w:r><w:t>The quick brown fox</w:t></w:r></w:p>
|
|
<w:p><w:r><w:t xml:space="preserve">jumps over </w:t></w:r><w:r><w:t>the lazy dog.</w:t></w:r></w:p>
|
|
</w:body>
|
|
</w:document>"#;
|
|
|
|
fn build_minimal_docx() -> Vec<u8> {
|
|
let mut buf = Vec::new();
|
|
{
|
|
let mut w = zip::ZipWriter::new(std::io::Cursor::new(&mut buf));
|
|
let opts: zip::write::SimpleFileOptions = zip::write::SimpleFileOptions::default()
|
|
.compression_method(zip::CompressionMethod::Deflated);
|
|
|
|
w.start_file("[Content_Types].xml", opts).unwrap();
|
|
w.write_all(CONTENT_TYPES_XML.as_bytes()).unwrap();
|
|
|
|
w.start_file("_rels/.rels", opts).unwrap();
|
|
w.write_all(RELS_XML.as_bytes()).unwrap();
|
|
|
|
w.start_file("word/document.xml", opts).unwrap();
|
|
w.write_all(DOCUMENT_XML.as_bytes()).unwrap();
|
|
|
|
w.finish().unwrap();
|
|
}
|
|
buf
|
|
}
|
|
|
|
fn dummy_context() -> Context {
|
|
Context {
|
|
invocation_id: "test".into(),
|
|
capability_namespace: "text".into(),
|
|
capability_name: "extract".into(),
|
|
deadline_ms: 0,
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn docx_round_trip_produces_expected_schema() {
|
|
let docx = build_minimal_docx();
|
|
let inputs = Inputs::from_pairs([("document", Payload::bytes(DOCX_MIME, docx))]);
|
|
|
|
let outputs = invoke(dummy_context(), inputs).expect("invoke ok");
|
|
let pairs = outputs.into_pairs();
|
|
assert_eq!(pairs.len(), 1, "exactly one output expected");
|
|
assert_eq!(pairs[0].0, "extracted");
|
|
|
|
let raw = match &pairs[0].1 {
|
|
Payload::Json(s) => s,
|
|
other => panic!("expected Json payload, got {other:?}"),
|
|
};
|
|
let doc: ExtractedDocument = serde_json::from_str(raw).expect("valid JSON");
|
|
|
|
assert_eq!(doc.engine, "zip+quick-xml");
|
|
assert_eq!(doc.engine_version, "0.1");
|
|
assert_eq!(doc.pages.len(), 1);
|
|
assert_eq!(doc.pages[0].number, 1);
|
|
assert!(
|
|
doc.pages[0].text.contains("quick brown fox"),
|
|
"missing first paragraph: {}",
|
|
doc.pages[0].text
|
|
);
|
|
assert!(
|
|
doc.pages[0].text.contains("jumps over the lazy dog"),
|
|
"missing second paragraph: {}",
|
|
doc.pages[0].text
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn unsupported_mime_type_returns_invalid_input() {
|
|
let inputs =
|
|
Inputs::from_pairs([("document", Payload::bytes("application/zip", vec![0u8; 32]))]);
|
|
let err = invoke(dummy_context(), inputs).expect_err("must fail");
|
|
let msg = format!("{err}");
|
|
assert!(msg.contains("unsupported MIME type"), "msg = {msg}");
|
|
}
|
|
|
|
#[test]
|
|
fn missing_document_input_returns_invalid_input() {
|
|
let inputs = Inputs::default();
|
|
let err = invoke(dummy_context(), inputs).expect_err("must fail");
|
|
let msg = format!("{err}");
|
|
assert!(msg.contains("missing input"), "msg = {msg}");
|
|
}
|