test: Unit-Tests für normalizeForComparison, parseVerdict, parseBlockers

28 Tests für die drei reinen Hilfsfunktionen aus pi-coder-judge-extension.ts.
run-tests.sh führt test-utils.ts ohne ts-node aus (sed-basiertes TS→JS-Stripping).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Dieter Schlüter 2026-05-29 18:14:03 +02:00
commit 7b13c4996d
2 changed files with 220 additions and 0 deletions

27
run-tests.sh Executable file
View file

@ -0,0 +1,27 @@
#!/usr/bin/env bash
# Unit-Tests für pi-coder-judge-extension.ts
# Keine Abhängigkeiten außer node — entfernt TypeScript-Annotationen on-the-fly.
set -euo pipefail
TS_FILE="$(dirname "$0")/test-utils.ts"
if ! command -v node &>/dev/null; then
echo "❌ node nicht gefunden" >&2
exit 1
fi
# TypeScript-Annotationen entfernen: `: string`, `: unknown`, `: void`, `: boolean`
# und `function f(a: T, b: T)` → `function f(a, b)` (einfache Parameterlisten)
node --input-type=module < <(
sed \
-e 's/: string\b//g' \
-e 's/: unknown\b//g' \
-e 's/: void\b//g' \
-e 's/: boolean\b//g' \
-e 's/: number\b//g' \
-e 's/(s: )/(s)/g' \
-e 's/(text: )/(text)/g' \
-e 's/(actual, expected: unknown, label: string)/( actual, expected, label)/g' \
"$TS_FILE"
)

193
test-utils.ts Normal file
View file

@ -0,0 +1,193 @@
// Unit-Tests für reine Hilfsfunktionen aus pi-coder-judge-extension.ts
//
// Ausführung (TypeScript):
// npx ts-node test-utils.ts
//
// Ausführung ohne ts-node (schneller):
// node --input-type=module < <(sed 's/: string//g; s/: unknown//g; s/: void//g; s/: boolean//g' test-utils.ts)
//
// Oder: Funktionen aus dieser Datei kopieren und als .js ausführen.
// ── Funktionen (aus Extension kopiert, kein pi-API-Import nötig) ─────────────
function normalizeForComparison(s: string): string {
return s.trim().replace(/\s+/g, " ").replace(/[.,;:!?]+$/g, "").toLowerCase();
}
function parseVerdict(text: string): string {
const m = text.match(/Urteil:\s*(PASS WITH CONCERNS|PASS|FAIL)/i);
return m ? m[1].toUpperCase() : "UNREADABLE";
}
function parseBlockers(text: string): string {
const m = text.match(
/(?:\*\*Blocker\*\*|##\s*Blocker|[-*]\s*Blocker)[:\n]([\s\S]*?)(?:\n(?:\*\*Major\*\*|##\s*Major|[-*]\s*Major)|\n(?:\*\*Minor\*\*|##\s*Minor|[-*]\s*Minor)|$)/i
);
return m ? m[1].trim() : "";
}
// ── Test-Harness ──────────────────────────────────────────────────────────────
let passed = 0;
let failed = 0;
function expect(actual: unknown, expected: unknown, label: string): void {
if (actual === expected) {
console.log(`${label}`);
passed++;
} else {
console.error(`${label}`);
console.error(` erwartet: ${JSON.stringify(expected)}`);
console.error(` erhalten: ${JSON.stringify(actual)}`);
failed++;
}
}
// ── normalizeForComparison ────────────────────────────────────────────────────
console.log("\nnormalizeForComparison()");
expect(normalizeForComparison(" Foo Bar "), "foo bar",
"trimmt führende/nachfolgende Leerzeichen");
expect(normalizeForComparison("Foo.\n"), "foo",
"entfernt trailing Punkt + Newline");
expect(normalizeForComparison("A B"), "a b",
"kollabiert mehrfache Leerzeichen");
expect(normalizeForComparison("Foo:"), "foo",
"entfernt trailing Doppelpunkt");
expect(normalizeForComparison("Foo;"), "foo",
"entfernt trailing Semikolon");
expect(normalizeForComparison("Foo!"), "foo",
"entfernt trailing Ausrufezeichen");
expect(normalizeForComparison("Foo?"), "foo",
"entfernt trailing Fragezeichen");
expect(normalizeForComparison("UPPER CASE"), "upper case",
"konvertiert zu Kleinbuchstaben");
// Loop-Detection: gleiche Blocker nach Normalisierung erkannt
expect(
normalizeForComparison("missing error handling.") ===
normalizeForComparison("missing error handling"),
true,
"Loop-Detection: trailing Punkt macht keinen Unterschied"
);
expect(
normalizeForComparison("null check missing\n") ===
normalizeForComparison("null check missing"),
true,
"Loop-Detection: Newline am Ende macht keinen Unterschied"
);
expect(
normalizeForComparison("Fehler bei Import.") ===
normalizeForComparison("Fehler bei Import"),
true,
"Loop-Detection: mehrfache Leerzeichen + Punkt machen keinen Unterschied"
);
expect(
normalizeForComparison("Blocker A") === normalizeForComparison("Blocker B"),
false,
"Loop-Detection: verschiedene Blocker werden NICHT als gleich erkannt"
);
// ── parseVerdict ──────────────────────────────────────────────────────────────
console.log("\nparseVerdict()");
expect(parseVerdict("Urteil: PASS"), "PASS",
"erkennt PASS");
expect(parseVerdict("Urteil: PASS WITH CONCERNS"), "PASS WITH CONCERNS",
"erkennt PASS WITH CONCERNS (vor PASS gematcht)");
expect(parseVerdict("Urteil: FAIL"), "FAIL",
"erkennt FAIL");
expect(parseVerdict("kein Urteil hier"), "UNREADABLE",
"gibt UNREADABLE zurück wenn kein Urteil");
expect(parseVerdict("urteil: pass"), "PASS",
"case-insensitiv: 'urteil: pass'");
expect(parseVerdict("urteil: Pass With Concerns"), "PASS WITH CONCERNS",
"case-insensitiv: gemischte Groß-/Kleinschreibung");
expect(parseVerdict("Das ist mein Urteil: PASS — und mehr Text dahinter"), "PASS",
"ignoriert Text nach dem Urteil");
expect(parseVerdict("Urteil:PASS"), "PASS",
"toleriert fehlenden Leerzeichen nach Doppelpunkt");
expect(parseVerdict(""), "UNREADABLE",
"leerer String → UNREADABLE");
// ── parseBlockers ─────────────────────────────────────────────────────────────
console.log("\nparseBlockers()");
expect(
parseBlockers("**Blocker**:\n- fehlende Validierung\n**Major**:\n- anderes Problem"),
"- fehlende Validierung",
"erkennt **Blocker** mit Bold-Syntax"
);
expect(
parseBlockers("## Blocker\nNull-Check fehlt\n## Major\nanderes"),
"Null-Check fehlt",
"erkennt ## Blocker mit Heading-Syntax"
);
expect(
parseBlockers("- Blocker:\n- fehlender Import\n- Minor:\n- Stil"),
"- fehlender Import",
"erkennt - Blocker mit Bullet-Syntax"
);
expect(
parseBlockers(" Blocker\nKein Logging\n- Minor\nKleinigkeit"),
"Kein Logging",
"erkennt Blocker (Gedankenstrich)"
);
expect(
parseBlockers("Urteil: PASS\n\nAlles ok."),
"",
"gibt leeren String zurück wenn kein Blocker-Abschnitt"
);
expect(
parseBlockers("**Blocker**:\nkeine\n**Minor**:\n- Stil"),
"keine",
"extrahiert 'keine' als Blocker-Text"
);
// Mehrzeiliger Blocker
const multilineInput = `**Blocker**:
- Import fehlt
- Funktion nicht definiert
**Major**:
- weitere Sache`;
const multilineResult = parseBlockers(multilineInput);
expect(
multilineResult.includes("Import fehlt") && multilineResult.includes("Funktion nicht definiert"),
true,
"extrahiert mehrzeiligen Blocker vollständig"
);
// ── Ergebnis ──────────────────────────────────────────────────────────────────
console.log(`\n${"─".repeat(50)}`);
console.log(`Gesamt: ${passed + failed} Tests — ${passed} bestanden, ${failed} fehlgeschlagen`);
if (failed > 0) {
process.exit(1);
}