set('market.enabled', true); config()->set('market.symbols', [ ['label' => 'DAX', 'symbol' => '^GDAXI', 'precision' => 0, 'suffix' => ''], ['label' => 'EUR/USD', 'symbol' => 'EURUSD=X', 'precision' => 2, 'suffix' => ''], ]); }); function fakeYahooChart(float $price, float $previousClose): array { return [ 'chart' => [ 'result' => [ ['meta' => ['regularMarketPrice' => $price, 'chartPreviousClose' => $previousClose]], ], ], ]; } it('holt Kurse, berechnet Deltas und cacht die Slides', function () { Http::fake([ '*GDAXI*' => Http::response(fakeYahooChart(24150.0, 24000.0)), '*EURUSD*' => Http::response(fakeYahooChart(1.0815, 1.0902)), ]); $slides = app(MarketDataService::class)->refresh(); expect($slides)->toHaveCount(2) ->and($slides[0]['label'])->toBe('DAX') ->and($slides[0]['value'])->toBe('24.150') ->and($slides[0]['delta'])->toBe('+0,6%') ->and($slides[0]['positive'])->toBeTrue() ->and($slides[1]['delta'])->toBe('-0,8%') ->and($slides[1]['positive'])->toBeFalse(); Http::fake(); expect(app(MarketDataService::class)->slides())->toBe($slides); }); it('behält den Cache-Stand, wenn der Abruf komplett fehlschlägt', function () { Cache::put((string) config('market.cache_key'), [ ['label' => 'DAX', 'value' => '24.150', 'delta' => '+0,6%', 'positive' => true], ], 3600); Http::fake(['*' => Http::response(null, 500)]); $slides = app(MarketDataService::class)->refresh(); expect($slides)->toHaveCount(1) ->and($slides[0]['value'])->toBe('24.150'); }); it('liefert keine Slides, wenn Marktdaten deaktiviert sind', function () { config()->set('market.enabled', false); Cache::put((string) config('market.cache_key'), [ ['label' => 'DAX', 'value' => '24.150', 'delta' => '+0,6%', 'positive' => true], ], 3600); expect(app(MarketDataService::class)->slides())->toBe([]); }); it('überspringt Symbole mit unbrauchbarer API-Antwort', function () { Http::fake([ '*GDAXI*' => Http::response(['chart' => ['result' => [['meta' => []]]]]), '*EURUSD*' => Http::response(fakeYahooChart(1.0815, 1.0902)), ]); $slides = app(MarketDataService::class)->refresh(); expect($slides)->toHaveCount(1) ->and($slides[0]['label'])->toBe('EUR/USD'); }); it('aktualisiert Kurse über den Artisan-Command', function () { Http::fake(['*' => Http::response(fakeYahooChart(24150.0, 24000.0))]); $this->artisan('market:refresh') ->expectsOutputToContain('DAX') ->assertSuccessful(); expect(app(MarketDataService::class)->slides())->not->toBeEmpty(); }); it('meldet einen Fehlschlag des Commands ohne Kursdaten', function () { Http::fake(['*' => Http::response(null, 500)]); $this->artisan('market:refresh')->assertFailed(); });