erkenntDenTestnutzer(); $this->raeumtDenBestandWeg(); $this->kaskadeNimmtDieSplitzeilenMit(); $this->fremderBestandBleibtStehen(); $this->probelaufLoeschtNichts(); $this->grundbestandTraegtDieDarstellungstests(); $this->grundbestandGehtBeimAufraeumenWiederWeg(); } /** Legt einen Nutzer an und macht ihn zum Mandanten. */ private function nutzerAnlegen(string $anzeigename): int { $id = Benutzer::anlegen( 'aufraeum_' . bin2hex(random_bytes(4)), 'testpasswort-' . bin2hex(random_bytes(4)), $anzeigename ); Mandant::setze($id); return $id; } /** Ein Konto mit einer Buchung darauf; liefert die Kennung der Buchung. */ private function bestandAnlegen(string $marke): int { $konto = Konto::anlegen([ 'name' => 'E2E Giro ' . $marke, 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1, ]); return Buchung::anlegen([ 'konto_id' => $konto, 'datum' => '2026-04-01', 'betrag' => -12.30, 'empfaenger' => 'Supermarkt', ]); } private function erkenntDenTestnutzer(): void { $this->assert( E2EAufraeumer::istTestnutzer(['anzeigename' => E2EAufraeumer::ANZEIGENAME]), 'Der Anzeigename des Testnutzers wird erkannt' ); // Die Schranke gegen den Fehlgriff: Wer E2E_USER auf seinen eigenen // Zugang stellt, soll nichts verlieren. $this->assert( !E2EAufraeumer::istTestnutzer(['anzeigename' => 'Felix']), 'Ein gewöhnlicher Nutzer gilt nicht als Testnutzer' ); $this->assert( !E2EAufraeumer::istTestnutzer([]), 'Ohne Anzeigename gilt niemand als Testnutzer' ); } private function raeumtDenBestandWeg(): void { $nutzer = $this->nutzerAnlegen(E2EAufraeumer::ANZEIGENAME); $this->bestandAnlegen('111111'); Kategorie::anlegen(['name' => 'E2E Einkauf 111111', 'art' => 'ausgabe']); Klasse::anlegen('E2E Privat 111111'); $ergebnis = E2EAufraeumer::ausfuehren($nutzer); $this->assertEquals(1, $ergebnis['konten'] ?? 0, 'Das Konto ist mitgezählt'); $this->assertEquals(1, $ergebnis['buchungen'] ?? 0, 'Die Buchung ist mitgezählt'); $this->assertEquals(1, $ergebnis['kategorien'] ?? 0, 'Die Kategorie ist mitgezählt'); $this->assertEquals(1, $ergebnis['klassen'] ?? 0, 'Die Klasse ist mitgezählt'); $this->assertEquals( 0, (int)Database::fetchValue('SELECT COUNT(*) FROM konten WHERE benutzer_id = ?', [$nutzer], 0), 'Nach dem Aufräumen steht kein Konto mehr' ); $this->assertEquals( 0, (int)Database::fetchValue('SELECT COUNT(*) FROM buchungen WHERE benutzer_id = ?', [$nutzer], 0), 'Nach dem Aufräumen steht keine Buchung mehr' ); // Der Nutzer selbst bleibt: Ohne ihn käme der nächste Lauf nicht durch // die Anmeldung. $this->assertNotEmpty(Benutzer::find($nutzer), 'Der Testnutzer selbst bleibt stehen'); } private function kaskadeNimmtDieSplitzeilenMit(): void { $nutzer = $this->nutzerAnlegen(E2EAufraeumer::ANZEIGENAME); $konto = Konto::anlegen([ 'name' => 'E2E Giro 222222', 'kontoart' => 'bank', 'anfangssaldo' => 0, 'aktiv' => 1, ]); $kategorie = Kategorie::anlegen(['name' => 'E2E Einkauf 222222', 'art' => 'ausgabe']); $buchung = Buchung::anlegen( ['konto_id' => $konto, 'datum' => '2026-04-02', 'betrag' => -90.0, 'empfaenger' => 'Aufzuteilen'], [ ['kategorie_id' => $kategorie, 'betrag' => -50.0, 'notiz' => 'Teil eins'], ['kategorie_id' => $kategorie, 'betrag' => -40.0, 'notiz' => 'Teil zwei'], ] ); $this->assertEquals(2, count(Buchung::splits($buchung)), 'Die Splitzeilen stehen zunächst'); E2EAufraeumer::ausfuehren($nutzer); // buchung_splits trägt keine benutzer_id – die Zeilen gehen nur mit, // weil der Fremdschlüssel kaskadiert. $this->assertEquals( 0, (int)Database::fetchValue( 'SELECT COUNT(*) FROM buchung_splits WHERE buchung_id = ?', [$buchung], 0 ), 'Die Splitzeilen gehen über die Kaskade mit' ); } private function fremderBestandBleibtStehen(): void { $fremd = $this->nutzerAnlegen('Felix'); $this->bestandAnlegen('333333'); $fremdeKategorie = Kategorie::anlegen(['name' => 'Lebensmittel', 'art' => 'ausgabe']); $test = $this->nutzerAnlegen(E2EAufraeumer::ANZEIGENAME); $this->bestandAnlegen('444444'); E2EAufraeumer::ausfuehren($test); $this->assertEquals( 1, (int)Database::fetchValue('SELECT COUNT(*) FROM konten WHERE benutzer_id = ?', [$fremd], 0), 'Das Konto des fremden Nutzers steht noch' ); $this->assertEquals( 1, (int)Database::fetchValue('SELECT COUNT(*) FROM buchungen WHERE benutzer_id = ?', [$fremd], 0), 'Die Buchung des fremden Nutzers steht noch' ); $this->assertNotEmpty( Database::fetchOne('SELECT * FROM kategorien WHERE id = ?', [$fremdeKategorie]), 'Die Kategorie des fremden Nutzers steht noch' ); } /** * Der Grundbestand ist kein beliebiger Datenhaufen: Jeder Zuschnitt hängt an * einem Test, der ihn braucht. Bleibt er hinter einer dieser Grenzen zurück, * fällt der zugehörige Browser-Test aus – und zwar mit einer Meldung über * eine fehlende Tabellenspalte, die den Grund nicht verrät. */ private function grundbestandTraegtDieDarstellungstests(): void { $nutzer = $this->nutzerAnlegen(E2EAufraeumer::ANZEIGENAME); $jahr = (int)date('Y'); E2EGrundbestand::anlegen($nutzer); $konten = (int)Database::fetchValue( 'SELECT COUNT(*) FROM konten WHERE benutzer_id = ?', [$nutzer], 0 ); $this->assertGreaterThan( Vermoegensentwicklung::SERIEN, $konten, 'Mehr Konten als Farbtöne – sonst gibt es kein „Übrige Konten“ zum Aufklappen' ); // Zwölf Monatsspalten füllen sich nur, wenn in jedem Monat etwas steht. $monate = (int)Database::fetchValue( 'SELECT COUNT(DISTINCT MONTH(datum)) FROM buchungen WHERE benutzer_id = ? AND YEAR(datum) = ?', [$nutzer, $jahr], 0 ); $this->assertEquals(12, $monate, 'Jeder Monat des laufenden Jahres trägt Buchungen'); $this->assertGreaterThan( 0, (int)Database::fetchValue( 'SELECT COUNT(*) FROM buchungen WHERE benutzer_id = ? AND YEAR(datum) = ?', [$nutzer, $jahr - 1], 0 ), 'Das Vorjahr ist besetzt – der Vergleichsbericht stellt zwei Zeiträume nebeneinander' ); $this->assertGreaterThan( 0, (int)Database::fetchValue( 'SELECT COUNT(*) FROM depot_buchungen WHERE benutzer_id = ?', [$nutzer], 0 ), 'Ein Wertpapiergeschäft steht – sonst sind /wertpapiere/buchungen eine leere Liste' ); // Ohne Kategorie bliebe der Summenbericht ein Platzhalter. $this->assertGreaterThan( 0, (int)Database::fetchValue( 'SELECT COUNT(*) FROM buchungen WHERE benutzer_id = ? AND kategorie_id IS NOT NULL', [$nutzer], 0 ), 'Die Buchungen tragen Kategorien' ); } private function grundbestandGehtBeimAufraeumenWiederWeg(): void { $nutzer = $this->nutzerAnlegen(E2EAufraeumer::ANZEIGENAME); E2EGrundbestand::anlegen($nutzer); E2EAufraeumer::ausfuehren($nutzer); foreach (['konten', 'buchungen', 'kategorien', 'wertpapiere', 'depot_buchungen'] as $tabelle) { $this->assertEquals( 0, (int)Database::fetchValue( 'SELECT COUNT(*) FROM `' . $tabelle . '` WHERE benutzer_id = ?', [$nutzer], 0 ), 'Der Grundbestand ist aus ' . $tabelle . ' wieder verschwunden' ); } } private function probelaufLoeschtNichts(): void { $nutzer = $this->nutzerAnlegen(E2EAufraeumer::ANZEIGENAME); $this->bestandAnlegen('555555'); $probe = E2EAufraeumer::ausfuehren($nutzer, true); $this->assertEquals(1, $probe['konten'] ?? 0, 'Der Probelauf zählt das Konto'); $this->assertEquals( 1, (int)Database::fetchValue('SELECT COUNT(*) FROM konten WHERE benutzer_id = ?', [$nutzer], 0), 'Der Probelauf lässt das Konto stehen' ); } }