# 02 – Audits im Detail (self / extension / redesign) > Was die drei eingebauten Audits konkret prüfen, welche **Assertions** `tests/audit.mjs` darauf aufsetzt und welche **Exit-Codes** daraus resultieren. > API-Basis: [`01_Debug_API.md`](./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) ```jsonc { "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 ```jsonc { "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 ```jsonc { "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 || visibleSlow` | | 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) ```jsonc { "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