123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606 |
- <?php
- declare(strict_types=1);
- namespace Tests\Unit\Utils;
- use App\Utils\IP;
- use Exception;
- use Illuminate\Http\Client\ConnectionException;
- use Illuminate\Support\Facades\App;
- use Illuminate\Support\Facades\Cache;
- use Illuminate\Support\Facades\Http;
- use ReflectionClass;
- use Tests\TestCase;
- class IPTest extends TestCase
- {
- public static function providerApiCases(): array
- {
- return [
- 'ipApi' => [[
- 'name' => 'ipApi',
- 'endpoint' => '*ip-api.com/json/*',
- 'response' => '{"city":"沈阳市","country":"中国","district":"","isp":"China Unicom CHINA169 Network","lat":41.8357,"lon":123.429,"query":"8.8.8.8","regionName":"辽宁","status":"success"}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁',
- 'city' => '沈阳市',
- 'isp' => 'China Unicom CHINA169 Network',
- 'latitude' => 41.8357,
- 'longitude' => 123.429,
- ],
- ]],
- 'baidu' => [[
- 'name' => 'Baidu',
- 'config' => ['services.ip.baidu_ak' => 'fake_baidu_ak'],
- 'endpoint' => 'https://api.map.baidu.com/location/ip*',
- 'response' => '{"status":0,"address":"CN|辽宁省|沈阳市|None|None|100|65|0","content":{"address":"辽宁省沈阳市","address_detail":{"adcode":"210100","city":"沈阳市","city_code":58,"district":"","province":"辽宁省","street":"","street_number":""},"point":{"x":"123.46466579069178","y":"41.67756788393409"}}}',
- 'expected' => [
- 'country' => 'CN',
- 'region' => '辽宁省',
- 'city' => '沈阳市',
- 'latitude' => '41.67756788393409',
- 'longitude' => '123.46466579069178',
- ],
- ]],
- 'baiduBce' => [[
- 'name' => 'baiduBce',
- 'endpoint' => 'https://qifu-api.baidubce.com/ip/geo/v1/district*',
- 'response' => '{"code":"Success","data":{"continent":"亚洲","country":"中国","zipcode":"110000","owner":"中国联通","isp":"中国联通","adcode":"210100","prov":"辽宁省","city":"沈阳市","district":""},"ip":"8.8.8.8"}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁省',
- 'city' => '沈阳市',
- 'isp' => '中国联通',
- ],
- ]],
- 'ipGeoLocation' => [[
- 'name' => 'ipGeoLocation',
- 'endpoint' => 'https://api.ipgeolocation.io/ipgeo?ip=*',
- 'response' => '{"ip":"8.8.8.8","country_name":"中国","country_name_official":"","state_prov":"辽宁","district":"沈阳","city":"沈阳","isp":"China Unicom CHINA169 Network","latitude":"41.79680","longitude":"123.42910"}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁',
- 'city' => '沈阳',
- 'isp' => 'China Unicom CHINA169 Network',
- 'area' => '沈阳',
- 'latitude' => '41.79680',
- 'longitude' => '123.42910',
- ],
- ]],
- 'taobao' => [[
- 'name' => 'TaoBao',
- 'endpoint' => 'https://ip.taobao.com/outGetIpInfo?ip=*',
- 'response' => '{"data":{"area":"","country":"中国","isp_id":"100026","queryIp":"103.250.104.0","city":"沈阳","ip":"8.8.8.8","isp":"联通","county":"","region_id":"210000","area_id":"","county_id":null,"region":"辽宁","country_id":"CN","city_id":"210100"},"msg":"query success","code":0}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁',
- 'city' => '沈阳',
- 'isp' => '联通',
- ],
- ]],
- 'speedtest' => [[
- 'name' => 'speedtest',
- 'endpoint' => 'https://api-v3.speedtest.cn/ip*',
- 'response' => '{"code":0,"data":"1z41bLnGrmhViAP9vBtxaTvcnepxEF7nyynwu4VDL1s6YCnSK48PoPFgNf6lDQQ3GV9cmRtDJTrLLrU16eItDnmB+8+3stMtsFBhaLaRH1ece5b+D4lR73Cy1FvaxFXmIfGuPxIOjV/g4Mh4F7GuvEy1gm5/tj9gm4egANOyl3vkzMFvp9tB1ET9PUhaP29DTMQOwxCV4CendJn2LdwF6tP6elucLUoy3xweFC4h2w20oha/GcOiQAxKLB+h6aslydvXqDAzvMmeXRV6e0CQ6A==","\'msg\'":"ok"}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁',
- 'city' => '沈阳',
- 'isp' => '中国联通',
- 'area' => '沈河区',
- 'latitude' => '41.796767',
- 'longitude' => '123.429096',
- ],
- ]],
- 'juHe' => [[
- 'name' => 'juHe',
- 'endpoint' => 'https://apis.juhe.cn/ip/Example/query.php*',
- 'response' => '{"resultcode":"200","reason":"success","result":{"Country":"中国","Province":"辽宁","City":"沈阳","District":"","Isp":"联通"},"error_code":0}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁',
- 'city' => '沈阳',
- 'isp' => '联通',
- ],
- ]],
- 'ip2Region' => [[
- 'name' => 'ip2Region',
- 'ip' => '103.250.104.0',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁省',
- 'city' => '沈阳市',
- 'isp' => '联通',
- ],
- ]],
- 'IPDB' => [[
- 'name' => 'IPDB',
- 'ip' => '103.250.104.0',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁',
- 'isp' => '联通',
- ],
- ]],
- 'IPSB' => [[
- 'name' => 'IPSB',
- 'endpoint' => 'https://api.ip.sb/geoip/*',
- 'response' => '{"country":"China","organization":"China Unicom","country_code":"CN","ip":"8.8.8.8","isp":"China Unicom","asn_organization":"CHINA UNICOM China169 Backbone","asn":4837,"offset":28800,"latitude":34.7732,"timezone":"Asia\/Shanghai","continent_code":"AS","longitude":113.722}',
- 'expected' => [
- 'country' => 'China',
- 'isp' => 'China Unicom',
- 'latitude' => 34.7732,
- 'longitude' => 113.722,
- ],
- ]],
- 'ipinfo' => [[
- 'name' => 'ipinfo',
- 'endpoint' => 'https://ipinfo.io*',
- 'response' => '{"input":"103.250.104.0","data":{"ip":"8.8.8.8","city":"Shenyang","region":"Liaoning","country":"CN","loc":"41.7922,123.4328","org":"AS4837 CHINA UNICOM China169 Backbone","postal":"110000","timezone":"Asia/Shanghai"}}',
- 'expected' => [
- 'country' => 'CN',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'AS4837 CHINA UNICOM China169 Backbone',
- 'latitude' => '41.7922',
- 'longitude' => '123.4328',
- ],
- ]],
- 'ip234' => [[
- 'name' => 'ip234',
- 'endpoint' => 'https://ip234.in/search_ip*',
- 'response' => '{"code":0,"data":{"asn":4837,"city":"Shenyang","continent":"Asia","continent_code":"AS","country":"china","country_code":"CN","ip":"8.8.8.8","latitude":41.8357,"longitude":123.429,"metro_code":null,"network":"103.250.104.0/22","organization":"CHINA UNICOM China169 Backbone","postal":"210000","region":"Liaoning","timezone":"Asia/Shanghai"},"msg":""}',
- 'expected' => [
- 'country' => 'china',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'CHINA UNICOM China169 Backbone',
- 'latitude' => '41.8357',
- 'longitude' => '123.429',
- ],
- ]],
- 'dbIP' => [[
- 'name' => 'dbIP',
- 'endpoint' => 'https://api.db-ip.com/v2/free/*',
- 'response' => '{"ipAddress":"8.8.8.8","continentCode":"AS","continentName":"Asia","countryCode":"CN","countryName":"China","stateProv":"Liaoning","city":"Shenyang"}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- ],
- ]],
- 'IP2Online' => [[
- 'name' => 'IP2Online',
- 'config' => ['services.ip.IP2Location_key' => 'fake_ip2location_key'],
- 'endpoint' => 'https://api.ip2location.io/*',
- 'response' => '{"ip":"8.8.8.8","country_code":"CN","country_name":"China","region_name":"Liaoning","city_name":"Shenyang","latitude":41.79222,"longitude":123.43288,"zip_code":"210000","time_zone":"+08:00","asn":"4837","as":"China Unicom China169 Backbone","is_proxy":false}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'China Unicom China169 Backbone',
- 'latitude' => '41.79222',
- 'longitude' => '123.43288',
- ],
- ]],
- 'ipdata' => [[
- 'name' => 'ipdata',
- 'config' => ['services.ip.ipdata_key' => 'fake_ipdata_key'],
- 'endpoint' => 'https://api.ipdata.co/*',
- 'response' => '{"ip":"8.8.8.8","city":null,"region":null,"country_name":"China","latitude":34.77320098876953,"longitude":113.72200012207031,"asn":null}',
- 'expected' => [
- 'country' => 'China',
- 'latitude' => 34.77320098876953,
- 'longitude' => 113.72200012207031,
- ],
- ]],
- 'ipApiCo' => [[
- 'name' => 'ipApiCo',
- 'endpoint' => 'https://ipapi.co/*/json/*',
- 'response' => '{"ip":"8.8.8.8","network":"103.250.104.0/22","version":"IPv4","city":"Shenyang","region":"Liaoning","region_code":"LN","country":"CN","country_name":"China","country_code":"CN","country_code_iso3":"CHN","country_capital":"Beijing","country_tld":".cn","continent_code":"AS","in_eu":false,"postal":null,"latitude":41.79222,"longitude":123.43278,"timezone":"Asia/Shanghai","utc_offset":"+0800","country_calling_code":"+86","currency":"CNY","currency_name":"Yuan Renminbi","languages":"zh-CN,yue,wuu,dta,ug,za","country_area":9596960,"country_population":1411778724,"asn":"AS4837","org":"CHINA UNICOM China169 Backbone"}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'CHINA UNICOM China169 Backbone',
- 'latitude' => 41.79222,
- 'longitude' => 123.43278,
- ],
- ]],
- 'ip2Location' => [[
- 'name' => 'ip2Location',
- 'ip' => '103.250.104.0',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'latitude' => 41.792221,
- 'longitude' => 123.432877,
- ],
- ]],
- 'GeoIP2' => [[
- 'name' => 'GeoIP2',
- 'ip' => '103.250.104.0',
- 'expected' => [
- 'country' => 'China',
- 'latitude' => 34.7732,
- 'longitude' => 113.722,
- ],
- ]],
- 'ipApiCom' => [[
- 'name' => 'ipApiCom',
- 'config' => ['services.ip.ipApiCom_acess_key' => 'fake_acess_key'],
- 'endpoint' => 'https://api.ipapi.com/api/*',
- 'response' => '{"ip": "8.8.8.8", "type": "ipv4", "continent_code": "AS", "continent_name": "Asia", "country_code": "CN", "country_name": "China", "region_code": "LN", "region_name": "Liaoning", "city": "Shenyang", "zip": "110000", "latitude": 41.801021575927734, "longitude": 123.40206909179688, "msa": null, "dma": null, "radius": "0", "ip_routing_type": "fixed", "connection_type": "tx", "location": {"geoname_id": 2034937, "capital": "Beijing", "languages": [{"code": "zh", "name": "Chinese", "native": "\u4e2d\u6587"}], "country_flag": "https://assets.ipstack.com/flags/cn.svg", "country_flag_emoji": "\ud83c\udde8\ud83c\uddf3", "country_flag_emoji_unicode": "U+1F1E8 U+1F1F3", "calling_code": "86", "is_eu": false}}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'latitude' => 41.801021575927734,
- 'longitude' => 123.40206909179688,
- 'address' => 'China Liaoning Shenyang',
- ],
- ]],
- 'vore' => [[
- 'name' => 'vore',
- 'endpoint' => 'https://api.vore.top/api/IPdata*',
- 'response' => '{"code":200,"msg":"SUCCESS","ipinfo":{"type":"ipv4","text":"103.250.104.0","cnip":true},"ipdata":{"info1":"辽宁省","info2":"沈阳市","info3":"","isp":"联通"},"adcode":{"o":"辽宁省沈阳市 - 联通","p":"辽宁","c":"沈阳","n":"辽宁-沈阳","r":"辽宁-沈阳","a":"210100","i":true},"tips":"接口由VORE-API(https://api.vore.top/)免费提供","time":1757149038}',
- 'expected' => [
- 'country' => '辽宁省',
- 'region' => '沈阳市',
- 'isp' => '联通',
- ],
- ]],
- 'ipw_v4' => [[
- 'name' => 'ipw',
- 'endpoint' => 'https://rest.ipw.cn/api/aw/v1/ipv4*',
- 'response' => '{"code":"Success","data":{"continent":"亚洲","country":"中国","zipcode":"110000","timezone":"UTC+8","accuracy":"城市","owner":"中国联通","isp":"中国联通","source":"数据挖掘","areacode":"CN","adcode":"210100","asnumber":"4837","lat":"41.800551","lng":"123.420011","radius":"109.2745","prov":"辽宁省","city":"沈阳市","district":""},"charge":false,"msg":"查询成功","ip":"8.8.8.8","coordsys":"WGS84"}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁省',
- 'city' => '沈阳市',
- 'isp' => '中国联通',
- 'latitude' => '41.800551',
- 'longitude' => '123.420011',
- ],
- ]],
- 'ipw_v6' => [[
- 'name' => 'ipw',
- 'ip' => '2408:8207:1850:2a60::4c8',
- 'endpoint' => 'https://rest.ipw.cn/api/aw/v1/ipv6*',
- 'response' => '{"code":"Success","data":{"continent":"亚洲","country":"日本","zipcode":"167-0033","timezone":"UTC+9","accuracy":"城市","owner":"亚马逊","isp":"亚马逊","source":"数据挖掘","areacode":"JP","adcode":"","asnumber":"16509","lat":"35.713914","lng":"139.616508","radius":"","prov":"东京都","city":"Suginami","district":"","currency_code":"JPY","currency_name":"日元"},"charge":false,"msg":"查询成功","ip":"2408:8207:1850:2a60::4c8","coordsys":"WGS84"}',
- 'expected' => [
- 'country' => '日本',
- 'region' => '东京都',
- 'city' => 'Suginami',
- 'isp' => '亚马逊',
- 'latitude' => '35.713914',
- 'longitude' => '139.616508',
- 'address' => '日本 东京都 Suginami',
- ],
- ]],
- 'bjjii' => [[
- 'name' => 'bjjii',
- 'config' => ['services.ip.bjjii_key' => 'fake_acess_key'],
- 'endpoint' => 'https://api.bjjii.com/api/ip/query*',
- 'response' => '{"code":200,"msg":"请求成功","data":{"ip":"8.8.8.8","info":{"StartIPNum":1744463872,"StartIPText":"103.250.104.0","EndIPNum":1744464895,"EndIPText":"103.250.107.255","Country":"辽宁省沈阳市","Local":"联通","lat":41.835709999999999,"lng":123.42925,"nation":"中国","province":"辽宁省","city":"沈阳市","district":"","adcode":210000,"nation_code":156,"update":"2025-09-06 17:48:39"}},"exec_time":0.023085000000000001,"ip":"117.147.44.132"}',
- 'expected' => [
- 'country' => '中国',
- 'region' => '辽宁省',
- 'city' => '沈阳市',
- 'latitude' => 41.83571,
- 'longitude' => 123.42925,
- ],
- ]],
- 'pconline' => [[
- 'name' => 'pconline',
- 'endpoint' => 'https://whois.pconline.com.cn/*',
- 'response' => '{"ip":"8.8.8.8","pro":"辽宁省","proCode":"210000","city":"沈阳市","cityCode":"210100","region":"","regionCode":"0","addr":"辽宁省沈阳市 联通","regionNames":"","err":""}',
- 'expected' => [
- 'region' => '辽宁省',
- 'city' => '沈阳市',
- ],
- ]],
- 'ipApiIO' => [[
- 'name' => 'ipApiIO',
- 'endpoint' => 'https://ip-api.io/api/v1/ip/*',
- 'response' => '{"ip":"8.8.8.8","suspicious_factors":{"is_proxy":false,"is_tor_node":false,"is_spam":false,"is_crawler":false,"is_datacenter":false,"is_vpn":false,"is_threat":false},"location":{"country":"China","country_code":"CN","city":null,"latitude":34.7732,"longitude":113.722,"zip":null,"timezone":"Asia/Shanghai","local_time":"2025-09-07T14:37:47+08:00","local_time_unix":1757227067,"is_daylight_savings":false}}',
- 'expected' => [
- 'country' => 'China',
- 'latitude' => 34.7732,
- 'longitude' => 113.722,
- ],
- ]],
- 'ipApiIS' => [[
- 'name' => 'ipApiIS',
- 'endpoint' => 'https://api.ipapi.is/*',
- 'response' => '{"ip":"8.8.8.8","asn":{"asn":4837,"abuser_score":"0.001 (Low)","route":"103.250.104.0/22","descr":"CHINA169-BACKBONE CHINA UNICOM China169 Backbone, CN","country":"cn","active":true,"org":"CHINA UNICOM China169 Backbone","domain":"chinaunicom.cn","abuse":"[email protected]","type":"isp","updated":"2024-02-06","rir":"APNIC","whois":"https://api.ipapi.is/?whois=AS4837"},"location":{"is_eu_member":false,"calling_code":"86","currency_code":"CNY","continent":"AS","country":"China","country_code":"CN","state":"Liaoning","city":"Shenyang","latitude":41.79222,"longitude":123.43278,"zip":"110000","timezone":"Asia/Shanghai","local_time":"2025-09-07T14:40:03+08:00","local_time_unix":1757227203,"is_dst":false},"elapsed_ms":1.05}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'CHINA UNICOM China169 Backbone',
- 'latitude' => 41.79222,
- 'longitude' => 123.43278,
- ],
- ]],
- 'freeipapi' => [[
- 'name' => 'freeipapi',
- 'endpoint' => 'https://free.freeipapi.com/*',
- 'response' => '{"ipVersion":4,"ipAddress":"8.8.8.8","latitude":41.8357,"longitude":123.429,"countryName":"China","countryCode":"CN","capital":"Beijing","phoneCodes":[86],"timeZones":["Asia\/Shanghai","Asia\/Urumqi"],"zipCode":"210000","cityName":"Shenyang","regionName":"Liaoning","continent":"Asia","continentCode":"AS","currencies":["CNY"],"languages":["zh"],"asn":"4837","asnOrganization":"CHINA UNICOM China169 Backbone","isProxy":false}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'CHINA UNICOM China169 Backbone',
- 'latitude' => 41.8357,
- 'longitude' => 123.429,
- ],
- ]],
- 'ipwhois' => [[
- 'name' => 'ipwhois',
- 'endpoint' => 'https://ipwhois.app/json/*',
- 'response' => '{"ip":"8.8.8.8","success":true,"type":"IPv4","continent":"Asia","continent_code":"AS","country":"China","country_code":"CN","country_flag":"https://cdn.ipwhois.io/flags/cn.svg","country_capital":"Beijing","country_phone":"+86","country_neighbours":"AF,BT,HK,IN,KG,KP,KZ,LA,MM,MN,MO,NP,PK,RU,TJ,VN","region":"Liaoning","city":"Shenyang","latitude":41.805699,"longitude":123.431472,"asn":"AS4837","org":"China Unicom Liaoning Province Network","isp":"China Unicom China1 Backbone","timezone":"Asia/Shanghai","timezone_name":"CST","timezone_dstOffset":0,"timezone_gmtOffset":28800,"timezone_gmt":"+08:00","currency":"Chinese Yuan","currency_code":"CNY","currency_symbol":"¥","currency_rates":7.133,"currency_plural":"Chinese yuan"}',
- 'expected' => [
- 'country' => 'China',
- 'region' => 'Liaoning',
- 'city' => 'Shenyang',
- 'isp' => 'China Unicom China1 Backbone',
- 'latitude' => 41.805699,
- 'longitude' => 123.431472,
- ],
- ]],
- ];
- }
- public function test_localhost_returns_false_for_loopback_ips(): void
- {
- $this->assertFalse(IP::getIPInfo('127.0.0.1'));
- $this->assertFalse(IP::getIPInfo('::1'));
- }
- public function test_get_client_ip_is_string_or_null(): void
- {
- $ip = IP::getClientIP();
- $this->assertTrue(is_null($ip) || is_string($ip));
- }
- /**
- * @dataProvider providerApiCases
- */
- public function test_get_ip_info_from_each_provider(array $case): void
- {
- App::setLocale($case['locale'] ?? 'zh_CN');
- if (! empty($case['config'])) { // 设置可能存在的假token参数,来激活 API 访问
- foreach ($case['config'] as $k => $v) {
- config([$k => $v]);
- }
- }
- // 模拟HTTP响应
- if (isset($case['response'])) {
- $fakeResponses[$case['endpoint']] = Http::response($case['response']);
- }
- $fakeResponses['*'] = Http::response([], 500);
- Http::fake($fakeResponses);
- $result = IP::getIPInfo($case['ip'] ?? '8.8.8.8', $case['name']);
- $this->assertIsArray($result, "Provider {$case['name']} should return an array");
- foreach ($case['expected'] as $k => $v) {
- $this->assertEquals($v, $result[$k] ?? null, "Provider {$case['name']} field {$k} mismatch");
- }
- }
- public function test_get_ip_info_caches_result_and_prevents_http_calls(): void
- {
- $ip = '9.9.9.9';
- $cached = [
- 'country' => 'CachedLand',
- 'region' => 'CachedRegion',
- 'city' => 'CachedCity',
- 'latitude' => 1.23,
- 'longitude' => 4.56,
- ];
- // 将结果写入缓存
- Cache::tags('IP_INFO')->put($ip, $cached, now()->addMinutes(10));
- // 伪造 HTTP,如果有请求发生将返回 500(测试应从缓存直接返回)
- Http::fake(['*' => Http::response([], 500)]);
- $result = IP::getIPInfo($ip);
- $this->assertIsArray($result);
- $this->assertEquals('CachedLand', $result['country']);
- // 确保没有执行任何外部 HTTP 请求
- Http::assertNothingSent();
- }
- public function test_get_ip_geo_returns_lat_lon_consistent_with_get_ip_info(): void
- {
- $ip = '5.6.7.8';
- Http::fake([
- 'http://ip-api.com/*' => Http::response([
- 'status' => 'success',
- 'country' => 'GeoTest',
- 'lat' => 11.11,
- 'lon' => 22.22,
- ]),
- '*' => Http::response([], 500),
- ]);
- // getIPInfo 会缓存并返回完整数据,getIPGeo 只返回 lat/lon
- $info = IP::getIPInfo($ip);
- $geo = IP::getIPGeo($ip);
- $this->assertIsArray($info);
- $this->assertIsArray($geo);
- $this->assertArrayHasKey('latitude', $geo);
- $this->assertArrayHasKey('longitude', $geo);
- $this->assertEquals($info['latitude'], $geo['latitude']);
- $this->assertEquals($info['longitude'], $geo['longitude']);
- }
- public function test_local_database_providers_are_used_when_http_fails(): void
- {
- // 设为中文以便优先走本地库(例如 IPDB / ip2region / ipip 等)
- App::setLocale('zh_CN');
- $ip = '123.123.123.123';
- // 强制 HTTP 全部失败,确保使用本地数据库驱动(已在测试环境通过 eval 注入本地驱动 mock)
- Http::fake(['*' => Http::response([], 500)]);
- Cache::tags('IP_INFO')->forget($ip);
- $result = IP::getIPInfo($ip);
- $this->assertIsArray($result);
- // 这些值依赖于测试时注入的本地 DB mock(原测试中为 中国 / 北京 / 北京市)
- $this->assertEquals('中国', $result['country'] ?? null);
- $this->assertEquals('北京', $result['region'] ?? null);
- $this->assertEquals('北京市', $result['city'] ?? null);
- }
- public function test_get_ip_info_returns_null_when_http_timeout(): void
- {
- App::setLocale('en_US');
- $ip = '2.2.2.2';
- // 模拟超时情况
- Http::fake([
- '*' => function () {
- // 模拟超时,抛出异常或返回超时错误
- throw new ConnectionException('cURL error 28: Operation timed out');
- },
- ]);
- $result = IP::getIPInfo($ip, 'ipApi');
- $this->assertNull($result);
- }
- public function test_get_ip_info_returns_null_when_invalid_json_response(): void
- {
- App::setLocale('en_US');
- $ip = '3.3.3.3';
- // 模拟返回无效JSON
- Http::fake([
- 'http://ip-api.com/*' => Http::response('Invalid JSON response', 200),
- '*' => Http::response([], 500),
- ]);
- $result = IP::getIPInfo($ip, 'ipApi');
- $this->assertNull($result);
- }
- public function test_get_ip_info_with_specific_checker_returns_null_when_provider_fails(): void
- {
- App::setLocale('en_US');
- $ip = '5.5.5.5';
- // 模拟特定检查器失败,并阻止其他检查器被调用
- Http::fake([
- '*' => Http::response([], 500), // 确保其他所有请求也失败
- ]);
- // 使用指定的checker
- $result = IP::getIPInfo($ip, 'ipApi');
- $this->assertNull($result);
- }
- public function test_real_api_requests(): void
- {
- $testIp = '8.8.8.8'; // 使用一个公共的IP地址进行测试
- $checkers = ['ipApi', 'Baidu', 'baiduBce', 'ipw', 'ipGeoLocation', 'TaoBao', 'speedtest', 'bjjii', 'vore', 'juHe', 'ip2Region', 'IPDB', 'IPSB', 'ipinfo', 'ip234', 'dbIP', 'IP2Online', 'ipdata', 'ipApiCo', 'ip2Location', 'GeoIP2', 'ipApiCom', 'pconline', 'ipApiIO', 'ipApiIS', 'freeipapi', 'ipwhois'];
- $successfulRequests = 0;
- $failedRequests = 0;
- $results = [];
- // 为每个检查器执行实际请求
- foreach ($checkers as $checker) {
- try {
- // 清除之前的缓存
- Cache::tags('IP_INFO')->forget($testIp);
- // 执行实际的API请求
- $result = IP::getIPInfo($testIp, $checker);
- if (is_array($result) && ! empty(array_filter($result))) {
- $successfulRequests++;
- } else {
- $failedRequests++;
- }
- $results[$checker] = $result;
- } catch (Exception $e) {
- $failedRequests++;
- echo "Checker {$checker} failed with exception: ".$e->getMessage()."\n";
- }
- }
- // 输出测试结果摘要
- echo "实际API请求测试结果:\n";
- echo "成功请求: {$successfulRequests}\n";
- echo "失败请求: {$failedRequests}\n";
- echo '总请求数: '.($successfulRequests + $failedRequests)."\n";
- $this->assertGreaterThan(0, $successfulRequests, '至少应有一个API请求成功');
- foreach ($results as $checker => $result) {
- echo "[$checker] - ".json_encode($result, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)."\n";
- }
- }
- /**
- * 测试单个API的实际请求
- *
- * @param string $checker 检查器名称
- * @param string $ip 测试IP地址
- */
- public function test_single_real_api_request(string $checker = 'speedtest', string $ip = '8.8.8.8'): void
- {
- try {
- // 执行实际的API请求
- $result = IP::getIPInfo($ip, $checker);
- // 输出结果
- echo "检查器: {$checker}\n";
- echo "测试IP: {$ip}\n";
- echo '结果: '.json_encode($result, JSON_UNESCAPED_UNICODE)."\n";
- // 验证结果
- if (is_array($result) && ! empty(array_filter($result))) {
- $this->assertIsArray($result);
- echo "测试成功: {$checker} 返回了有效数据\n";
- } else {
- echo "警告: {$checker} 没有返回有效数据\n";
- }
- } catch (Exception $e) {
- echo "检查器 {$checker} 失败,异常信息: ".$e->getMessage()."\n";
- $this->markTestIncomplete("检查器 {$checker} 请求失败: ".$e->getMessage());
- }
- }
- protected function setUp(): void
- {
- parent::setUp();
- // 清理 HTTP 假造与缓存
- Http::fake([]);
- Cache::tags('IP_INFO')->flush();
- // 重置 basicRequest
- $ref = new ReflectionClass(IP::class);
- if ($ref->hasProperty('basicRequest')) {
- $prop = $ref->getProperty('basicRequest');
- $prop->setAccessible(true);
- $prop->setValue(null, null);
- }
- }
- }
|