feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
"""Tests für die Klartext-Schicht (laienverständliche Ausgabe)."""
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
from scanner.plain import ampel, ampel_zeile, klartext_befunde, was_tun
|
|
|
|
|
|
|
|
|
|
# Fachbegriffe, die in der Klartext-Ausgabe NICHT vorkommen dürfen
|
|
|
|
|
JARGON = ["Score", "Canonical", "JSON-LD", "Meta-Refresh", "Hash", "SHA-256",
|
|
|
|
|
"Whitelist", "Hidden Content", "Exit-Code"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _no_jargon(saetze):
|
|
|
|
|
text = " ".join(saetze)
|
|
|
|
|
treffer = [w for w in JARGON if w.lower() in text.lower()]
|
|
|
|
|
assert not treffer, f"Fachbegriff(e) in Klartext gefunden: {treffer}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestAmpel:
|
|
|
|
|
@pytest.mark.parametrize("level", ["green", "yellow", "red"])
|
|
|
|
|
def test_each_level_has_symbol_and_sentence(self, level):
|
|
|
|
|
symbol, satz = ampel(level)
|
|
|
|
|
assert symbol
|
|
|
|
|
assert satz.endswith(".") or satz.endswith(")")
|
|
|
|
|
|
|
|
|
|
def test_unknown_level_safe(self):
|
|
|
|
|
symbol, satz = ampel("bogus")
|
|
|
|
|
assert symbol and satz
|
|
|
|
|
|
|
|
|
|
def test_ampel_zeile_combines(self):
|
|
|
|
|
zeile = ampel_zeile("green")
|
|
|
|
|
assert zeile.startswith("🟢")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestKlartextBefunde:
|
|
|
|
|
def test_empty_diff_says_all_good(self):
|
|
|
|
|
saetze = klartext_befunde({})
|
|
|
|
|
assert any("nichts" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
|
|
|
|
def test_new_external_domain_plain(self):
|
|
|
|
|
saetze = klartext_befunde({"new_external_domains": ["spam.example.com"]})
|
|
|
|
|
assert any("fremde" in s.lower() or "adresse" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
|
|
|
|
def test_hidden_content_plain(self):
|
|
|
|
|
diff = {"page_diffs": [{"url": "x", "new_hidden_content": [{"type": "x"}]}]}
|
|
|
|
|
saetze = klartext_befunde(diff)
|
|
|
|
|
assert any("unsichtbar" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
|
|
|
|
def test_suspicious_filename_is_serious(self):
|
|
|
|
|
saetze = klartext_befunde({"suspicious_filenames": [{"page": "x", "url": "shell.php"}]})
|
|
|
|
|
assert any("warnzeichen" in s.lower() or "verdächtig" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
|
|
|
|
def test_cloaking_extra_link_plain(self):
|
|
|
|
|
cloak = {"findings": [{"url": "x", "extra_links": ["https://spam.com/x"], "extra_text_chars": 0}]}
|
|
|
|
|
saetze = klartext_befunde({}, cloak_diff=cloak)
|
|
|
|
|
assert any("google" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
|
|
|
|
def test_asset_change_plain(self):
|
|
|
|
|
asset = {"changed": [{"url": "x", "type": "script"}]}
|
|
|
|
|
saetze = klartext_befunde({}, asset_diff=asset)
|
|
|
|
|
assert any("datei" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
|
|
|
|
def test_broken_ext_links_plain(self):
|
|
|
|
|
ext = {"broken": 3}
|
|
|
|
|
saetze = klartext_befunde({}, ext_links=ext)
|
|
|
|
|
assert any("funktionieren" in s.lower() or "website" in s.lower() for s in saetze)
|
|
|
|
|
_no_jargon(saetze)
|
|
|
|
|
|
2026-06-12 12:18:38 +02:00
|
|
|
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)
|
|
|
|
|
|
feat: Laientauglichkeit — Auto-Modus, Klartext-Ausgabe, einfachere Doku
Ziel: ein einziger täglicher Befehl genügt, jede Meldung ist auch ohne
IT-Wissen verständlich.
Auto-Modus (ein Cron-Job genügt für alles):
- data/state.json merkt sich, wann die Wochen-Prüfungen zuletzt liefen
(baseline.py: load_state/save_state/is_check_due/mark_check_run)
- `scan` führt fällige Zusatzprüfungen (Tarnung/externe Links/Dateien)
automatisch mit aus; Gesamt-Ampel = schlechtestes Teilergebnis;
EIN kombinierter Report + EIN Alert
- neuer Config-Abschnitt periodic_checks (Default: wöchentlich, an)
Klartext (scanner/plain.py):
- Ampel 🟢/🟡/🔴 + Sätze ohne Fachbegriffe, aus den strukturierten Befunden
- Report: Klartext oben, "Technische Details (für Ihren Dienstleister)" unten
- Terminal-Ausgabe und E-Mail (alerter.py) ebenso umgestellt
Komfort:
- approve --all/--rebuild legt die Datei-Überwachung automatisch mit an
(Opt-out: --skip-assets); approve-assets bleibt für den Sonderfall
- status zeigt Ampel zuoberst + wann die Wochen-Prüfungen zuletzt liefen
- Onboarding-Text in einfacher Sprache, wenn noch kein Vergleichsstand existiert
Doku: README + BEDIENUNGSANLEITUNG mit "In 3 Schritten"-Einstieg.
Tests: test_state.py, test_plain.py (Fachbegriff-Assertion), Auto-Asset-
Baseline-Test. 165 Tests grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 11:14:05 +02:00
|
|
|
|
|
|
|
|
class TestWasTun:
|
|
|
|
|
def test_red_has_urgent_steps(self):
|
|
|
|
|
schritte = was_tun("red")
|
|
|
|
|
assert any("betreut" in s.lower() or "informieren" in s.lower() for s in schritte)
|
|
|
|
|
|
|
|
|
|
def test_green_needs_nothing(self):
|
|
|
|
|
schritte = was_tun("green")
|
|
|
|
|
assert any("nichts" in s.lower() for s in schritte)
|
|
|
|
|
|
|
|
|
|
def test_no_jargon_anywhere(self):
|
|
|
|
|
for lvl in ("green", "yellow", "red"):
|
|
|
|
|
_no_jargon(was_tun(lvl))
|