--- /dev/null
+from __future__ import annotations
+from pathlib import Path
+from core.models import Article
+from core.frontmatter import parse_frontmatter
+
+def scan_articles(blog_root: Path) -> list[Article]:
+ articles: list[Article] = []
+ by_slug: dict[tuple[str, str], Path] = {}
+
+ for lang in ("it", "en"):
+ content_dir = blog_root / "content" / lang / "articles"
+ if not content_dir.exists():
+ continue
+ for index_md in sorted(content_dir.glob("*/index.md")):
+ slug = index_md.parent.name
+ by_slug[(lang, slug)] = index_md
+
+ for (lang, slug), path in by_slug.items():
+ other_lang = "en" if lang == "it" else "it"
+ translation_path = by_slug.get((other_lang, slug))
+ try:
+ fm, _ = parse_frontmatter(path)
+ except (ValueError, Exception):
+ fm = {}
+ articles.append(Article(
+ slug=slug,
+ lang=lang,
+ path=path,
+ frontmatter=fm,
+ has_translation=translation_path is not None,
+ translation_path=translation_path,
+ ))
+
+ return articles
--- /dev/null
+# tests/test_article_scanner.py
+from pathlib import Path
+from core.article_scanner import scan_articles
+
+def _make_article(base: Path, lang: str, slug: str, fm_extra: str = "") -> Path:
+ p = base / "content" / lang / "articles" / slug
+ p.mkdir(parents=True)
+ (p / "index.md").write_text(f"+++\ntitle = \"{slug}\"\ntype = \"Tech\"\n{fm_extra}+++\n\nBody.\n")
+ return p / "index.md"
+
+def test_scan_finds_articles(tmp_path):
+ _make_article(tmp_path, "it", "primo-post")
+ _make_article(tmp_path, "en", "first-post")
+ articles = scan_articles(tmp_path)
+ slugs = [a.slug for a in articles]
+ assert "primo-post" in slugs
+ assert "first-post" in slugs
+
+def test_scan_detects_translation_pair(tmp_path):
+ _make_article(tmp_path, "it", "shared-slug")
+ _make_article(tmp_path, "en", "shared-slug")
+ articles = scan_articles(tmp_path)
+ it_art = next(a for a in articles if a.lang == "it" and a.slug == "shared-slug")
+ en_art = next(a for a in articles if a.lang == "en" and a.slug == "shared-slug")
+ assert it_art.has_translation is True
+ assert it_art.translation_path == en_art.path
+ assert en_art.has_translation is True
+
+def test_scan_detects_missing_translation(tmp_path):
+ _make_article(tmp_path, "it", "solo-italiano")
+ articles = scan_articles(tmp_path)
+ art = next(a for a in articles if a.slug == "solo-italiano")
+ assert art.has_translation is False
+ assert art.translation_path is None
+
+def test_scan_parses_frontmatter(tmp_path):
+ _make_article(tmp_path, "it", "con-fm", 'tags = ["linux"]\n')
+ articles = scan_articles(tmp_path)
+ art = next(a for a in articles if a.slug == "con-fm")
+ assert art.frontmatter["title"] == "con-fm"
+ assert "linux" in art.frontmatter["tags"]