mod common;
use common::{corpus_a, dbmd, write_file};
const TWO_SECTIONS: &str = "\
---
type: wiki-page
summary: a page
---
# Title
Intro paragraph.
## Timeline
- a point
### Sub-detail
more.
## Commercials
closing.
";
#[test]
fn text_lists_headings_indented_by_depth() {
let tmp = tempfile::TempDir::new().unwrap();
let file = write_file(tmp.path(), "page.md", TWO_SECTIONS);
let out = dbmd().arg("sections").arg(&file).assert().success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
let expected = "Timeline (L6)\n Sub-detail (L10)\nCommercials (L14)\n";
assert_eq!(stdout, expected);
}
#[test]
fn json_emits_heading_level_line_array() {
let tmp = tempfile::TempDir::new().unwrap();
let file = write_file(tmp.path(), "page.md", TWO_SECTIONS);
let out = dbmd()
.arg("--json")
.arg("sections")
.arg(&file)
.assert()
.success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("valid JSON");
let expected = serde_json::json!([
{ "heading": "Timeline", "level": 2, "line": 6 },
{ "heading": "Sub-detail", "level": 3, "line": 10 },
{ "heading": "Commercials", "level": 2, "line": 14 },
]);
assert_eq!(parsed, expected);
}
#[test]
fn file_with_no_h2_sections_prints_nothing() {
let tmp = tempfile::TempDir::new().unwrap();
let file = write_file(
tmp.path(),
"flat.md",
"---\ntype: wiki-page\nsummary: s\n---\n\n# Only a title\n\nJust prose.\n",
);
let out = dbmd().arg("sections").arg(&file).assert().success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
assert_eq!(stdout, "", "no sections → empty stdout (pipe-safe)");
}
#[test]
fn fenced_code_headings_are_not_sections() {
let tmp = tempfile::TempDir::new().unwrap();
let file = write_file(
tmp.path(),
"code.md",
"---\ntype: wiki-page\nsummary: s\n---\n\n## Real\n\n```\n## not a heading\n```\n\n## Also real\n",
);
let out = dbmd().arg("sections").arg(&file).assert().success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
assert!(stdout.contains("Real"));
assert!(stdout.contains("Also real"));
assert!(
!stdout.contains("not a heading"),
"a `##` inside a code fence is not a section: {stdout:?}"
);
assert_eq!(stdout.lines().count(), 2, "exactly the two real headings");
}
#[test]
fn corpus_a_wiki_page_sections_are_seen() {
let file = corpus_a().join("wiki/projects/northstar-renewal.md");
let out = dbmd()
.arg("--json")
.arg("sections")
.arg(&file)
.assert()
.success();
let stdout = String::from_utf8(out.get_output().stdout.clone()).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("valid JSON array");
let arr = parsed.as_array().expect("array");
let headings: Vec<&str> = arr
.iter()
.filter_map(|s| s.get("heading").and_then(|h| h.as_str()))
.collect();
assert_eq!(
headings,
vec!["Timeline", "Commercials"],
"the two H2 sections, in document order"
);
assert!(arr
.iter()
.all(|s| s.get("level").and_then(|l| l.as_u64()) == Some(2)));
}
#[test]
fn missing_file_is_runtime_error_nonzero_exit() {
let tmp = tempfile::TempDir::new().unwrap();
let missing = tmp.path().join("does-not-exist.md");
dbmd()
.arg("sections")
.arg(&missing)
.assert()
.failure()
.code(1); }