- Change query_json column from TEXT to JSON, ip/user_agent to CHAR(64) for PII redaction compliance - Update repository, service, and views for hardened schema - Add architecture contract test for security logging redaction - Add search resource provider test coverage - Include DB migration script for existing installs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
172 lines
6.0 KiB
PHP
172 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Tests\Module\Audit\Providers;
|
|
|
|
use MintyPHP\Module\Audit\Providers\AuditSearchResourceProvider;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class AuditSearchResourceProviderTest extends TestCase
|
|
{
|
|
private AuditSearchResourceProvider $provider;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
$this->provider = new AuditSearchResourceProvider();
|
|
}
|
|
|
|
public function testResourcesReturnsBothAuditKeys(): void
|
|
{
|
|
$resources = $this->provider->resources();
|
|
|
|
$this->assertArrayHasKey('api-audit', $resources);
|
|
$this->assertArrayHasKey('system-audit', $resources);
|
|
$this->assertCount(2, $resources);
|
|
}
|
|
|
|
public function testResourcesContainRequiredSqlKeys(): void
|
|
{
|
|
foreach ($this->provider->resources() as $key => $resource) {
|
|
$this->assertArrayHasKey('label', $resource, "Resource '{$key}' missing label");
|
|
$this->assertArrayHasKey('permission', $resource, "Resource '{$key}' missing permission");
|
|
$this->assertArrayHasKey('count_sql', $resource, "Resource '{$key}' missing count_sql");
|
|
$this->assertArrayHasKey('preview_sql', $resource, "Resource '{$key}' missing preview_sql");
|
|
$this->assertArrayHasKey('result_sql', $resource, "Resource '{$key}' missing result_sql");
|
|
}
|
|
}
|
|
|
|
public function testResourcePermissionKeys(): void
|
|
{
|
|
$resources = $this->provider->resources();
|
|
|
|
$this->assertSame('audit.api.view', $resources['api-audit']['permission']);
|
|
$this->assertSame('audit.system.view', $resources['system-audit']['permission']);
|
|
}
|
|
|
|
public function testApiAuditSearchSqlIncludesUserAgentHashField(): void
|
|
{
|
|
$resources = $this->provider->resources();
|
|
$apiAudit = $resources['api-audit'];
|
|
|
|
$this->assertStringContainsString('user_agent like ?', $apiAudit['count_sql']);
|
|
$this->assertStringContainsString('user_agent like ?', $apiAudit['preview_sql']);
|
|
$this->assertStringContainsString('user_agent like ?', $apiAudit['result_sql']);
|
|
}
|
|
|
|
public function testMapApiAuditItem(): void
|
|
{
|
|
$row = [
|
|
'id' => 42,
|
|
'method' => 'POST',
|
|
'path' => '/api/v1/users',
|
|
'status_code' => 201,
|
|
'request_id' => 'req-abc',
|
|
'created_at' => '2026-03-15 10:30:00',
|
|
];
|
|
|
|
$result = $this->provider->mapResultItem('api-audit', $row);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame('POST /api/v1/users', $result['title']);
|
|
$this->assertStringContains('201', $result['subtitle']);
|
|
$this->assertStringContains('2026-03-15 10:30:00', $result['subtitle']);
|
|
$this->assertSame('bi-shield-check', $result['icon']);
|
|
$this->assertStringContainsString('admin/api-audit/view/42', $result['url']);
|
|
}
|
|
|
|
public function testMapApiAuditItemFallsBackToRequestId(): void
|
|
{
|
|
$row = ['id' => 1, 'method' => '', 'path' => '', 'status_code' => 0, 'request_id' => 'req-fallback'];
|
|
|
|
$result = $this->provider->mapResultItem('api-audit', $row);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame('req-fallback', $result['title']);
|
|
}
|
|
|
|
public function testMapApiAuditItemReturnsNullWhenEmpty(): void
|
|
{
|
|
$row = ['id' => 1, 'method' => '', 'path' => '', 'status_code' => 0, 'request_id' => ''];
|
|
|
|
$result = $this->provider->mapResultItem('api-audit', $row);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function testMapSystemAuditItem(): void
|
|
{
|
|
$row = [
|
|
'id' => 99,
|
|
'event_type' => 'admin.settings.update',
|
|
'outcome' => 'success',
|
|
'request_id' => 'req-xyz',
|
|
'created_at' => '2026-03-15 11:00:00',
|
|
];
|
|
|
|
$result = $this->provider->mapResultItem('system-audit', $row);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame('admin.settings.update', $result['title']);
|
|
$this->assertStringContains('SUCCESS', $result['subtitle']);
|
|
$this->assertStringContains('2026-03-15 11:00:00', $result['subtitle']);
|
|
$this->assertSame('bi-journal-lock', $result['icon']);
|
|
$this->assertStringContainsString('admin/system-audit/view/99', $result['url']);
|
|
}
|
|
|
|
public function testMapSystemAuditItemFallsBackToRequestId(): void
|
|
{
|
|
$row = ['id' => 1, 'event_type' => '', 'outcome' => '', 'request_id' => 'req-fallback'];
|
|
|
|
$result = $this->provider->mapResultItem('system-audit', $row);
|
|
|
|
$this->assertNotNull($result);
|
|
$this->assertSame('req-fallback', $result['title']);
|
|
}
|
|
|
|
public function testMapSystemAuditItemReturnsNullWhenEmpty(): void
|
|
{
|
|
$row = ['id' => 1, 'event_type' => '', 'outcome' => '', 'request_id' => ''];
|
|
|
|
$result = $this->provider->mapResultItem('system-audit', $row);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function testMapResultItemReturnsNullForUnknownKey(): void
|
|
{
|
|
$result = $this->provider->mapResultItem('unknown', ['id' => 1]);
|
|
|
|
$this->assertNull($result);
|
|
}
|
|
|
|
public function testListUrlForApiAudit(): void
|
|
{
|
|
$url = $this->provider->listUrl('api-audit', 'test%20query');
|
|
|
|
$this->assertStringContainsString('admin/api-audit', $url);
|
|
$this->assertStringContainsString('search=test%20query', $url);
|
|
}
|
|
|
|
public function testListUrlForSystemAudit(): void
|
|
{
|
|
$url = $this->provider->listUrl('system-audit', 'test');
|
|
|
|
$this->assertStringContainsString('admin/system-audit', $url);
|
|
$this->assertStringContainsString('search=test', $url);
|
|
}
|
|
|
|
public function testListUrlReturnsEmptyForUnknownKey(): void
|
|
{
|
|
$url = $this->provider->listUrl('unknown', 'test');
|
|
|
|
$this->assertSame('', $url);
|
|
}
|
|
|
|
/**
|
|
* Helper to check string containment (mirrors assertStringContainsString for readability).
|
|
*/
|
|
private static function assertStringContains(string $needle, string $haystack): void
|
|
{
|
|
self::assertStringContainsString($needle, $haystack);
|
|
}
|
|
}
|