fix: always report broken internal links + show source pages
Previously, broken links that were part of the baseline ("known broken")
disappeared from the terminal output (score=0, no Klartext mention).
- differ.py: build reverse link index to record which pages link to
each broken URL; "pages" field added to new_broken/known_broken entries
- plain.py: klartext_befunde() now includes known_broken_urls alongside
new_broken_urls — broken links are always reported until fixed
- __main__.py: report shows "gefunden auf: <page>" for every broken entry,
known entries labelled "[bekannt, noch nicht behoben]"
- 3 new tests (169 total)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
30ac95a5cf
commit
b01344dcd8
5 changed files with 57 additions and 6 deletions
|
|
@ -299,8 +299,12 @@ def _render_markdown(r: dict) -> str:
|
|||
lines += ["", "## Kaputte Links", ""]
|
||||
for entry in new_broken:
|
||||
lines.append(f"- [NEU] HTTP {entry['status']} `{entry['url']}`")
|
||||
for page in entry.get("pages", [])[:3]:
|
||||
lines.append(f" - gefunden auf: {page}")
|
||||
for entry in known_broken:
|
||||
lines.append(f"- [bekannt] HTTP {entry['status']} `{entry['url']}`")
|
||||
lines.append(f"- [bekannt, noch nicht behoben] HTTP {entry['status']} `{entry['url']}`")
|
||||
for page in entry.get("pages", [])[:3]:
|
||||
lines.append(f" - gefunden auf: {page}")
|
||||
|
||||
if d.get("unexpected_jsonld"):
|
||||
lines += ["", "## Unerwartetes JSON-LD", ""]
|
||||
|
|
|
|||
|
|
@ -216,12 +216,27 @@ def compare_snapshots(baseline_pages: dict, current_pages: dict, cfg: dict) -> d
|
|||
if p.get("status", 0) >= 400
|
||||
}
|
||||
current_broken = set(current_broken_map)
|
||||
|
||||
# Reverse index: broken_url → pages that contain a link to it
|
||||
link_sources: dict[str, list[str]] = {}
|
||||
for page_url, page in current_pages.items():
|
||||
for link_list in page.get("links", {}).values():
|
||||
if not isinstance(link_list, list):
|
||||
continue
|
||||
for link in link_list:
|
||||
if isinstance(link, dict):
|
||||
href = link.get("url", "")
|
||||
if href:
|
||||
link_sources.setdefault(href, []).append(page_url)
|
||||
|
||||
new_broken = [
|
||||
{"url": url, "status": current_broken_map[url]}
|
||||
{"url": url, "status": current_broken_map[url],
|
||||
"pages": sorted(set(link_sources.get(url, [])))}
|
||||
for url in sorted(current_broken - baseline_broken)
|
||||
]
|
||||
known_broken = [
|
||||
{"url": url, "status": current_broken_map[url]}
|
||||
{"url": url, "status": current_broken_map[url],
|
||||
"pages": sorted(set(link_sources.get(url, [])))}
|
||||
for url in sorted(current_broken & baseline_broken)
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -144,11 +144,14 @@ def klartext_befunde(
|
|||
"Nach einem geplanten Update ist das normal — sonst bitte prüfen lassen."
|
||||
)
|
||||
|
||||
# --- Kaputte Links (rein informativ) ---
|
||||
# --- Kaputte Links (immer melden, bis behoben) ---
|
||||
neue_kaputt = diff.get("new_broken_urls", [])
|
||||
if neue_kaputt:
|
||||
bekannte_kaputt = diff.get("known_broken_urls", [])
|
||||
alle_kaputt = len(neue_kaputt) + len(bekannte_kaputt)
|
||||
if alle_kaputt:
|
||||
saetze.append(
|
||||
f"{len(neue_kaputt)} interne Link(s) führen ins Leere (Seite nicht gefunden)."
|
||||
f"{alle_kaputt} interne Link(s) führen ins Leere (Seite nicht gefunden). "
|
||||
"Bitte prüfen und den Verweis auf der betroffenen Seite korrigieren."
|
||||
)
|
||||
if ext_links and ext_links.get("broken"):
|
||||
saetze.append(
|
||||
|
|
|
|||
|
|
@ -234,6 +234,20 @@ class TestBrokenLinks:
|
|||
assert len(result["new_broken_urls"]) == 1
|
||||
assert result["new_broken_urls"][0]["url"] == "https://b.info/missing/"
|
||||
assert result["new_broken_urls"][0]["status"] == 404
|
||||
assert "pages" in result["new_broken_urls"][0]
|
||||
|
||||
def test_broken_url_includes_source_pages(self):
|
||||
"""broken entry lists the pages that link to the broken URL."""
|
||||
home = self._make_page("https://b.info/")
|
||||
home["links"]["a"] = [{"url": "https://b.info/missing/", "class": "internal"}]
|
||||
baseline = {"https://b.info/": self._make_page("https://b.info/")}
|
||||
current = {
|
||||
"https://b.info/": home,
|
||||
"https://b.info/missing/": self._make_page("https://b.info/missing/", status=404),
|
||||
}
|
||||
result = compare_snapshots(baseline, current, BASE_CFG)
|
||||
entry = result["new_broken_urls"][0]
|
||||
assert "https://b.info/" in entry["pages"]
|
||||
|
||||
def test_known_broken_not_in_new(self):
|
||||
broken_page = {"https://b.info/old/": self._make_page("https://b.info/old/", status=404)}
|
||||
|
|
@ -242,6 +256,7 @@ class TestBrokenLinks:
|
|||
result = compare_snapshots(baseline, current, BASE_CFG)
|
||||
assert result["new_broken_urls"] == []
|
||||
assert len(result["known_broken_urls"]) == 1
|
||||
assert "pages" in result["known_broken_urls"][0]
|
||||
|
||||
def test_broken_url_scores(self):
|
||||
result = score_diff({
|
||||
|
|
|
|||
|
|
@ -70,6 +70,20 @@ class TestKlartextBefunde:
|
|||
assert any("funktionieren" in s.lower() or "website" in s.lower() for s in saetze)
|
||||
_no_jargon(saetze)
|
||||
|
||||
def test_new_broken_internal_link_plain(self):
|
||||
diff = {"new_broken_urls": [{"url": "https://x.de/gone", "status": 404, "pages": []}]}
|
||||
saetze = klartext_befunde(diff)
|
||||
assert any("leere" in s.lower() or "gefunden" in s.lower() for s in saetze)
|
||||
_no_jargon(saetze)
|
||||
|
||||
def test_known_broken_link_still_reported(self):
|
||||
"""known_broken_urls must appear in Klartext even with score=0."""
|
||||
diff = {"known_broken_urls": [{"url": "https://x.de/old", "status": 404, "pages": []}]}
|
||||
saetze = klartext_befunde(diff)
|
||||
assert any("leere" in s.lower() or "gefunden" in s.lower() for s in saetze), \
|
||||
"known_broken_urls müssen auch bei Score=0 im Klartext erscheinen"
|
||||
_no_jargon(saetze)
|
||||
|
||||
|
||||
class TestWasTun:
|
||||
def test_red_has_urgent_steps(self):
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue