110 lines
3.8 KiB
PHP
110 lines
3.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Unit;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class ApiLogTest extends TestCase
|
|
{
|
|
private string $logDir;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
require_once dirname(__DIR__, 2) . '/lib/api_log.php';
|
|
$this->logDir = $_ENV['QDB_API_LOG_DIR'] ?? (QDB_TEST_UPLOADS . '/logs/api');
|
|
if (!is_dir($this->logDir)) {
|
|
mkdir($this->logDir, 0775, true);
|
|
}
|
|
$this->clearLogFiles();
|
|
unset($GLOBALS['qdb_api_log_ctx']);
|
|
}
|
|
|
|
protected function tearDown(): void
|
|
{
|
|
$this->clearLogFiles();
|
|
unset($GLOBALS['qdb_api_log_ctx']);
|
|
}
|
|
|
|
public function testClassifyActivity(): void
|
|
{
|
|
$this->assertNull(qdb_api_log_classify_activity('OPTIONS', 'web', 'users'));
|
|
$this->assertSame('web_change', qdb_api_log_classify_activity('POST', 'web', 'users'));
|
|
$this->assertSame('app_change', qdb_api_log_classify_activity('POST', '', 'app_questionnaires'));
|
|
$this->assertSame('app_sync', qdb_api_log_classify_activity('GET', '', 'app_questionnaires'));
|
|
$this->assertSame('web_export', qdb_api_log_classify_activity('GET', 'web', 'export'));
|
|
$this->assertSame('web_change', qdb_api_log_classify_activity('POST', 'web', 'backup'));
|
|
$this->assertTrue(qdb_api_log_is_website_download('backup'));
|
|
}
|
|
|
|
public function testRedactSensitiveFields(): void
|
|
{
|
|
$redacted = qdb_api_log_redact_value([
|
|
'username' => 'coach1',
|
|
'password' => 'secret',
|
|
'encrypted' => str_repeat('x', 40),
|
|
]);
|
|
$this->assertSame('coach1', $redacted['username']);
|
|
$this->assertSame('[REDACTED]', $redacted['password']);
|
|
$this->assertSame('[ENCRYPTED:40 chars]', $redacted['encrypted']);
|
|
}
|
|
|
|
public function testRedactQueryString(): void
|
|
{
|
|
$qs = qdb_api_log_redact_query_string('questionnaireID=qn1&token=abc&password=pw');
|
|
parse_str((string)$qs, $params);
|
|
$this->assertSame('qn1', $params['questionnaireID'] ?? null);
|
|
$this->assertSame('[REDACTED]', $params['token'] ?? null);
|
|
$this->assertSame('[REDACTED]', $params['password'] ?? null);
|
|
}
|
|
|
|
public function testTokenHintMasksMiddle(): void
|
|
{
|
|
$hint = qdb_api_log_token_hint('0123456789abcdef');
|
|
$this->assertStringContainsString('…', $hint);
|
|
$this->assertStringNotContainsString('56789', $hint);
|
|
}
|
|
|
|
public function testClientIpUsesForwardedFor(): void
|
|
{
|
|
$_SERVER['HTTP_X_FORWARDED_FOR'] = '203.0.113.50, 198.51.100.1';
|
|
$_SERVER['REMOTE_ADDR'] = '127.0.0.1';
|
|
$this->assertSame('203.0.113.50', qdb_api_log_client_ip());
|
|
unset($_SERVER['HTTP_X_FORWARDED_FOR']);
|
|
}
|
|
|
|
public function testBeginFinishWritesWebChangeEntry(): void
|
|
{
|
|
$_SERVER['HTTP_X_QDB_CLIENT'] = 'web';
|
|
$_SERVER['REQUEST_URI'] = '/api/users';
|
|
$_SERVER['QUERY_STRING'] = '';
|
|
qdb_test_set_request_body(json_encode(['username' => 'new_coach'], JSON_THROW_ON_ERROR));
|
|
http_response_code(200);
|
|
|
|
qdb_api_log_begin('POST', 'users');
|
|
qdb_api_log_finish();
|
|
|
|
$date = date('Y-m-d');
|
|
$read = qdb_api_log_read_entries($date, 'web_change', 10);
|
|
$entries = $read['entries'];
|
|
$this->assertNotEmpty($entries);
|
|
$last = $entries[0];
|
|
$this->assertSame('web_change', $last['activity']);
|
|
$this->assertSame('POST', $last['method']);
|
|
$this->assertSame('users', $last['route']);
|
|
$changes = $last['changes'] ?? [];
|
|
$fields = array_column($changes, 'field');
|
|
$this->assertContains('username', $fields);
|
|
|
|
qdb_test_reset_http_state();
|
|
}
|
|
|
|
private function clearLogFiles(): void
|
|
{
|
|
foreach (glob($this->logDir . '/api-*.log') ?: [] as $path) {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
}
|