diff --git a/tests/Feature/AccountControllerTest.php b/tests/Feature/AccountControllerTest.php index 3220e2d1..2076e130 100644 --- a/tests/Feature/AccountControllerTest.php +++ b/tests/Feature/AccountControllerTest.php @@ -14,6 +14,11 @@ use App\Models\User; beforeEach(function () { config(['landing.hide_auth_buttons' => false]); + // The balance-evolution endpoint converts account currency via the external + // currency-rate provider; fake it so these tests stay hermetic under the + // stray-request guard instead of hitting the CDN. + fakeCurrencyApi(); + $this->user = User::factory()->onboarded()->create(); $this->actingAs($this->user); }); diff --git a/tests/Feature/LoanTest.php b/tests/Feature/LoanTest.php index 6b140f8f..9f2f1521 100644 --- a/tests/Feature/LoanTest.php +++ b/tests/Feature/LoanTest.php @@ -15,6 +15,11 @@ use function Pest\Laravel\assertDatabaseHas; use function Pest\Laravel\assertDatabaseMissing; beforeEach(function () { + // The balance-evolution endpoint converts account currency via the external + // currency-rate provider; fake it so these tests stay hermetic under the + // stray-request guard instead of hitting the CDN. + fakeCurrencyApi(); + $this->user = User::factory()->onboarded()->create(); $this->bank = Bank::factory()->create(); $this->service = app(LoanAmortizationService::class); diff --git a/tests/Pest.php b/tests/Pest.php index 69540061..ac647803 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -13,6 +13,7 @@ use App\Services\Banking\BalanceSyncService; use App\Services\Banking\Sync\BankingConnectionSyncerFactory; use App\Services\Banking\TransactionSyncService; use Illuminate\Foundation\Testing\RefreshDatabase; +use Illuminate\Http\Client\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Http; use Stripe\Collection as StripeCollection; @@ -303,3 +304,37 @@ function runSync( $job->handle(app(BankingConnectionSyncerFactory::class)); } + +/** + * Fake the external currency-rate provider (the jsdelivr CDN and its pages.dev + * fallback) that CurrencyConversionService and ExchangeRateService fetch from, + * returning a deterministic 1:1 rate for every currency. Tests that trigger a + * currency conversion (e.g. the balance-evolution endpoint) can call this in + * their setup to stay hermetic under the stray-request guard. + * + * The stub only matches the provider's `/currencies/{code}.min.json` path and + * returns null otherwise, so it never shadows another test's own Http::fake() + * nor the stray-request guard for unrelated hosts. + */ +function fakeCurrencyApi(): void +{ + Http::fake(function (Request $request) { + if (! preg_match('#/currencies/([a-z0-9]+)\.min\.json#i', $request->url(), $matches)) { + return null; + } + + $currency = strtolower($matches[1]); + + return Http::response([ + 'date' => '2024-01-01', + $currency => [ + 'usd' => 1.0, + 'eur' => 1.0, + 'gbp' => 1.0, + 'jpy' => 1.0, + 'btc' => 1.0, + 'eth' => 1.0, + ], + ]); + }); +}