2026-03-17 12:47:02 +08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
namespace HyperfTest\Cases\Unit\Middleware;
|
|
|
|
|
|
|
|
|
|
use App\Middleware\RequestLogMiddleware;
|
2026-03-18 08:49:10 +08:00
|
|
|
use App\Utils\RequestHelper;
|
2026-03-17 12:47:02 +08:00
|
|
|
use GuzzleHttp\Psr7\Response;
|
|
|
|
|
use GuzzleHttp\Psr7\ServerRequest;
|
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* RequestLogMiddleware 静态辅助方法单元测试
|
|
|
|
|
*
|
|
|
|
|
* 覆盖 sanitizeBody 敏感字段脱敏、extractResponseCode JSON 提取、getClientIp IP 获取
|
|
|
|
|
*
|
|
|
|
|
* @internal
|
|
|
|
|
* @coversNothing
|
|
|
|
|
*/
|
|
|
|
|
class RequestLogMiddlewareTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
// ========== sanitizeBody ==========
|
|
|
|
|
|
|
|
|
|
public function test_sanitize_body_replaces_password_with_asterisks(): void
|
|
|
|
|
{
|
|
|
|
|
$body = ['username' => 'admin', 'password' => 'secret123'];
|
|
|
|
|
$result = RequestLogMiddleware::sanitizeBody($body);
|
|
|
|
|
|
|
|
|
|
$this->assertSame('admin', $result['username']);
|
|
|
|
|
$this->assertSame('***', $result['password']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_sanitize_body_handles_nested_password_fields(): void
|
|
|
|
|
{
|
|
|
|
|
$body = [
|
|
|
|
|
'user' => [
|
|
|
|
|
'name' => 'test',
|
|
|
|
|
'password' => 'nested_secret',
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$result = RequestLogMiddleware::sanitizeBody($body);
|
|
|
|
|
|
|
|
|
|
$this->assertSame('test', $result['user']['name']);
|
|
|
|
|
$this->assertSame('***', $result['user']['password']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_sanitize_body_handles_all_password_variants(): void
|
|
|
|
|
{
|
|
|
|
|
$body = [
|
|
|
|
|
'password' => 'p1',
|
|
|
|
|
'old_password' => 'p2',
|
|
|
|
|
'new_password' => 'p3',
|
|
|
|
|
'password_confirmation' => 'p4',
|
|
|
|
|
];
|
|
|
|
|
$result = RequestLogMiddleware::sanitizeBody($body);
|
|
|
|
|
|
|
|
|
|
$this->assertSame('***', $result['password']);
|
|
|
|
|
$this->assertSame('***', $result['old_password']);
|
|
|
|
|
$this->assertSame('***', $result['new_password']);
|
|
|
|
|
$this->assertSame('***', $result['password_confirmation']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_sanitize_body_preserves_non_sensitive_fields(): void
|
|
|
|
|
{
|
|
|
|
|
$body = ['username' => 'admin', 'email' => 'a@b.com', 'status' => 1];
|
|
|
|
|
$result = RequestLogMiddleware::sanitizeBody($body);
|
|
|
|
|
|
|
|
|
|
$this->assertSame($body, $result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_sanitize_body_handles_empty_array(): void
|
|
|
|
|
{
|
|
|
|
|
$result = RequestLogMiddleware::sanitizeBody([]);
|
|
|
|
|
$this->assertSame([], $result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ========== extractResponseCode ==========
|
|
|
|
|
|
|
|
|
|
public function test_extract_response_code_from_json_response(): void
|
|
|
|
|
{
|
|
|
|
|
$response = new Response(200, ['Content-Type' => 'application/json'], json_encode([
|
|
|
|
|
'code' => 0,
|
|
|
|
|
'message' => 'success',
|
|
|
|
|
'data' => [],
|
|
|
|
|
]));
|
|
|
|
|
|
|
|
|
|
$code = RequestLogMiddleware::extractResponseCode($response);
|
|
|
|
|
$this->assertSame(0, $code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_extract_response_code_returns_null_for_non_json(): void
|
|
|
|
|
{
|
|
|
|
|
$response = new Response(200, ['Content-Type' => 'text/html'], '<html></html>');
|
|
|
|
|
|
|
|
|
|
$code = RequestLogMiddleware::extractResponseCode($response);
|
|
|
|
|
$this->assertNull($code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_extract_response_code_returns_null_when_no_code_field(): void
|
|
|
|
|
{
|
|
|
|
|
$response = new Response(200, ['Content-Type' => 'application/json'], json_encode([
|
|
|
|
|
'message' => 'ok',
|
|
|
|
|
]));
|
|
|
|
|
|
|
|
|
|
$code = RequestLogMiddleware::extractResponseCode($response);
|
|
|
|
|
$this->assertNull($code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ========== getClientIp ==========
|
|
|
|
|
|
|
|
|
|
public function test_get_client_ip_from_x_forwarded_for(): void
|
|
|
|
|
{
|
|
|
|
|
$request = new ServerRequest('GET', '/test', ['X-Forwarded-For' => '1.2.3.4, 5.6.7.8']);
|
|
|
|
|
|
2026-03-18 08:49:10 +08:00
|
|
|
$ip = RequestHelper::getClientIp($request);
|
2026-03-17 12:47:02 +08:00
|
|
|
$this->assertSame('1.2.3.4', $ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_get_client_ip_from_x_real_ip(): void
|
|
|
|
|
{
|
|
|
|
|
$request = new ServerRequest('GET', '/test', ['X-Real-IP' => '10.0.0.1']);
|
|
|
|
|
|
2026-03-18 08:49:10 +08:00
|
|
|
$ip = RequestHelper::getClientIp($request);
|
2026-03-17 12:47:02 +08:00
|
|
|
$this->assertSame('10.0.0.1', $ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_get_client_ip_from_server_params(): void
|
|
|
|
|
{
|
|
|
|
|
$request = new ServerRequest('GET', '/test', [], null, '1.1', ['remote_addr' => '192.168.1.1']);
|
|
|
|
|
|
2026-03-18 08:49:10 +08:00
|
|
|
$ip = RequestHelper::getClientIp($request);
|
2026-03-17 12:47:02 +08:00
|
|
|
$this->assertSame('192.168.1.1', $ip);
|
|
|
|
|
}
|
2026-03-17 14:17:06 +08:00
|
|
|
|
|
|
|
|
public function test_get_client_ip_returns_null_when_no_ip_available(): void
|
|
|
|
|
{
|
|
|
|
|
$request = new ServerRequest('GET', '/test');
|
|
|
|
|
|
2026-03-18 08:49:10 +08:00
|
|
|
$ip = RequestHelper::getClientIp($request);
|
2026-03-17 14:17:06 +08:00
|
|
|
$this->assertNull($ip);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_extract_response_code_handles_code_as_string(): void
|
|
|
|
|
{
|
|
|
|
|
$response = new Response(200, ['Content-Type' => 'application/json'], json_encode([
|
|
|
|
|
'code' => '200',
|
|
|
|
|
'message' => 'success',
|
|
|
|
|
]));
|
|
|
|
|
|
|
|
|
|
$code = RequestLogMiddleware::extractResponseCode($response);
|
|
|
|
|
$this->assertSame(200, $code);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function test_sanitize_body_handles_deeply_nested_structures(): void
|
|
|
|
|
{
|
|
|
|
|
$body = [
|
|
|
|
|
'level1' => [
|
|
|
|
|
'level2' => [
|
|
|
|
|
'level3' => [
|
|
|
|
|
'password' => 'deep_secret',
|
|
|
|
|
'token' => 'deep_token',
|
|
|
|
|
'name' => 'keep_this',
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
];
|
|
|
|
|
$result = RequestLogMiddleware::sanitizeBody($body);
|
|
|
|
|
|
|
|
|
|
$this->assertSame('***', $result['level1']['level2']['level3']['password']);
|
|
|
|
|
$this->assertSame('***', $result['level1']['level2']['level3']['token']);
|
|
|
|
|
$this->assertSame('keep_this', $result['level1']['level2']['level3']['name']);
|
|
|
|
|
}
|
2026-03-17 12:47:02 +08:00
|
|
|
}
|