Steuerkram/tests/92_BankinstituteTest.php

107 lines
5.6 KiB
PHP

<?php
declare(strict_types=1);
/**
* Test 92: Bankleitzahlen-Verzeichnis (Fix #187).
*
* Deckt FintsInstituteImport (ISO-8859-1, mehrzeiliger Institutsname in
* Anführungszeichen, doppelte Bankleitzahlen, überzählige Leerzeilen am
* Ende, Text statt Adresse in der PIN/TAN-Spalte, fehlende Pflicht-Header,
* Stand-Erkennung) sowie Models\Bankinstitut (replaceAll, search,
* findAccessPointByBlz, importMeta) ab.
*/
class BankinstituteTest extends TestCase
{
protected string $testName = 'Test 92: Bankleitzahlen-Verzeichnis';
private function makeIso88591Csv(string $utf8Content): string
{
$path = $this->tempPath('.csv');
file_put_contents($path, mb_convert_encoding($utf8Content, 'ISO-8859-1', 'UTF-8'));
return $path;
}
public function run(): void
{
$import = new \Steuerkram\Services\FintsInstituteImport();
// ── Vollständige, realistische Datei ───────────────────────────
$csv = "Kreditinstitute mit PIN/TAN-Zugang Stand: 20.05.2026\n"
. "BLZ;Institut;Ort;BIC;PIN/TAN-Zugang URL;DDV;RDH-1\n"
. "10030000;Sparkasse Köln Bonn;Köln;COLSDE33XXX;https://banking.spk.de/fints;1;1\n"
. "10030000;Sparkasse Köln Bonn Filiale;Bonn;COLSDE33XXX;https://banking.spk.de/fints;1;1\n"
. "12030000;\"Oldenburgische\nLandesbank\";Oldenburg;OLBODEH2XXX;https://fints.oldenburgischelandesbank.de;0;1\n"
. "99999999;Kleine Bank ohne Zugang;Nirgendwo;;kein PIN/TAN-Zugang;0;0\n"
. "88888888;;;;;;\n"
. ";;;;;;\n"
. "88888889;;;;;;\n";
$tmp = $this->makeIso88591Csv($csv);
$result = $import->analyze($tmp, 'blz-aktuell-csv-data.csv');
$this->assertEquals(4, $result['zeilen'], 'Vier verwertbare Zeilen (Leerzeilen am Ende ausgeschlossen)');
$this->assertEquals(3, $result['blz_anzahl'], 'Drei verschiedene Bankleitzahlen (10030000 doppelt)');
$this->assertEquals(3, $result['mit_zugangspunkt'], 'Drei Zeilen mit echtem PIN/TAN-Zugangspunkt (die vierte hat nur Text statt Adresse)');
$this->assertEquals('20.05.2026', $result['stand'], 'Stand aus der Titelzeile erkannt');
$names = array_column($result['rows'], 'name');
$this->assert(
(bool)preg_grep('/Oldenburgische.*Landesbank/s', $names),
'Mehrzeiliger, in Anführungszeichen stehender Institutsname wird als eine Zeile gelesen'
);
$koeln = $result['rows'][0];
$this->assertEquals('Köln', mb_substr($koeln['ort'], 0, 4), 'ISO-8859-1 → UTF-8: Umlaut in "Köln" korrekt dekodiert');
$ohneZugang = array_values(array_filter($result['rows'], fn($r) => $r['blz'] === '99999999'))[0];
$this->assert($ohneZugang['pin_tan_url'] === null, 'Text statt Adresse in der PIN/TAN-Spalte wird nicht als URL übernommen');
$this->assert(
!in_array('88888888', array_column($result['rows'], 'blz'), true)
&& !in_array('88888889', array_column($result['rows'], 'blz'), true),
'Zeilen ohne Institutsnamen (trailing Leerzeilen) werden übersprungen'
);
// ── Fehlender Pflicht-Header bricht den Import ab ──────────────
$csvOhneHeader = "Nur irgendein Text;ohne;Header\n1;2;3\n";
$tmpBad = $this->makeIso88591Csv($csvOhneHeader);
$exceptionThrown = false;
try {
$import->analyze($tmpBad, 'kaputt.csv');
} catch (\RuntimeException $e) {
$exceptionThrown = true;
}
$this->assert($exceptionThrown, 'Fehlen BLZ/Institut-Header, wird der Import mit RuntimeException abgebrochen');
// ── Models\Bankinstitut: replaceAll / search / findAccessPointByBlz ──
\Steuerkram\Models\Bankinstitut::replaceAll($result['rows'], [
'filename' => 'blz-aktuell-csv-data.csv',
'stand' => $result['stand'],
'zeilen' => $result['zeilen'],
'blz_anzahl' => $result['blz_anzahl'],
'mit_zugangspunkt' => $result['mit_zugangspunkt'],
]);
$this->assertEquals(4, \Steuerkram\Models\Bankinstitut::count(), 'replaceAll() hat vier Zeilen übernommen');
$treffer = \Steuerkram\Models\Bankinstitut::findAccessPointByBlz('10030000');
$this->assert($treffer !== null && $treffer['pin_tan_url'] === 'https://banking.spk.de/fints', 'findAccessPointByBlz() liefert Zugangspunkt trotz doppelter BLZ');
$suchtreffer = \Steuerkram\Models\Bankinstitut::search('1822', 10);
$this->assertEquals(0, count($suchtreffer), 'Suche nach "1822" findet keinen Treffer, wenn weder BLZ noch Name/Ort/BIC passen');
$suchtreffer2 = \Steuerkram\Models\Bankinstitut::search('Köln', 10);
$this->assertGreaterThan(0, count($suchtreffer2), 'Suche über den Namen findet Treffer');
$meta = \Steuerkram\Models\Bankinstitut::importMeta();
$this->assertEquals('20.05.2026', $meta['stand'] ?? null, 'importMeta() liefert den zuletzt importierten Stand');
// ── Ein zweiter Import ersetzt den Bestand vollständig ─────────
\Steuerkram\Models\Bankinstitut::replaceAll(
[['blz' => '11111111', 'name' => 'Nur noch dieses Institut', 'ort' => 'X', 'bic' => '', 'pin_tan_url' => null]],
['filename' => 'neu.csv', 'stand' => null, 'zeilen' => 1, 'blz_anzahl' => 1, 'mit_zugangspunkt' => 0]
);
$this->assertEquals(1, \Steuerkram\Models\Bankinstitut::count(), 'Ein erneuter Import ersetzt den gesamten Bestand statt ihn zu ergänzen');
}
}