NetworkDetectionTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. <?php
  2. namespace Tests\Unit\Utils;
  3. use App\Utils\NetworkDetection;
  4. use Exception;
  5. use Illuminate\Support\Facades\Http;
  6. use ReflectionClass;
  7. use Tests\TestCase;
  8. class NetworkDetectionTest extends TestCase
  9. {
  10. public static function providerDetectionServices(): array
  11. {
  12. return [
  13. 'toolsdaquan' => [[
  14. 'name' => 'toolsdaquan',
  15. 'responses' => [
  16. 'https://www.toolsdaquan.com/toolapi/public/ipchecking*' => '{"success":1,"msg":"检查成功","data":{"tcp":"success","icmp":"success","outside_tcp":"success","outside_icmp":"success"}}',
  17. ],
  18. 'expected' => [
  19. 'icmp' => 1,
  20. 'tcp' => 1,
  21. ],
  22. ]],
  23. 'vps234' => [[
  24. 'name' => 'vps234',
  25. 'responses' => [
  26. 'https://www.vps234.com/ipcheck/getdata/*' => '{"error":false,"data":{"success":true,"msg":"请求成功","data":{"innerICMP":true,"innerTCP":true,"outICMP":true,"outTCP":true}}}',
  27. ],
  28. 'expected' => [
  29. 'icmp' => 1,
  30. 'tcp' => 1,
  31. ],
  32. ]],
  33. 'idcoffer' => [[
  34. 'name' => 'idcoffer',
  35. 'responses' => [
  36. 'https://api.24kplus.com/ipcheck*' => '{"code":1,"message":"\u68C0\u67E5\u6210\u529F\uFF01","data":{"ping":true,"tcp":true,"ip":"220.181.7.203","countryClode":"CN"}}',
  37. 'https://api.idcoffer.com/ipcheck*' => '{"code":1,"message":"\u68C0\u67E5\u6210\u529F\uFF01","data":{"ping":true,"tcp":true,"ip":"220.181.7.203","countryClode":"HK"}}',
  38. ],
  39. 'expected' => [
  40. 'icmp' => 1,
  41. 'tcp' => 1,
  42. ],
  43. ]],
  44. 'ip112' => [[
  45. 'name' => 'ip112',
  46. 'responses' => [
  47. 'https://api.ycwxgzs.com/ipcheck/index.php' => '{"ip":"220.181.7.203","port":"443","tcp":"<span class=\"mdui-text-color-green\">\u7aef\u53e3\u53ef\u7528<\/span>","icmp":"<span class=\"mdui-text-color-green\">IP\u53ef\u7528<\/span>"}',
  48. 'https://api.52bwg.com/ipcheck/ipcheck.php' => '{"ip":"220.181.7.203","port":"443","tcp":"<span class=\"mdui-text-color-green\">\u7aef\u53e3\u53ef\u7528<\/span>","icmp":"<span class=\"mdui-text-color-green\">IP\u53ef\u7528<\/span>"}',
  49. ],
  50. 'expected' => [
  51. 'icmp' => 1,
  52. 'tcp' => 1,
  53. ],
  54. ]],
  55. 'upx8' => [[
  56. 'name' => 'upx8',
  57. 'responses' => [
  58. 'https://api.sm171.com/check-cn.php' => '{"ip":"220.181.7.203","port":"443","tcp":"\u6b63\u5e38","icmp":"\u6b63\u5e38"}',
  59. 'https://ip.upx8.com/api/check-us.php' => '{"ip":"220.181.7.203","port":"443","tcp":"\u6b63\u5e38","icmp":"\u6b63\u5e38"}',
  60. ],
  61. 'expected' => [
  62. 'icmp' => 1,
  63. 'tcp' => 1,
  64. ],
  65. ]],
  66. 'rss' => [[
  67. 'name' => 'rss',
  68. 'responses' => [
  69. 'https://ip.rss.ink/api/scan*' => '{"code":200,"data":"","msg":"Ok"}',
  70. 'https://tcp.mk/api/scan*' => '{"code":200,"data":"","msg":"Ok"}',
  71. ],
  72. 'expected' => [
  73. 'icmp' => 1,
  74. 'tcp' => 1,
  75. ],
  76. ]],
  77. 'vps1352' => [[
  78. 'name' => 'vps1352',
  79. 'responses' => [
  80. 'https://www.vps1352.com/check.php' => '{"ip":"220.181.7.203","port":"443","tcp":"\u5f00\u653e","icmp":"\u5f00\u653e"}',
  81. ],
  82. 'expected' => [
  83. 'icmp' => 1,
  84. 'tcp' => 1,
  85. ],
  86. ]],
  87. 'selfHost' => [[
  88. 'name' => 'selfHost',
  89. 'config' => ['services.probe.domestic' => 'test_domestic.com|test_token', 'services.probe.foreign' => 'test_foreign.com:8080'],
  90. 'responses' => [
  91. 'test_domestic.com*' => '[{"ip":"220.181.7.203","icmp":29.627562,"tcp":29.17411}]',
  92. 'test_foreign.com*' => '[{"ip":"220.181.7.203","icmp":29.627562,"tcp":29.17411}]',
  93. ],
  94. 'expected' => [
  95. 'icmp' => 1,
  96. 'tcp' => 1,
  97. ],
  98. ]],
  99. ];
  100. }
  101. /**
  102. * 测试所有检测服务
  103. *
  104. * @dataProvider providerDetectionServices
  105. */
  106. public function test_network_detection_services(array $case): void
  107. {
  108. // 准备响应
  109. if (! empty($case['config'])) { // 设置可能存在的假token参数,来激活 API 访问
  110. foreach ($case['config'] as $k => $v) {
  111. config([$k => $v]);
  112. }
  113. }
  114. $responses = array_map(static function ($response) {
  115. return Http::response($response);
  116. }, $case['responses']);
  117. // 添加通配符响应以防止意外请求
  118. $responses['*'] = Http::response(['error' => 'Not found'], 404);
  119. Http::fake($responses);
  120. $result = NetworkDetection::networkStatus($case['ip'] ?? '8.8.8.8', $case['port'] ?? 443, $case['name']);
  121. $this->assertIsArray($result, "Service {$case['name']} should return an array");
  122. foreach ($case['expected'] as $protocol => $status) {
  123. $this->assertEquals($status, $result[$protocol], "Service {$case['name']} protocol {$protocol} mismatch");
  124. }
  125. }
  126. /**
  127. * 测试被墙的情况.
  128. */
  129. public function test_network_status_detects_blocked_ips()
  130. {
  131. Http::fake([
  132. 'https://www.vps234.com/ipcheck/getdata/*' => Http::response([
  133. 'error' => false,
  134. 'data' => [
  135. 'success' => true,
  136. 'msg' => '请求成功',
  137. 'data' => [
  138. 'innerICMP' => false,
  139. 'innerTCP' => false,
  140. 'outICMP' => true,
  141. 'outTCP' => true,
  142. ],
  143. ],
  144. ]),
  145. '*' => Http::response(['error' => 'Not found'], 404),
  146. ]);
  147. $result = NetworkDetection::networkStatus('8.8.8.8', 443, 'vps234');
  148. $this->assertIsArray($result);
  149. $this->assertEquals(3, $result['icmp']); // 被墙
  150. $this->assertEquals(3, $result['tcp']); // 被墙
  151. }
  152. /**
  153. * 测试国外访问异常的情况.
  154. */
  155. public function test_network_status_detects_foreign_access_issues()
  156. {
  157. Http::fake([
  158. 'https://www.vps234.com/ipcheck/getdata/*' => Http::response([
  159. 'error' => false,
  160. 'data' => [
  161. 'success' => true,
  162. 'msg' => '请求成功',
  163. 'data' => [
  164. 'innerICMP' => true,
  165. 'innerTCP' => true,
  166. 'outICMP' => true,
  167. 'outTCP' => false,
  168. ],
  169. ],
  170. ]),
  171. '*' => Http::response(['error' => 'Not found'], 404),
  172. ]);
  173. $result = NetworkDetection::networkStatus('8.8.8.8', 443, 'vps234');
  174. $this->assertIsArray($result);
  175. $this->assertEquals(1, $result['icmp']); // 正常
  176. $this->assertEquals(2, $result['tcp']); // 国外访问异常
  177. }
  178. /**
  179. * 测试服务器宕机的情况.
  180. */
  181. public function test_network_status_detects_server_down()
  182. {
  183. Http::fake([
  184. 'https://www.vps234.com/ipcheck/getdata/*' => Http::response([
  185. 'error' => false,
  186. 'data' => [
  187. 'success' => true,
  188. 'data' => [
  189. 'innerICMP' => false,
  190. 'innerTCP' => false,
  191. 'outICMP' => false,
  192. 'outTCP' => false,
  193. ],
  194. ],
  195. ]),
  196. '*' => Http::response(['error' => 'Not found'], 404),
  197. ]);
  198. $result = NetworkDetection::networkStatus('8.8.8.8', 443, 'vps234');
  199. $this->assertIsArray($result);
  200. $this->assertEquals(4, $result['icmp']); // 服务器宕机
  201. $this->assertEquals(4, $result['tcp']); // 服务器宕机
  202. }
  203. /**
  204. * 测试当所有检测服务都失败时返回 null.
  205. */
  206. public function test_network_status_returns_null_when_all_services_fail()
  207. {
  208. Http::fake([
  209. '*' => Http::response(['error' => 'Service unavailable'], 500),
  210. ]);
  211. $result = NetworkDetection::networkStatus('8.8.8.8', 443);
  212. $this->assertNull($result);
  213. }
  214. /**
  215. * 测试真实可用的 IP.
  216. */
  217. public function test_real_ip_connectivity()
  218. {
  219. $successfulRequests = 0;
  220. $failedRequests = 0;
  221. $results = [];
  222. $ip = '220.181.7.203';
  223. $port = 443;
  224. foreach (['selfHost', 'vps234', 'idcoffer', 'ip112', 'upx8', 'rss', 'vps1352'] as $service) {
  225. try {
  226. $result = NetworkDetection::networkStatus($ip, $port, $service);
  227. if (is_array($result)) {
  228. $successfulRequests++;
  229. $results["{$ip}:{$port}-{$service}"] = $result;
  230. } else {
  231. $failedRequests++;
  232. $results["{$ip}:{$port}-{$service}"] = 'Failed to get result';
  233. }
  234. } catch (Exception $e) {
  235. $failedRequests++;
  236. $results["{$ip}:{$port}-{$service}"] = 'Exception: '.$e->getMessage();
  237. }
  238. }
  239. // 输出测试结果摘要
  240. echo "实际网络连通性测试结果:\n";
  241. echo "成功请求: {$successfulRequests}\n";
  242. echo "失败请求: {$failedRequests}\n";
  243. echo '总请求数: '.($successfulRequests + $failedRequests)."\n\n";
  244. // 输出详细结果
  245. foreach ($results as $testName => $result) {
  246. echo "[{$testName}] - ".json_encode($result, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE)."\n";
  247. }
  248. $this->assertGreaterThan(0, $successfulRequests, '至少应有一个网络检测请求成功');
  249. }
  250. protected function setUp(): void
  251. {
  252. parent::setUp();
  253. // 清理 HTTP 假造与缓存
  254. Http::fake([]);
  255. // 重置 basicRequest
  256. $ref = new ReflectionClass(NetworkDetection::class);
  257. if ($ref->hasProperty('basicRequest')) {
  258. $prop = $ref->getProperty('basicRequest');
  259. $prop->setAccessible(true);
  260. $prop->setValue(null, null);
  261. }
  262. }
  263. }