adressen(); $this->meldungen(); $this->yahooAusMarkup(); $this->yahooOhneKuerzel(); } /** * Yahoo liefert die Kursseite in zwei Ausprägungen aus – beide müssen * denselben Kurs ergeben. Das Markup stammt aus den echten Seiten vom * 13.08.2026, gekürzt auf das Wesentliche. */ private function yahooAusMarkup(): void { $streamer = 'Allianz SE (ALV.DE) Aktienkurs – Yahoo Finanzen' . ''; $this->assertEquals(441.7, Kursabruf::yahooKurs($streamer, 'ALV.DE'), 'Kurs aus '); $angezeigt = 'SAP SE (SAP.DE) Aktienkurs – Yahoo Finanzen' . '177,22 ' . '+1,26 '; $this->assertEquals(177.22, Kursabruf::yahooKurs($angezeigt, 'SAP.DE'), 'Kurs aus dem angezeigten Preis'); $tausender = 'Test (XYZ.DE) – Yahoo Finanzen' . '1.234,56 '; $this->assertEquals(1234.56, Kursabruf::yahooKurs($tausender, 'XYZ.DE'), 'Tausenderpunkt und Dezimalkomma'); // Die Indexleiste am Seitenkopf führt fremde Kurse mit – die dürfen // nicht als Kurs des Papiers durchgehen. $fremd = 'Allianz SE (ALV.DE) Aktienkurs – Yahoo Finanzen' . '' . ''; $this->assertEquals(441.7, Kursabruf::yahooKurs($fremd, 'ALV.DE'), 'Der DAX-Stand der Kopfleiste zählt nicht'); $this->assertNull( Kursabruf::yahooKurs('Allianz SE (ALV.DE)

Umbau

', 'ALV.DE'), 'Ohne bekanntes Muster kommt kein Kurs statt eines geratenen' ); // Yahoo zeigt für Papiere ohne Umsatz „--“ an – das ist kein Kurs. $this->assertNull( Kursabruf::yahooKurs( 'Commerce Bancshares Inc (CB5.DE)--', 'CB5.DE' ), 'Ein Strich statt einer Zahl gilt nicht als Kurs' ); // Ein falsches Kürzel darf keinen fremden Kurs in den Bestand tragen. $fehler = ''; try { Kursabruf::yahooKurs('Siemens AG (SIE.DE) Aktienkurs' . '210,00', 'ALV.DE'); } catch (\RuntimeException $e) { $fehler = $e->getMessage(); } $this->assert(str_contains($fehler, 'ALV.DE'), 'Eine fremde Seite wird am Titel erkannt'); } /** Ohne Kürzel kann Yahoo nichts finden – das muss die Meldung sagen. */ private function yahooOhneKuerzel(): void { $kurs = new ReflectionMethod(Kursabruf::class, 'yahoo'); $kurs->setAccessible(true); $fehler = ''; try { $kurs->invoke(null, ['id' => 0, 'symbol' => ' ', 'isin' => 'DE0008404005', 'name' => 'Allianz']); } catch (\RuntimeException $e) { $fehler = $e->getMessage(); } $this->assert(str_contains($fehler, 'Kürzel'), 'Das fehlende Kürzel wird benannt'); $this->assert(str_contains($fehler, 'ALV.DE'), 'Die Meldung nennt ein Beispiel'); } /** Keine abgeschalteten Adressen mehr im Quelltext. */ private function adressen(): void { $quelle = (string)file_get_contents(dirname(__DIR__) . '/src/Services/Kursabruf.php'); $this->assert( !str_contains($quelle, 'financialmodelingprep.com/api/v3'), 'Financial Modeling Prep wird nicht mehr über /api/v3 angesprochen' ); $this->assert( str_contains($quelle, 'financialmodelingprep.com/stable/quote?symbol='), 'Der Kurs kommt über /stable/quote' ); $this->assert( str_contains($quelle, 'financialmodelingprep.com/stable/search-isin?isin='), 'Die ISIN wird über /stable/search-isin aufgelöst' ); } /** * Die Begründung des Dienstes gehört in die Fehlermeldung – sonst sucht man * beim Schlüssel, wenn die Adresse das Problem ist. */ private function meldungen(): void { $meldung = new ReflectionMethod(Kursabruf::class, 'meldung'); $meldung->setAccessible(true); $json = (string)$meldung->invoke(null, '{"Error Message": "Legacy Endpoint : nicht mehr\nunterstützt"}'); $this->assertEquals( ' Legacy Endpoint : nicht mehr unterstützt', $json, 'Aus JSON wird der Klartext einzeilig übernommen' ); $text = (string)$meldung->invoke(null, 'Restricted Endpoint: nicht im Tarif'); $this->assertEquals( ' Restricted Endpoint: nicht im Tarif', $text, 'Antworten ohne JSON werden unverändert übernommen' ); $this->assertEquals('', (string)$meldung->invoke(null, ' '), 'Leere Antworten hängen nichts an'); $this->assertEquals('', (string)$meldung->invoke(null, '[]'), 'Ein leeres Feld hängt nichts an'); $lang = (string)$meldung->invoke(null, str_repeat('x', 500)); // Ein Leerzeichen davor, 200 Zeichen Text, ein Auslassungszeichen dahinter. $this->assertEquals(202, mb_strlen($lang), 'Lange Antworten werden auf 200 Zeichen gekürzt'); $this->assert(str_ends_with($lang, '…'), 'Gekürzte Antworten enden mit Auslassungszeichen'); } }