newSessionGateway(null, null); self::assertSame(30, $gateway->getSessionIdleTimeoutMinutes()); self::assertSame(8, $gateway->getSessionAbsoluteTimeoutHours()); } public function testSessionTimeoutsUseStoredValuesWhenValid(): void { $gateway = $this->newSessionGateway('15', '12'); self::assertSame(15, $gateway->getSessionIdleTimeoutMinutes()); self::assertSame(12, $gateway->getSessionAbsoluteTimeoutHours()); self::assertSame(900, $gateway->getSessionIdleTimeoutSeconds()); self::assertSame(43200, $gateway->getSessionAbsoluteTimeoutSeconds()); } public function testSessionTimeoutsFallbackWhenOutOfBounds(): void { $gateway = $this->newSessionGateway('2', '100'); self::assertSame(30, $gateway->getSessionIdleTimeoutMinutes()); self::assertSame(8, $gateway->getSessionAbsoluteTimeoutHours()); } public function testSessionSettersRejectInvalidValues(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $gateway = new SettingsSessionGateway(new SettingsMetadataGateway($settings)); self::assertFalse($gateway->setSessionIdleTimeoutMinutes(2)); self::assertFalse($gateway->setSessionIdleTimeoutMinutes(2000)); self::assertFalse($gateway->setSessionAbsoluteTimeoutHours(0)); } public function testAutoRememberDefaultUsesExpectedBooleanNormalization(): void { $metadata = $this->createMock(SettingsMetadataGateway::class); $metadata->method('getValue')->willReturnMap([ ['microsoft_auto_remember_default', null], ['microsoft_auto_remember_default', '1'], ]); $gateway = new SettingsLoginPersistenceGateway($metadata); self::assertFalse($gateway->isMicrosoftAutoRememberDefault()); $metadataTrue = $this->createMock(SettingsMetadataGateway::class); $metadataTrue->expects($this->any())->method('getValue')->with('microsoft_auto_remember_default')->willReturn('1'); $gatewayTrue = new SettingsLoginPersistenceGateway($metadataTrue); self::assertTrue($gatewayTrue->isMicrosoftAutoRememberDefault()); } public function testAutoRememberSetterPersistsBooleanFlag(): void { $metadata = $this->createMock(SettingsMetadataGateway::class); $metadata->expects($this->exactly(2)) ->method('set') ->willReturnCallback(static function (string $key, ?string $value): bool { return $key === 'microsoft_auto_remember_default' && in_array($value, ['0', '1'], true); }); $gateway = new SettingsLoginPersistenceGateway($metadata); self::assertTrue($gateway->setMicrosoftAutoRememberDefault(true)); self::assertTrue($gateway->setMicrosoftAutoRememberDefault(false)); } public function testRememberTokenLifetimeUsesFallbackWhenMissingOrInvalid(): void { $metadataMissing = $this->createMock(SettingsMetadataGateway::class); $metadataMissing->expects($this->any())->method('getInt')->with('remember_token_lifetime_days')->willReturn(null); $gatewayMissing = new SettingsLoginPersistenceGateway($metadataMissing); self::assertSame(30, $gatewayMissing->getRememberTokenLifetimeDays()); self::assertSame(30 * 86400, $gatewayMissing->getRememberTokenLifetimeSeconds()); $metadataInvalid = $this->createMock(SettingsMetadataGateway::class); $metadataInvalid->expects($this->any())->method('getInt')->with('remember_token_lifetime_days')->willReturn(500); $gatewayInvalid = new SettingsLoginPersistenceGateway($metadataInvalid); self::assertSame(30, $gatewayInvalid->getRememberTokenLifetimeDays()); } public function testRememberTokenLifetimeAcceptsValidRangeAndComputesSeconds(): void { $metadata = $this->createMock(SettingsMetadataGateway::class); $metadata->expects($this->any())->method('getInt')->with('remember_token_lifetime_days')->willReturn(7); $metadata->expects($this->once()) ->method('set') ->with('remember_token_lifetime_days', '14', $this->anything()) ->willReturn(true); $gateway = new SettingsLoginPersistenceGateway($metadata); self::assertSame(7, $gateway->getRememberTokenLifetimeDays()); self::assertSame(7 * 86400, $gateway->getRememberTokenLifetimeSeconds()); self::assertTrue($gateway->setRememberTokenLifetimeDays(14)); } public function testRememberTokenLifetimeRejectsInvalidRangeAndNullUsesDefault(): void { $rejectingMetadata = $this->createMock(SettingsMetadataGateway::class); $rejectingMetadata->expects($this->never())->method('set'); $rejectingGateway = new SettingsLoginPersistenceGateway($rejectingMetadata); self::assertFalse($rejectingGateway->setRememberTokenLifetimeDays(0)); self::assertFalse($rejectingGateway->setRememberTokenLifetimeDays(400)); $defaultingMetadata = $this->createMock(SettingsMetadataGateway::class); $defaultingMetadata->expects($this->once()) ->method('set') ->with('remember_token_lifetime_days', '30', $this->anything()) ->willReturn(true); $defaultingGateway = new SettingsLoginPersistenceGateway($defaultingMetadata); self::assertTrue($defaultingGateway->setRememberTokenLifetimeDays(null)); } private function newSessionGateway(?string $idle, ?string $absolute): SettingsSessionGateway { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturnCallback( static function (string $key) use ($idle, $absolute): ?string { return match ($key) { 'session_idle_timeout_minutes' => $idle, 'session_absolute_timeout_hours' => $absolute, default => null, }; } ); return new SettingsSessionGateway(new SettingsMetadataGateway($settings)); } }