feature(1100): Discover .markdown posts and always log run summary

Extend the anniversary matcher to recursively scan both .md and
.markdown posts under _posts/blog, retaining the existing filename
validation and relative-path dedupe identifier so published-state
bookkeeping keeps working.

Drop the zero-counter short-circuit in log_run_summary so the
run_complete line is always emitted on a successful pass. Debug and
manual runs now clearly show that scanning completed and found
nothing instead of going silent.

Update matching, logging, and run-logging tests to cover the new
extension and always-emit contract, and refresh the logging
architecture doc accordingly.
This commit is contained in:
m0rph3us1987
2026-08-05 11:58:04 +02:00
parent 5f6ddf76a4
commit f243f2213c
6 changed files with 79 additions and 12 deletions
+11 -2
View File
@@ -61,10 +61,19 @@ def test_log_run_summary_emits_one_line_with_counters() -> None:
assert payload["posted_ids"] == ["x"]
def test_log_run_summary_silent_when_no_matches() -> None:
def test_log_run_summary_always_emits_summary_line_with_zero_counters() -> None:
buf = _capture(logging.getLogger("tenbackward"))
log_run_summary(0, 0, 0, 0, [])
assert buf.getvalue() == ""
lines = [line for line in buf.getvalue().splitlines() if line.strip()]
assert len(lines) == 1
payload = json.loads(lines[0])
assert payload["message"] == "run complete"
assert payload["event"] == "run_complete"
assert payload["scanned"] == 0
assert payload["matched"] == 0
assert payload["posted"] == 0
assert payload["skipped"] == 0
assert payload["posted_ids"] == []
def test_log_startup_emits_single_info_line() -> None:
+42
View File
@@ -222,6 +222,48 @@ def test_missing_post_root_returns_empty(tmp_path: Path) -> None:
assert find_anniversary_matches(tmp_path / "does-not-exist", SITE_URL, today=today) == []
def test_markdown_extension_is_discovered(tmp_path: Path) -> None:
root = tmp_path / "_posts" / "blog"
_write_post(
root,
"2014/2014-08-04-foo.markdown",
body="---\ntitle: Foo\ndate: 2014-08-04\n---\nbody\n",
)
today = date(2024, 8, 4)
matches = find_anniversary_matches(root, SITE_URL, today=today)
assert len(matches) == 1
match = matches[0]
assert match.path == "2014/2014-08-04-foo.markdown"
assert match.title == "Foo"
assert match.date == date(2014, 8, 4)
assert match.url == "https://chaospott.de/2014/08/04/foo/"
def test_markdown_and_md_are_discovered_together(tmp_path: Path) -> None:
root = tmp_path / "_posts" / "blog"
_write_post(
root,
"2014/2014-08-04-from-md.md",
body="---\ntitle: Md\ndate: 2014-08-04\n---\n",
)
_write_post(
root,
"2014/2014-08-04-from-markdown.markdown",
body="---\ntitle: Markdown\ndate: 2014-08-04\n---\n",
)
today = date(2024, 8, 4)
matches = find_anniversary_matches(root, SITE_URL, today=today)
paths = {m.path for m in matches}
assert paths == {
"2014/2014-08-04-from-md.md",
"2014/2014-08-04-from-markdown.markdown",
}
def test_invalid_filename_is_skipped(tmp_path: Path) -> None:
root = tmp_path / "_posts" / "blog"
_write_post(root, "2014/not-a-date.md", body="---\ntitle: Bad\n---\n")
+8 -2
View File
@@ -80,7 +80,7 @@ def test_run_emits_one_info_summary_on_success(env_setup, data_dir, capture_logg
assert "already-1" in state
def test_run_silent_when_no_matches(env_setup, data_dir, capture_logger, monkeypatch) -> None:
def test_run_emits_zero_summary_when_no_matches(env_setup, data_dir, capture_logger, monkeypatch) -> None:
monkeypatch.setenv("DATA_DIR", str(data_dir))
monkeypatch.setattr(main_module, "ensure_repo", lambda *a, **kw: None)
monkeypatch.setattr(main_module, "_iter_candidates", lambda config: [])
@@ -90,7 +90,13 @@ def test_run_silent_when_no_matches(env_setup, data_dir, capture_logger, monkeyp
lines = _run_lines(capture_logger)
summary = [line for line in lines if line.get("event") == "run_complete"]
assert summary == []
assert len(summary) == 1
payload = summary[0]
assert payload["scanned"] == 0
assert payload["matched"] == 0
assert payload["posted"] == 0
assert payload["skipped"] == 0
assert payload["posted_ids"] == []
def test_run_returns_nonzero_on_pipeline_error(env_setup, data_dir, capture_logger, monkeypatch) -> None: