Files
server/tests/php/Unit/Controllers/FirewallControllerTest.php
T
2026-08-03 22:22:07 -04:00

117 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
namespace KTXT\Unit\Controllers;
use KTXC\Context\IdentityContextInterface;
use KTXC\Context\TenantContextInterface;
use KTXC\Controllers\FirewallController;
use KTXC\Service\FirewallRuleCache;
use KTXC\Service\FirewallRuleManager;
use KTXC\Service\FirewallStatusService;
use KTXC\Service\FirewallLogService;
use KTXC\Service\SystemFirewallLogService;
use KTXC\Service\SystemFirewallRuleService;
use KTXC\Service\SystemFirewallStatusService;
use KTXC\Service\TenantFirewallLogService;
use KTXC\Service\TenantFirewallRuleService;
use KTXC\Service\TenantFirewallStatusService;
use KTXC\Stores\FirewallStore;
use KTXF\Event\EventDispatcherInterface;
use KTXF\Routing\Attributes\AuthenticatedRoute;
use PHPUnit\Framework\Attributes\AllowMockObjectsWithoutExpectations;
use PHPUnit\Framework\Attributes\TestDox;
use PHPUnit\Framework\TestCase;
#[AllowMockObjectsWithoutExpectations]
final class FirewallControllerTest extends TestCase
{
private FirewallStore $store;
private FirewallController $controller;
protected function setUp(): void
{
$this->store = $this->createMock(FirewallStore::class);
$tenant = $this->createMock(TenantContextInterface::class);
$tenant->method('requireIdentifier')->willReturn('tenant-a');
$identity = $this->createMock(IdentityContextInterface::class);
$identity->method('hasPermission')->willReturn(true);
$manager = new FirewallRuleManager(
$this->store,
new FirewallRuleCache($this->store),
$this->createStub(EventDispatcherInterface::class)
);
$this->controller = new FirewallController(
new TenantFirewallRuleService($manager, $tenant, $identity),
new SystemFirewallRuleService($manager, $identity),
new TenantFirewallLogService(new FirewallLogService($this->store), $tenant, $identity),
new SystemFirewallLogService(new FirewallLogService($this->store), $identity),
new TenantFirewallStatusService(new FirewallStatusService($this->store), $tenant, $identity),
new SystemFirewallStatusService(new FirewallStatusService($this->store), $identity)
);
}
#[TestDox('Tenant rule endpoint returns bounded paginated results')]
public function testTenantRules(): void
{
$this->store->expects(self::once())
->method('queryRules')
->with('tenant', 'tenant-a', 'active', null, null, 25, 10)
->willReturn(['items' => [], 'total' => 0, 'limit' => 25, 'offset' => 10]);
$response = $this->controller->tenantRules(limit: '25', offset: '10');
$data = json_decode($response->getContent(), true, flags: JSON_THROW_ON_ERROR);
self::assertSame(200, $response->getStatusCode());
self::assertSame(25, $data['limit']);
self::assertSame(10, $data['offset']);
}
#[TestDox('Rule endpoints reject malformed and excessive pagination')]
public function testPaginationValidation(): void
{
$this->store->expects(self::never())->method('queryRules');
self::assertSame(400, $this->controller->tenantRules(limit: 'invalid')->getStatusCode());
self::assertSame(400, $this->controller->systemRules(limit: '101')->getStatusCode());
}
#[TestDox('Metric endpoints return scoped counts and stable validation errors')]
public function testMetrics(): void
{
$this->store->method('countBlockedRequests')->willReturn(4);
$response = $this->controller->tenantMetrics();
$data = json_decode($response->getContent(), true, flags: JSON_THROW_ON_ERROR);
self::assertSame(4, $data['blockedRequests']);
self::assertSame(400, $this->controller->systemMetrics(since: 'not-a-date')->getStatusCode());
}
#[TestDox('Every rule endpoint declares its scope-specific read permission')]
public function testRoutePermissions(): void
{
$expected = [
'tenantRules' => TenantFirewallRuleService::PERMISSION_READ,
'tenantRule' => TenantFirewallRuleService::PERMISSION_READ,
'effectivePolicy' => TenantFirewallRuleService::PERMISSION_READ,
'systemRules' => SystemFirewallRuleService::PERMISSION_READ,
'systemRule' => SystemFirewallRuleService::PERMISSION_READ,
'tenantLogs' => TenantFirewallLogService::PERMISSION_READ,
'systemLogs' => SystemFirewallLogService::PERMISSION_READ,
'tenantMetrics' => TenantFirewallLogService::PERMISSION_READ,
'tenantConfiguration' => TenantFirewallStatusService::PERMISSION_SETTINGS_READ,
'systemMetrics' => SystemFirewallLogService::PERMISSION_READ,
'maintenanceStatus' => SystemFirewallStatusService::PERMISSION_MAINTENANCE_READ,
];
foreach ($expected as $method => $permission) {
$attributes = (new \ReflectionMethod(FirewallController::class, $method))
->getAttributes(AuthenticatedRoute::class);
self::assertCount(1, $attributes);
self::assertSame([$permission], $attributes[0]->newInstance()->permissions);
}
}
}