3f9c2500d9
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
87 lines
3.5 KiB
PHP
87 lines
3.5 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace KTXT\Unit\Service;
|
|
|
|
use KTXC\Models\Tenant\TenantConfiguration;
|
|
use KTXC\Models\Tenant\TenantObject;
|
|
use KTXC\Service\FirewallSettingsService;
|
|
use KTXC\Service\TenantService;
|
|
use KTXF\Event\EventDispatcherInterface;
|
|
use KTXC\Security\Event\FirewallSettingsUpdatedEvent;
|
|
use PHPUnit\Framework\Attributes\TestDox;
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
final class FirewallSettingsServiceTest extends TestCase
|
|
{
|
|
#[TestDox('Settings updates preserve other tenant configuration and emit complete audit context')]
|
|
public function testUpdate(): void
|
|
{
|
|
$tenant = (new TenantObject())
|
|
->setId('507f1f77bcf86cd799439011')
|
|
->setIdentifier('tenant-a')
|
|
->setConfiguration((new TenantConfiguration())->jsonDeserialize([
|
|
'firewall' => ['enabled' => true, 'maxAuthFailures' => 5],
|
|
'security' => ['code' => 'preserved'],
|
|
]));
|
|
$tenants = $this->createMock(TenantService::class);
|
|
$tenants->method('fetchById')->with('tenant-a')->willReturn($tenant);
|
|
$tenants->expects(self::once())
|
|
->method('deposit')
|
|
->with(self::callback(static fn(TenantObject $updated): bool =>
|
|
$updated->getConfiguration()->firewall()->maxAuthFailures() === 8
|
|
&& $updated->getConfiguration()->security()->jsonSerialize()['code'] === 'preserved'
|
|
))
|
|
->willReturnArgument(0);
|
|
$events = $this->createMock(EventDispatcherInterface::class);
|
|
$events->expects(self::once())
|
|
->method('dispatch')
|
|
->with(self::callback(static fn(FirewallSettingsUpdatedEvent $event): bool =>
|
|
$event->getName() === FirewallSettingsUpdatedEvent::class
|
|
&& $event->getTenantId() === 'tenant-a'
|
|
&& $event->getIdentityId() === 'admin-a'
|
|
&& $event->get('changeReason') === 'Tighten authentication controls'
|
|
&& $event->get('previous')['maxAuthFailures'] === 5
|
|
&& $event->get('current')['maxAuthFailures'] === 8
|
|
));
|
|
|
|
$result = (new FirewallSettingsService($tenants, $events))->update(
|
|
'tenant-a', false, 8, 600, 7200, 'Tighten authentication controls', 'admin-a'
|
|
);
|
|
|
|
self::assertSame([
|
|
'enabled' => false,
|
|
'maxAuthFailures' => 8,
|
|
'authFailureWindow' => 600,
|
|
'autoBlockDuration' => 7200,
|
|
], $result);
|
|
}
|
|
|
|
#[TestDox('Invalid settings are rejected before tenant reads or persistence')]
|
|
public function testValidation(): void
|
|
{
|
|
$tenants = $this->createMock(TenantService::class);
|
|
$tenants->expects(self::never())->method('fetchById');
|
|
$tenants->expects(self::never())->method('deposit');
|
|
$this->expectException(\InvalidArgumentException::class);
|
|
|
|
(new FirewallSettingsService(
|
|
$tenants,
|
|
$this->createStub(EventDispatcherInterface::class)
|
|
))->update('tenant-a', true, 0, 300, 3600, 'Invalid threshold', 'admin-a');
|
|
}
|
|
|
|
#[TestDox('Unknown tenants return no configuration without emitting audit events')]
|
|
public function testMissingTenant(): void
|
|
{
|
|
$tenants = $this->createStub(TenantService::class);
|
|
$events = $this->createMock(EventDispatcherInterface::class);
|
|
$events->expects(self::never())->method('dispatch');
|
|
|
|
self::assertNull((new FirewallSettingsService($tenants, $events))->update(
|
|
'missing', true, 5, 300, 3600, 'Apply defaults', 'admin-a'
|
|
));
|
|
}
|
|
}
|