use std::fs;
use std::thread::sleep;
use std::time::Duration;
use mini_docs::{Builder, DocError, MarkdownProcessor};
use serde_json::Value;
const MTIME_SETTLE: Duration = Duration::from_millis(50);
struct MarkerProcessor;
impl MarkdownProcessor for MarkerProcessor {
fn process(&self, body: &str, _frontmatter: &Value) -> Result<String, DocError> {
Ok(format!("{body}\n\n*Processed by marker processor*"))
}
fn name(&self) -> &str {
"marker"
}
}
struct DuplicateProcessor;
impl MarkdownProcessor for DuplicateProcessor {
fn process(&self, body: &str, _frontmatter: &Value) -> Result<String, DocError> {
Ok(format!("## Duplicate\n{body}\n## Duplicate\n{body}"))
}
fn name(&self) -> &str {
"duplicate"
}
}
struct ConflictProcessor;
impl MarkdownProcessor for ConflictProcessor {
fn process(&self, body: &str, _frontmatter: &Value) -> Result<String, DocError> {
Ok(body.to_string())
}
fn name(&self) -> &str {
"marker" }
}
#[test]
fn processor_output_appears_in_rendered_html() {
let input = tempfile::tempdir().expect("create input tempdir");
let templates = tempfile::tempdir().expect("create templates tempdir");
let output = tempfile::tempdir().expect("create output tempdir");
fs::write(input.path().join("page.md"), "# Test\n\nBody content.\n").expect("write page.md");
fs::write(
templates.path().join("page.html"),
"{{ page.content | safe }}",
)
.expect("write page.html");
Builder::new(input.path())
.templates(templates.path())
.output(output.path())
.default_template("page.html")
.processor(MarkerProcessor)
.build()
.expect("build should succeed");
let written = fs::read_to_string(output.path().join("page.html")).expect("read output file");
assert!(
written.contains("Processed by marker processor"),
"processor marker text should appear in rendered HTML: {written}"
);
}
#[test]
fn duplicate_processor_names_cause_error() {
let input = tempfile::tempdir().expect("create input tempdir");
let templates = tempfile::tempdir().expect("create templates tempdir");
let output = tempfile::tempdir().expect("create output tempdir");
fs::write(input.path().join("page.md"), "# Test\n\nBody content.\n").expect("write page.md");
fs::write(
templates.path().join("page.html"),
"{{ page.content | safe }}",
)
.expect("write page.html");
let result = Builder::new(input.path())
.templates(templates.path())
.output(output.path())
.default_template("page.html")
.processor(MarkerProcessor)
.processor(ConflictProcessor)
.build();
match result {
Err(DocError::Extension(msg)) => {
assert!(
msg.contains("marker"),
"error message should mention the conflicting name: {msg}"
);
}
_ => panic!("expected DocError::Extension with duplicate name, got {result:?}"),
}
assert!(
!output.path().join("page.html").exists(),
"no output should be written when build fails"
);
}
#[test]
fn processor_works_in_watch_path() {
let input = tempfile::tempdir().expect("create input tempdir");
let templates = tempfile::tempdir().expect("create templates tempdir");
let output = tempfile::tempdir().expect("create output tempdir");
fs::write(input.path().join("page.md"), "# Test\n\nInitial.\n").expect("write page.md");
fs::write(
templates.path().join("page.html"),
"{{ page.content | safe }}",
)
.expect("write page.html");
let builder = Builder::new(input.path())
.templates(templates.path())
.output(output.path())
.default_template("page.html")
.processor(MarkerProcessor);
let mut watcher = builder.watch().expect("initial watch build should succeed");
let initial = fs::read_to_string(output.path().join("page.html")).expect("read output file");
assert!(
initial.contains("Processed by marker processor"),
"processor should run during initial watch build: {initial}"
);
sleep(MTIME_SETTLE);
fs::write(input.path().join("page.md"), "# Test\n\nUpdated content.\n")
.expect("rewrite page.md");
let changed = watcher.tick().expect("tick should succeed");
assert_eq!(
changed.len(),
1,
"the page should be rebuilt on tick: {changed:?}"
);
let after = fs::read_to_string(output.path().join("page.html")).expect("read updated output");
assert!(
after.contains("Processed by marker processor"),
"processor should run on watch tick: {after}"
);
assert!(
after.contains("Updated content"),
"updated content should be present: {after}"
);
}
#[test]
fn multiple_processors_run_in_order() {
let input = tempfile::tempdir().expect("create input tempdir");
let templates = tempfile::tempdir().expect("create templates tempdir");
let output = tempfile::tempdir().expect("create output tempdir");
fs::write(input.path().join("page.md"), "# Header\n\nContent.\n").expect("write page.md");
fs::write(
templates.path().join("page.html"),
"{{ page.content | safe }}",
)
.expect("write page.html");
Builder::new(input.path())
.templates(templates.path())
.output(output.path())
.default_template("page.html")
.processor(DuplicateProcessor)
.processor(MarkerProcessor)
.build()
.expect("build should succeed");
let written = fs::read_to_string(output.path().join("page.html")).expect("read output file");
assert!(
written.contains("Processed by marker processor"),
"marker processor should run: {written}"
);
assert!(
written.contains("Duplicate"),
"duplicate processor output should be present: {written}"
);
let header_count = written.matches("<h1").count();
assert!(
header_count >= 2,
"duplicate processor should have created multiple copies of the header: {written}"
);
}