7.9 KiB
02 – Audits im Detail (self / extension / redesign)
Was die drei eingebauten Audits konkret prüfen, welche Assertions
tests/audit.mjsdarauf aufsetzt und welche Exit-Codes daraus resultieren. API-Basis:01_Debug_API.md.
1. Übersicht
| Audit | API-Funktion | Fokus |
|---|---|---|
| Self | runSelfAudit() |
Konnektivität (11 Levels), Attacks, Skills, Platzierung, AutoWave, Endless |
| Extension | runExtensionAudit() |
Mortar-Splash, Tesla-Chain/Freeze, Reward-Dedup, Blocker-Logik |
| Redesign | runRedesignAudit() |
Kryo, Sniper (Pierce), Mortar-FixImpact, Quantum-Beam, Nano-Spawner |
audit.mjs führt alle drei in einer Headless-Chromium-Session aus, sammelt pageerror-Events und bewertet die Rückgabe-Werte mit eigenen Assertion-Hilfsfunktionen.
2. runSelfAudit() – Struktur & Assertions
2.1 Report-Struktur (Rückgabe der API)
{
"connectivity": [ { "level": 0, "starts": 2, "exit": true, "reachable": true }, … ], // 11 Levels
"attacks": [ { "id": "sniper", "projectiles": 3, "holos": 0, "hurt": 2 }, … ],
"skills": [ { "id": "sniper", "rank": 4 }, … ],
"placement": { "wallAccepted": true, "pathAccepted": false, "count": 1 },
"autoWave": { "state": "playing", "active": true },
"endless": { "state": "endless", "endlessWave": 1 }
}
2.2 Assertions in tests/audit.mjs (assertSelfAudit)
| # | Assertion | Erwartung |
|---|---|---|
| S-conn | L{n} has start |
starts > 0 (pro Level) |
| S-conn | L{n} has exit |
exit === true (pro Level) |
| S-conn | L{n} reachable |
reachable === true (pro Level) |
| S-atk | tower {id}: projectiles=… holos=… hurt=… |
info (Support-/Relay-Türme feuern absichtlich nicht → kein Hard-Fail) |
| S-sk | skill {id} rank>=1 |
rank >= 1 (nach 4× buySkill) |
| S-plc | placement: wallAccepted (tower on free cell) |
wallAccepted === true |
| S-plc | placement: pathAccepted (tower on path rejected) |
pathAccepted === false |
| S-plc | placement: tower count>=1 |
count >= 1 |
| S-aw | autoWave: state / autoWave: active |
info (nur State prüfen) |
| S-ee | endless: state |
info |
| S-ee | endless: endlessWave>=0 |
endlessWave >= 0 |
Hinweis: „Attacks“ und „AutoWave“ sind bewusst Diagnose-/Info-Checks – die harten Pfade laufen über Konnektivität, Skills und Platzierung.
3. runExtensionAudit() – Struktur & Assertions
3.1 Report-Struktur
{
"mortar": { "projectile": true, "hurt": true },
"tesla": { "damaged": 3, "frozen": 2 },
"reward": { "pointsAfterDuplicate": 1, "storedRank": 4, "rankAfterBuy": 4 },
"blockerAlternate":{ "placed": true, "finite": true, "soldPathRestored": true },
"blockerClosed": { "placed": true, "destroyed": true, "pathRestored": true }
}
3.2 Assertions (assertExtensionAudit)
| # | Assertion | Erwartung |
|---|---|---|
| E-mrt | mortar: projectile spawned (may resolve instantly) |
info |
| E-mrt | mortar: both targets hurt (splash) |
hurt === true |
| E-tes | tesla: damaged enemies count |
damaged > 1 |
| E-tes | tesla: frozen count |
frozen > 1 |
| E-rwd | reward: pointsAfterDuplicate>=1 |
pointsAfterDuplicate >= 1 |
| E-rwd | reward: storedRank matches rankAfterBuy |
storedRank === rankAfterBuy |
| E-blt | blockerAlternate: placed / route still finite / path restored after sell |
placed, finite, soldPathRestored (falls Kandidat gefunden) |
| E-blc | blockerClosed: placed / destroyed by enemy / path restored |
placed, destroyed, pathRestored (falls Kandidat gefunden) |
Fehlt ein geeigneter Blocker-Kandidat, wird ein info-Pass („no candidate found (acceptable)") erzeugt – kein Hard-Fail.
4. runRedesignAudit() – Struktur & Assertions
4.1 Report-Struktur
{
"kryo": { "randomLastTarget": true, "visibleSlow": true },
"sniper": { "pierced": 3, "noDot": true, "highestAbsoluteHp": true },
"mortar": { "fixedImpact": true },
"quantum":{ "lineHits": 3, "offLineUntouched": true, "beam": true },
"nano": { "spawned": 5, "damaged": true, "cleaned": true }
}
4.2 Assertions (assertRedesignAudit)
| # | Assertion | Erwartung |
|---|---|---|
| R-kyo | kryo: target hit |
`randomLastTarget |
| R-kyo | kryo: slow applied |
visibleSlow === true |
| R-snp | sniper: pierced count |
pierced > 1 |
| R-snp | sniper: no DoT (burn/poison) |
noDot === true |
| R-snp | sniper: highest absolute HP targeting |
highestAbsoluteHp === true (falls vorhanden) |
| R-mrt | mortar: fixed impact (witness hit, fleeer untouched) |
fixedImpact === true |
| R-qtm | quantum: line hits |
lineHits > 1 |
| R-qtm | quantum: off-line untouched |
offLineUntouched === true |
| R-qtm | quantum: beam created |
beam === true |
| R-nno | nano: spawned count |
spawned > 1 |
| R-nno | nano: damaged target |
damaged === true |
| R-nno | nano: cleaned up |
cleaned === true |
5. audit-result.json – Schema (authoritative)
{
"timestamp": "2026-08-28T19:00:00.000Z",
"file": "…/Aegis-Labyrinth.html",
"fileHash": "a1b2c3d4e5f60718", // sha256, erste 16 Hex-Zeichen
"self": { /* Sektion 2.1 */ },
"extension": { /* Sektion 3.1 */ },
"redesign": { /* Sektion 4.1 */ },
"assertions": {
"self": [ { "name", "actual", "expected", "passed" } ],
"extension": [ … ],
"redesign": [ … ]
},
"pageErrors": [], // gemeldete JS-Fehler (pageerror-Events)
"summary": { "pass": 40, "fail": 0, "total": 42 } // total inkl. PageErrors
}
Das Gesamtverdict in report.mjs wird direkt aus summary.fail === 0 und pageErrors.length === 0 abgeleitet.
6. Exit-Code & Fail-Verhalten
exit 0 → summary.fail === 0 UND pageErrors leer
exit 1 → ≥ 1 Assertion failed ODER ≥ 1 PageError
exit 2 → harter Crash (Datei fehlt, Browser-Start, Timeout, …)
Zusätzlich werden immer audit-result.json und audit-final.png geschrieben (auch bei Fail), damit die Fehler in report.html nachvollziehbar bleiben.
7. Fehlerklassen, die die Audits typischerweise fangen
| Kategorie | Wo sichtbar | Häufige Ursache |
|---|---|---|
| Pfad unvollständig | self.connectivity[n].reachable === false |
Flow-Field rebuild() nach grid-Change vergessen |
| Turm schießt nicht | self.attacks[id].projectiles === 0 (info) |
Cooldown nicht reset / Range zu klein |
| Skill nicht kaufbar | self.skills[id].rank < 1 |
buySkill-Cost > Credits / Skill-ID-Typo |
| Platzierung falsch | self.placement.pathAccepted === true |
placeTower prüft grid nicht |
| Splash trifft nicht | extension.mortar.hurt === false |
Splash-Radius / Ziel-Filter |
| Tesla ohne Effekt | extension.tesla.frozen < 1 |
Freeze-Status nicht gesetzt |
| Doppelte Rewards | extension.reward.storedRank !== rankAfterBuy |
triggerVictory ohne Idempotenz |
| Pfad kaputt nach Blocker | extension.blockerClosed.pathRestored === false |
sellTower ruft Game.flow.rebuild() nicht auf |
| Sniper kein Pierce | redesign.sniper.pierced < 2 |
Pierce-Logik fehlt |
| Quantum trifft Off-Line | redesign.quantum.offLineUntouched === false |
Winkel-Test zu locker |
| Naniten lecken | redesign.nano.cleaned === false |
Cleanup-Flag / NANITE_LIMIT |
8. Bug-Fix-Routine (empfohlen)
1. Replizieren
→ npm run audit
→ audit-result.json öffnen → failing Assertion identifizieren
→ Optional: In-Game-Debug-Console (Taste ` `) → `audit self` / `state`
2. Isolation
→ Spiel-State minimal halten:
Game.towers = [onlyTower]
Game.enemies = [onlyEnemy]
Game.projectiles = []
→ Einzelne Funktion aufrufen: towerFire(t, 1/60)
3. Fixen
→ Aegis-Labyrinth.html editieren
4. Verifizieren
→ npm run audit erneut → grün
→ npm run report → report.html prüfen