$code, 'name' => $code, 'symbol' => $code, 'rate_to_rial' => $code === 'IRR' ? 1 : 0, // غیر IRR صفر → اولین import باید بنویسد 'is_active' => true, ]); } } // ═══════════════════ موفقیت ‌ها ═══════════════════ #[Test] public function it_fetches_usd_and_aed_from_exir_with_auto_approve() { $this->seedCurrencies(); Http::fake([ 'https://api.exir.io/*' => Http::response([ 'usdt-rls' => ['price' => 60000.0], // 1 دلار = ۶۰,۰۰۰ تومان 'usdt-aed' => ['price' => 0.27], // 1 درهم = 0.27 تتر → 0.27 × 60000 = 16200 ریال ], 200), ]); $this->artisan('ifnex:import-fx-rates', ['--source' => 'exir', '--auto-approve' => true]) ->assertSuccessful(); $this->assertDatabaseHas('currencies', ['code' => 'USD', 'rate_to_rial' => 60000.000]); $this->assertDatabaseHas('currencies', ['code' => 'AED', 'rate_to_rial' => 16200.000]); // باید رکورد approved در تاریخچه ساخته شود $this->assertDatabaseHas('exchange_rate_history', [ 'currency_from' => 'USD', 'status' => 'approved', 'api_source' => 'exir.io', ]); } #[Test] public function it_creates_pending_when_change_exceeds_auto_approve_threshold() { $this->seedCurrencies(); // USD قبلاً ۶۰,۰۰۰ بوده؛ حالا ۸۰,۰۰۰ → تغییر ۳۳٪ > threshold 5٪ Currency::where('code', 'USD')->update(['rate_to_rial' => 60000]); Currency::where('code', 'AED')->update(['rate_to_rial' => 16200]); Http::fake([ 'https://api.exir.io/*' => Http::response([ 'usdt-rls' => ['price' => 80000.0], // ۳۳٪ تغییر 'usdt-aed' => ['price' => 0.30], ], 200), ]); $this->artisan('ifnex:import-fx-rates', [ '--source' => 'exir', '--auto-approve' => true, '--threshold' => 5, ])->assertSuccessful(); // باید pending بسازد چون تغییر > threshold $this->assertDatabaseHas('exchange_rate_history', [ 'currency_from' => 'USD', 'status' => 'pending', ]); // نرخ currencies نباید تغییر کرده باشد (چون pending است) $this->assertSame('60000.0000', Currency::where('code', 'USD')->value('rate_to_rial')); } #[Test] public function it_creates_pending_by_default_without_auto_approve() { $this->seedCurrencies(); Currency::where('code', 'USD')->update(['rate_to_rial' => 60000]); Http::fake([ 'https://api.exir.io/*' => Http::response([ 'usdt-rls' => ['price' => 90000.0], // ۵۰٪ تغییر ], 200), ]); $this->artisan('ifnex:import-fx-rates', ['--source' => 'exir']) ->assertSuccessful(); // بدون --auto-approve همیشه pending می‌سازد $this->assertDatabaseHas('exchange_rate_history', [ 'currency_from' => 'USD', 'status' => 'pending', ]); // نرخ currencies نباید تغییر کند $this->assertSame('60000.0000', Currency::where('code', 'USD')->value('rate_to_rial')); } #[Test] public function it_skips_when_change_is_below_threshold() { $this->seedCurrencies(); Currency::where('code', 'USD')->update(['rate_to_rial' => 60000]); // تغییر فقط ۱٪ < 0.5٪ threshold؟ نه — threshold پیش‌فرض 0.5٪، پس ۱٪ باید رکورد بسازد // برای تست skip، تغییر بسیار کوچک می‌زنیم Http::fake([ 'https://api.exir.io/*' => Http::response([ 'usdt-rls' => ['price' => 60100.0], // ۰.۱۷٪ تغییر < 0.5٪ ], 200), ]); $this->artisan('ifnex:import-fx-rates', ['--source' => 'exir']) ->assertSuccessful(); // نباید هیچ رکوردی در تاریخچه بسازد $this->assertDatabaseCount('exchange_rate_history', 0); // نرخ currencies هم نباید تغییر کند $this->assertSame('60000.0000', Currency::where('code', 'USD')->value('rate_to_rial')); } #[Test] public function it_handles_exir_http_failure_gracefully() { $this->seedCurrencies(); Http::fake([ 'https://api.exir.io/*' => Http::response([], 500), ]); $result = $this->artisan('ifnex:import-fx-rates', ['--source' => 'exir']); // نباید کد موفق بدهد چون هیچ نرخی دریافت نشد $result->assertFailed(); // هیچ رکوردی نباید ساخته شود $this->assertDatabaseCount('exchange_rate_history', 0); } #[Test] public function it_skips_freecurrencyapi_when_api_key_is_missing() { $this->seedCurrencies(); config([ 'ifnex.currency.api_key' => '', // بدون کلید ]); Http::fake([ 'https://api.freecurrencyapi.com/*' => Http::response([ 'data' => ['IRR' => 60000, 'EUR' => 0.95, 'CNY' => 7.2], ], 200), ]); $this->artisan('ifnex:import-fx-rates', ['--source' => 'freecurrencyapi']) ->assertFailed(); // چون هیچ نرخی دریافت نشد // نباید رکوردی ساخته شود $this->assertDatabaseCount('exchange_rate_history', 0); } #[Test] public function it_fetches_eur_and_cny_from_freecurrencyapi() { $this->seedCurrencies(); config([ 'ifnex.currency.api_key' => 'test-key-123', ]); Http::fake([ 'https://api.freecurrencyapi.com/*' => Http::response([ 'data' => [ 'IRR' => 60000.0, // 1 USD = 60,000 IRR 'EUR' => 0.95, // 1 USD = 0.95 EUR → 1 EUR = 0.95 × 60000 = 57000 IRR 'CNY' => 7.2, // 1 USD = 7.2 CNY → 1 CNY = 7.2 × 60000 = 432000 IRR ], ], 200), ]); $this->artisan('ifnex:import-fx-rates', [ '--source' => 'freecurrencyapi', '--auto-approve' => true, ])->assertSuccessful(); // باید در currencies نوشته شود $this->assertDatabaseHas('currencies', ['code' => 'EUR']); $this->assertDatabaseHas('currencies', ['code' => 'CNY']); $this->assertDatabaseHas('exchange_rate_history', [ 'currency_from' => 'EUR', 'status' => 'approved', 'api_source' => 'freecurrencyapi.com', ]); } #[Test] public function it_warns_when_currencies_table_is_empty() { // جدول خالی است — نباید crash کند، باید هشدار دهد Http::fake([ 'https://api.exir.io/*' => Http::response([ 'usdt-rls' => ['price' => 60000.0], ], 200), ]); $this->artisan('ifnex:import-fx-rates', ['--source' => 'exir']) ->assertSuccessful(); // چون ارز USD وجود ندارد، نرخ نوشته نمی‌شود $this->assertDatabaseCount('exchange_rate_history', 0); $this->assertDatabaseCount('currencies', 0); } #[Test] public function it_rejects_invalid_source_option() { $this->artisan('ifnex:import-fx-rates', ['--source' => 'invalid']) ->assertFailed(); } }