createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'US', 'as' => 'AS16509 Amazon.com, Inc.', 'org' => 'Amazon Web Services', 'isp' => 'Amazon.com, Inc.', ]); $result = $service->detect('1.2.3.4'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('cloud_asn:', $result['reason']); $this->assertStringContainsString('amazon', $result['reason']); } /** @test */ public function test_google_cloud_ip_as_risky() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'US', 'as' => 'AS15169 Google LLC', 'org' => 'Google Cloud Platform', 'isp' => 'Google LLC', ]); $result = $service->detect('8.8.8.8'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('cloud_asn:', $result['reason']); } /** @test */ public function test_azure_ip_as_risky() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'US', 'as' => 'AS8075 Microsoft Corporation', 'org' => 'Microsoft Azure', 'isp' => 'Microsoft Corporation', ]); $result = $service->detect('40.76.4.15'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('microsoft', $result['reason']); } /** @test */ public function test_digitalocean_ip_as_risky() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'US', 'as' => 'AS14061 DigitalOcean, LLC', 'org' => 'DigitalOcean, LLC', 'isp' => 'DigitalOcean, LLC', ]); $result = $service->detect('167.71.0.1'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('digitalocean', $result['reason']); } // ==================== 非美国 IP 检测 ==================== /** @test */ public function test_china_ip_as_risky() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'CN', 'as' => 'AS4134 CHINANET', 'org' => 'Chinanet', 'isp' => 'Chinanet', ]); $result = $service->detect('114.114.114.114'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('non_us:CN', $result['reason']); } /** @test */ public function test_brazil_ip_as_risky() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'BR', 'as' => 'AS7738 Vivo', 'org' => 'Telefonica Brasil', 'isp' => 'Vivo', ]); $result = $service->detect('200.200.0.1'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('non_us:BR', $result['reason']); } // ==================== 正常 IP ==================== /** @test */ public function test_normal_us_residential_ip_not_flagged() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'US', 'as' => 'AS7922 Comcast Cable', 'org' => 'Comcast Cable Communications', 'isp' => 'Comcast Cable', ]); $result = $service->detect('73.162.0.1'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } // ==================== 边界值 ==================== /** @test */ public function test_skips_empty_ip() { $service = new IpRiskService(); $result = $service->detect(''); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_skips_localhost_ip() { $service = new IpRiskService(); $result = $service->detect('127.0.0.1'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_skips_ipv6_localhost() { $service = new IpRiskService(); $result = $service->detect('::1'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_skips_private_ip_range_10() { $service = new IpRiskService(); $result = $service->detect('10.0.0.1'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_skips_private_ip_range_192_168() { $service = new IpRiskService(); $result = $service->detect('192.168.1.1'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_safe_when_api_query_fails() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn(null); $result = $service->detect('8.8.8.8'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_safe_when_api_returns_fail_status() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'status' => 'fail', 'message' => 'invalid query', ]); $result = $service->detect('999.999.999.999'); $this->assertFalse($result['is_risky']); $this->assertEmpty($result['reason']); } /** @test */ public function test_cloud_asn_priority_over_non_us() { $service = $this->createPartialMock(IpRiskService::class, ['queryIpInfo']); $service->method('queryIpInfo')->willReturn([ 'countryCode' => 'US', 'as' => 'AS16509 Amazon.com, Inc.', 'org' => 'Amazon Web Services', 'isp' => 'Amazon.com, Inc.', ]); $result = $service->detect('52.95.0.1'); $this->assertTrue($result['is_risky']); $this->assertStringContainsString('cloud_asn', $result['reason']); } }