import pruefung import struktur def _pruefe(bereich, feld, text): return struktur.befunde(bereich, {feld: text}) def _probleme(befunde): return [b["problem"] for b in befunde] def test_satzzeichen_am_zeilenende(): b = _pruefe("funktion", "regeln", "- Passwort nie loggen.")[0] assert b["problem"] == "Satzzeichen am Zeilenende" assert b["vorschlag"] == "Passwort nie loggen" def test_punkt_im_wort_ist_kein_satzende(): # Der Prüfer meldete hier „Punkt am Zeilenende" — die Zeile endet auf „v" assert _pruefe("funktion", "regeln", "- API Zugänge stehen in .env") == [] assert _pruefe("funktion", "regeln", "- Nur kern und optional erlaubt") == [] def test_fuellwort_wird_entfernt(): b = _pruefe("funktion", "regeln", "- Auch Features entfernen")[0] assert b["problem"] == "Füllwort „auch“" assert b["vorschlag"] == "Features entfernen" def test_liste_der_fuellwoerter_ist_abschliessend(): # Das Modell hielt „Im", „Nur" und „direkt" für Füllwörter — sind sie nicht for zeile in ("- Im Modal Namen eingeben", "- Nur wenige Wörter", "- Feature direkt in Vollmodus geöffnet"): assert _pruefe("funktion", "regeln", zeile) == [] def test_wortgrenze_je_bereich_und_feld(): assert _probleme(_pruefe("feature", "titel", "Karten der Features prüfen")) \ == ["Mehr als 3 Wörter"] assert _pruefe("funktion", "titel", "Karten der Features prüfen") == [] assert _probleme(_pruefe("funktion", "testen", "- Eins zwei drei vier fünf sechs sieben acht neun")) \ == ["Mehr als 8 Wörter"] def test_ohne_vorschlag_wenn_der_code_nicht_kuerzen_kann(): b = _pruefe("funktion", "testen", "- Eins zwei drei vier fünf sechs sieben acht neun")[0] assert b["vorschlag"] == "" # den Ersatz schreibt das Modell def test_klammer_und_schraegstrich(): assert _probleme(_pruefe("funktion", "regeln", "- Rand blau (kern)")) \ == ["Klammer oder Schrägstrich"] assert _probleme(_pruefe("funktion", "regeln", "- Kern/Optional getrennt")) \ == ["Klammer oder Schrägstrich"] def test_ziel_saetze_und_laenge(): lang = "Dieses Ziel hat deutlich mehr als zwölf Wörter in einem einzigen langen Satz" assert "Satz mit mehr als 12 Wörtern" in _probleme(_pruefe("feature", "ziel", lang)) assert _probleme(_pruefe("feature", "ziel", "Eins. Zwei. Drei")) == ["Mehr als 2 Sätze"] def test_badge_nur_kern_oder_optional(): assert _pruefe("feature", "badge", "optional") == [] b = _pruefe("feature", "badge", "wichtig")[0] assert b["vorschlag"] == "kern" def test_jede_zeile_einzeln(): befunde = _pruefe("funktion", "regeln", "- Erste Regel.\n- Auch zweite Regel") assert _probleme(befunde) == ["Satzzeichen am Zeilenende", "Füllwort „auch“"] def test_ein_befund_je_zeile(): # Zwei Vorschläge für dieselbe Zeile wären nicht unabhängig anwendbar befunde = _pruefe("funktion", "regeln", "- Auch Passwort prüfen.") assert len(befunde) == 1 assert befunde[0]["problem"] == "Satzzeichen am Zeilenende, Füllwort „auch“" assert befunde[0]["vorschlag"] == "Passwort prüfen" def test_unreparierbares_zieht_den_ganzen_befund_zum_modell(): # Zu lang und Füllwort: der Code streicht nicht halb, das Modell schreibt neu b = _pruefe("funktion", "testen", "- Auch eins zwei drei vier fünf sechs sieben acht")[0] assert b["problem"] == "Füllwort „auch“, Mehr als 8 Wörter" assert b["vorschlag"] == "" def test_auftrag_nennt_feld_und_stelle(): b = _pruefe("feature", "titel", "Karten der Features prüfen")[0] assert struktur.auftrag(b) == '- titel „Karten der Features prüfen“: Mehr als 3 Wörter' async def test_messung_ist_auftrag_und_tuersteher(datenordner, monkeypatch): """Die Messung geht als Auftrag rein und prüft die Antwort wieder nach. Ein Vorschlag, der weiter verstößt, wird zurückgewiesen — mit Begründung.""" import agents import features import projekte projekte.anlegen("Test") f = features.anlegen( "Test", "\nLogin\n\nAnmelden\n" "\n- Auch Passwort prüfen.\n", ) gesehen = [] # Jede Stimme liefert erst einen halbherzigen Vorschlag: Punkt weg, Füllwort bleibt halb = '{"felder": {"regeln": {"neu": "- Auch Passwort prüfen", "grund": "Füllwort"}}}' ganz = '{"felder": {"regeln": {"neu": "- Passwort prüfen", "grund": "Füllwort"}}}' async def modell(system, inhalt, temperatur=0.0): gesehen.append(inhalt) return ganz if "Dein Vorschlag verstößt" in inhalt else halb monkeypatch.setattr(agents, "frage", modell) aufgabe = pruefung.aufgaben(f)[1] probleme = await pruefung._pruefe({"funktion": ""}, aufgabe) # Eine Anfrage je Stimme, dazu je eine Nachbesserung assert len(gesehen) == 2 * len(pruefung.TEMPERATUREN) assert "Gemessen" in gesehen[0] assert "Füllwort „auch“" in gesehen[0] assert sum("Dein Vorschlag verstößt" in g for g in gesehen) == len(pruefung.TEMPERATUREN) assert [p["original"] for p in probleme] == ["Auch Passwort prüfen."] assert probleme[0]["vorschlag"] == "Passwort prüfen" assert probleme[0]["feld"] == "regeln" assert probleme[0]["funktion"] == "Anmelden" async def test_gemessener_verstoss_wird_notfalls_selbst_behoben(datenordner, monkeypatch): """Ein gezählter Verstoß steht fest. Liefert keine Stimme einen brauchbaren Ersatz, greift der Ersatz des Messers — sonst bliebe der Fehler stehen.""" import agents import features import projekte projekte.anlegen("Test") f = features.anlegen( "Test", "\nLogin\n\nAnmelden\n" "\n- Auch Passwort prüfen.\n", ) async def sturr(system, inhalt, temperatur=0.0): return '{"felder": {"regeln": {"neu": "- Auch anders.", "grund": "x"}}}' monkeypatch.setattr(agents, "frage", sturr) probleme = await pruefung._pruefe({"funktion": ""}, pruefung.aufgaben(f)[1]) assert [p["vorschlag"] for p in probleme] == ["Passwort prüfen"] assert probleme[0]["regel"] == "Vom System gemessen" def test_gemessene_befunde_haengen_am_ausschnitt(datenordner): import features import projekte projekte.anlegen("Test") f = features.anlegen("Test", "\nEin viel zu langer Kartentitel\n") karte = pruefung.aufgaben(f)[0] befunde = struktur.befunde("feature", karte["felder"]) assert "Gemessen" not in pruefung.inhalt(karte) text = pruefung.inhalt(karte, befunde) assert "Gemessen, bitte neu schreiben:" in text assert "Mehr als 3 Wörter" in text def test_funktionstitel_braucht_ein_verb(): """Ohne Verb steht dort ein Bereich oder ein Zustand — nichts Testbares.""" for titel in ("Galerie im Modal", "Preis aktualisiert", "In den Warenkorb", "Modal tastaturgesteuert"): assert _probleme(_pruefe("funktion", "titel", titel)) \ == ["Kein Verb im Infinitiv am Ende"], titel def test_gute_funktionstitel_bleiben_unberuehrt(): for titel in ("Feature anlegen", "Anzeigemodus umschalten", "Option wählen", "Nächsten Rückschritt anzeigen", "Karten prüfen", "Stand umschalten", "Projekt aufrufen", "Bildergalerie bedienen", "PDF hochladen"): assert _pruefe("funktion", "titel", titel) == [], titel def test_kartentitel_darf_ohne_verb_stehen(): """Eine Karte ist ein Gegenstand, keine Handlung.""" assert _pruefe("feature", "titel", "Karten prüfen") == [] assert _pruefe("feature", "titel", "Darkmode") == [] ABLAUF = "- Vollmodus öffnen\n- LMB auf Regeln\n- Text ändern\n- Regeln angepasst" def test_ein_ablauf_geht_durch(): assert _pruefe("funktion", "testen", ABLAUF) == [] def test_zweiter_ablauf_wird_gemeldet(): """So entstand „Bildergalerie bedienen": blättern, Miniatur, Großansicht in einem.""" zwei = ("- Zurück-Button klicken\n- Vorheriges Bild erscheint\n" "- Miniaturbild klicken\n- Zugehöriges Bild erscheint") b = _pruefe("funktion", "testen", zwei) assert _probleme(b) == ["Zweiter Ablauf in einer Funktion"] assert b[0]["original"] == "Miniaturbild klicken" def test_nominalstil_ist_eine_handlung(): """„Bestätigen im Modal" hat das Verb vorn — sonst gälte es als Ergebnis.""" text = ("- RMB auf Featurecard\n- Klick auf \"Feature entfernen\"\n" "- Bestätigen im Modal\n- Feature entfernt") assert _pruefe("funktion", "testen", text) == [] def test_mehrzahl_auf_en_ist_kein_verb(): """Deutsche Mehrzahl endet auch auf -en — nur Anfang und Ende zählen.""" text = "- LMB auf Icon\n- Fehlerhafte Karten rot markiert" assert _pruefe("funktion", "testen", text) == [] def test_zeilenfehler_haben_vorrang(): """Erst die Zeilen in Ordnung bringen; zwei Vorschläge je Feld wären nicht unabhängig anwendbar.""" text = "- Auch klicken.\n- Ergebnis da\n- Weiter klicken" assert _probleme(_pruefe("funktion", "testen", text)) \ == ["Satzzeichen am Zeilenende, Füllwort „auch“"] def test_funktion_ohne_jedes_feld_ist_leer(): """Ein bloßer Titel beschreibt nichts — der Prüfer stülpt ihn über alles.""" b = struktur.befunde("funktion", {"titel": "Modal steuern", "ziel": "", "regeln": "", "testen": ""}) assert _probleme(b) == ["Funktion ohne Ziel, Regeln und Testen"] def test_ein_einziges_feld_genuegt(): for feld in ("ziel", "regeln", "testen"): felder = {"titel": "Modal steuern", "ziel": "", "regeln": "", "testen": ""} felder[feld] = "Etwas tun" if feld == "ziel" else "- Etwas tun" assert struktur.befunde("funktion", felder) == [], feld def test_einzelnes_feld_loest_nichts_aus(): """Beim Prüfen eines Feldes sagt das Fehlen der anderen nichts.""" assert struktur.befunde("funktion", {"titel": "Modal steuern"}) == [] def test_kartenebene_darf_leer_sein(): assert struktur.befunde("feature", {"titel": "Darkmode", "badge": "kern", "ziel": "", "regeln": ""}) == [] def test_platzhalter_ist_kein_inhalt(): """`` erfüllt jede Formregel und sagt trotzdem nichts.""" assert struktur._umschreiben("funktion", "testen", "") assert struktur._umschreiben("funktion", "regeln", "Noch offen ...") assert struktur._umschreiben("funktion", "regeln", "TODO klären") def test_kleiner_als_ist_kein_platzhalter(): assert not struktur._umschreiben("funktion", "regeln", "Wert 3 < 5")