[[ 'name' => 'exchangerateApi', 'endpoint' => 'https://open.er-api.com/v6/latest/*', 'response' => '{"result":"success","provider":"https://www.exchangerate-api.com","documentation":"https://www.exchangerate-api.com/docs/free","terms_of_use":"https://www.exchangerate-api.com/terms","time_last_update_unix":1757462551,"time_last_update_utc":"Wed, 10 Sep 2025 00:02:31 +0000","time_next_update_unix":1757550141,"time_next_update_utc":"Thu, 11 Sep 2025 00:22:21 +0000","time_eol_unix":0,"base_code":"USD","rates":{"USD":1,"BYN":3.233971,"BZD":2,"CAD":1.383268,"CDF":2878.431948,"CHF":0.79644,"CLP":971.373897,"CNY":7.124079}}', 'expected' => 7.12, ]], 'exchangerateApi_paid' => [[ 'name' => 'exchangerateApi', 'config' => ['services.currency.exchangerate-api_key' => 'fake_key'], 'endpoint' => 'https://v6.exchangerate-api.com/v6/*', 'response' => '{"result":"success","documentation":"https://www.exchangerate-api.com/docs","terms_of_use":"https://www.exchangerate-api.com/terms","time_last_update_unix":1757462401,"time_last_update_utc":"Wed, 10 Sep 2025 00:00:01 +0000","time_next_update_unix":1757548801,"time_next_update_utc":"Thu, 11 Sep 2025 00:00:01 +0000","base_code":"USD","target_code":"CNY","conversion_rate":7.1241}', 'expected' => 7.12, ]], 'k780' => [[ 'name' => 'k780', 'endpoint' => 'https://sapi.k780.com/?app=finance.rate&scur=USD&tcur=CNY&appkey=10003&sign=b59bc3ef6191eb9f747dd4e83c99f2a4&format=json', 'response' => '{"success":"1","result":{"status":"ALREADY","scur":"USD","tcur":"CNY","ratenm":"美元/人民币","rate":"7.1206","update":"2025-09-10 22:47:01"}}', 'expected' => 7.12, ]], 'it120' => [[ 'name' => 'it120', 'config' => ['services.currency.it120_key' => 'fake_key'], 'endpoint' => 'https://api.it120.cc/*', 'response' => '{"code":0,"data":{"rate":6.5749,"toCode":657.49,"fromCode":100},"msg":"success"}', 'expected' => 6.57, ]], 'fixer' => [[ 'name' => 'fixer', 'config' => ['services.currency.apiLayer_key' => 'fake_key'], 'endpoint' => 'https://api.apilayer.com/fixer/latest*', 'response' => '{"success":true,"timestamp":1757522110,"base":"USD","date":"2025-09-10","rates":{"CNY":7.121499}}', 'expected' => 7.12, ]], 'currencyData' => [[ 'name' => 'currencyData', 'config' => ['services.currency.apiLayer_key' => 'fake_key'], 'endpoint' => 'https://api.apilayer.com/currency_data/live*', 'response' => '{"success":true,"timestamp":1757522348,"source":"USD","quotes":{"USDCNY":7.121498}}', 'expected' => 7.12, ]], 'exchangeRatesData' => [[ 'name' => 'exchangeRatesData', 'config' => ['services.currency.apiLayer_key' => 'fake_key'], 'endpoint' => 'https://api.apilayer.com/exchangerates_data/latest*', 'response' => '{"success":true,"timestamp":1757564885,"base":"USD","date":"2025-09-11","rates":{"CNY":7.12125}}', 'expected' => 7.12, ]], 'jsdelivrFile' => [[ 'name' => 'jsdelivrFile', 'endpoint' => 'https://cdn.jsdelivr.net/npm/@fawazahmed0/currency-api@latest/v1/currencies/*', 'response' => '{"date":"2025-09-10","usd":{"cfx":5.80551999,"chf":0.79810103,"chz":23.57267514,"clp":967.40244215,"cnh":7.1267651,"cny":7.12926824,"comp":0.022938216,"cop":3922.09820874,"crc":505.17600184,"cro":3.94111354,"crv":1.28387416,"cspr":103.9529098,"cuc":1,"cup":24.06001705,"cve":94.26691294,"cvx":0.28371305,"cyp":0.5003353,"czk":20.81016181,"dai":0.9997095,"dash":0.040430194}}', 'expected' => 7.12, ]], 'duckduckgo' => [[ 'name' => 'duckduckgo', 'endpoint' => 'https://duckduckgo.com/js/spice/currency_convert/1/*', 'response' => '{"terms":"https://www.xe.com/legal/","privacy":"http://www.xe.com/privacy.php","from":"USD","amount":1.0,"timestamp":"2025-09-11T04:44:00Z","to":[{"quotecurrency":"CNY","mid":7.1209786645}]}', 'expected' => 7.12, ]], 'wise' => [[ 'name' => 'wise', 'endpoint' => 'https://api.wise.com/v1/rates*', 'response' => '[{"rate":7.12105,"source":"USD","target":"CNY","time":"2025-09-11T04:47:28+0000"}]', 'expected' => 7.12, ]], 'xRates' => [[ 'name' => 'xRates', 'endpoint' => 'https://www.x-rates.com/calculator/*', 'response' => '
1.00 USD =7.121208 CNY
Sep 11, 2025 05:21 UTC', 'expected' => 7.12, ]], 'valutafx' => [[ 'name' => 'valutafx', 'endpoint' => 'https://www.valutafx.com/api/v2/rates/lookup*', 'response' => '{"Amount":1,"Rate":7.1218,"UpdatedDateTimeUTC":"2025-09-11T06:15:00","FormattedResult":"= 7.1218 CNY","FormattedRates":"1 USD = 7.1218 CNY","FormattedIndirectRates":"1 CNY = 0.14041 USD","FormattedDateTime":"Last update 2025-09-11 6:15 AM UTC","ErrorMessage":null}', 'expected' => 7.12, ]], 'unionpay' => [[ 'name' => 'unionpay', 'endpoint' => 'https://www.unionpayintl.com/upload/jfimg/*', 'response' => '{"exchangeRateJson":[{"transCur":"AED","baseCur":"AUD","rateData":0.41397204},{"transCur":"TJS","baseCur":"CNY","rateData":0.75934168},{"transCur":"TMT","baseCur":"CNY","rateData":2.0464021},{"transCur":"TND","baseCur":"CNY","rateData":2.45719918},{"transCur":"TOP","baseCur":"CNY","rateData":2.99489085},{"transCur":"UGX","baseCur":"CNY","rateData":0.00204028},{"transCur":"USD","baseCur":"CNY","rateData":7.1399},{"transCur":"UYU","baseCur":"CNY","rateData":0.17892278},{"transCur":"UZS","baseCur":"CNY","rateData":0.00058941}],"curDate":"2025-09-11"}', 'expected' => 7.13, ]], 'baidu' => [[ 'name' => 'baidu', 'endpoint' => 'https://finance.pae.baidu.com/vapi/async/v1*', 'response' => '{"DispExt":null,"QueryDispInfo":null,"ResultCode":0,"ResultNum":2,"QueryID":"162386106721317024","Result":[{"ClickNeed":"1","Degree":"0","DisplayData":{"StdCls":"2","StdStg":"5293","StdStl":"2","resultData":{"extData":{"OriginQuery":"","resourceid":"5293","tplt":"exrate"},"tplData":{"StdCls":"2","StdStg":"5293","StdStl":"2","card_order":"1","content1":"1美元=7.12270000人民币","content2":"1人民币=0.14039700美元","money1":"美元","money1_num":"1","money1_rev":"1","money2":"人民币","money2_num":"7.12270000","money2_rev":"0.140397","pk":[],"sigma_use":"1","strong_use":"1","templateName":"exrate","template_type":"1","text":"更新时间:2025-09-11 15:23 数据仅供参考"}},"strategy":{"ctplOrPhp":"1","hilightWord":"","precharge":"0","tempName":"unitstatic"}},"RecoverCacheTime":"0","Sort":"1","SrcID":"5293","SubResNum":"0","SubResult":[],"SuppInfo":"汇率换算","Title":"汇率换算","Weight":"3"}]}', 'expected' => 7.13, ]], ]; } /** * @dataProvider providerExchangeServices */ public function test_currency_exchange_services(array $case): void { // 设置配置 if (isset($case['config'])) { foreach ($case['config'] as $key => $value) { config([$key => $value]); } } // 模拟HTTP响应 if (isset($case['response'])) { $fakeResponses[$case['endpoint']] = Http::response($case['response']); } $fakeResponses['*'] = Http::response([], 500); Http::fake($fakeResponses); $result = CurrencyExchange::getCurrencyRate($case['target'] ?? 'CNY', $case['base'] ?? 'USD', $case['name']); $this->assertEqualsWithDelta( $case['expected'], $result, 0.01, "Currency exchange service {$case['name']} failed" ); } public function test_currency_exchange_with_cache(): void { Cache::put('Currency_USD_CNY_ExRate', 6.5, 3600); $result = CurrencyExchange::getCurrencyRate('CNY', 'USD'); $this->assertEquals(6.5, $result); // 验证没有进行HTTP请求 Http::assertNothingSent(); } public function test_currency_exchange_fallback(): void { // 模拟所有API都失败 Http::fake([ '*' => Http::response('Not Found', 404), ]); $result = CurrencyExchange::getCurrencyRate('CNY', 'USD'); $this->assertNull($result); } public function test_convert_method(): void { Cache::put('Currency_USD_CNY_ExRate', 6.5, 3600); $result = CurrencyExchange::convert('CNY', 100, 'USD'); $this->assertEquals(650.0, $result); } public function test_real_api_requests(): void { $target = 'CNY'; $base = 'USD'; $services = ['exchangerateApi', 'k780', 'it120', 'fixer', 'currencyData', 'exchangeRatesData', 'jsdelivrFile', 'duckduckgo', 'wise', 'xRates', 'valutafx', 'unionpay', 'baidu']; $successfulRequests = 0; $failedRequests = 0; $results = []; foreach ($services as $service) { try { $result = CurrencyExchange::getCurrencyRate($target, $base, $service); if (is_numeric($result)) { $successfulRequests++; } else { $failedRequests++; } $results[$service] = $result; } catch (Exception $e) { $failedRequests++; echo "Service {$service} failed with exception: ".$e->getMessage()."\n"; } } // 输出测试结果摘要 echo "实际汇率API请求测试结果:\n"; echo "成功请求: {$successfulRequests}\n"; echo "失败请求: {$failedRequests}\n"; echo '总请求数: '.($successfulRequests + $failedRequests)."\n\n"; // 输出详细结果 foreach ($results as $service => $result) { echo "[{$service}] - ".json_encode($result, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)."\n"; } $this->assertGreaterThan(0, $successfulRequests, '至少应有一个汇率API请求成功'); } protected function setUp(): void { parent::setUp(); // 清理 HTTP 假造与缓存 Http::fake([]); Cache::flush(); // 重置 basicRequest $ref = new ReflectionClass(CurrencyExchange::class); if ($ref->hasProperty('basicRequest')) { $prop = $ref->getProperty('basicRequest'); $prop->setAccessible(true); $prop->setValue(null, null); } } }