createMock(SettingRepositoryInterface::class); $settings->expects($this->once()) ->method('set') ->with('app_locale', null, 'setting.app_locale') ->willReturn(true); $gateway = $this->newAppGateway($settings); self::assertTrue($gateway->setAppLocale(' ')); } public function testAppRegistrationDefaultIsTrueWhenUnset(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturnMap([ ['app_registration', null], ]); $gateway = $this->newAppGateway($settings); self::assertTrue($gateway->isRegistrationEnabled()); } public function testSystemAuditUsesFallbacksAndRejectsOutOfRangeRetention(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturnCallback(static function (string $key): ?string { return $key === 'system_audit_retention_days' ? '5' : null; }); $settings->expects($this->never())->method('set'); $gateway = new SettingsSystemAuditGateway(new SettingsMetadataGateway($settings)); self::assertTrue($gateway->isSystemAuditEnabled()); self::assertSame(365, $gateway->getSystemAuditRetentionDays()); self::assertFalse($gateway->setSystemAuditRetentionDays(10)); } public function testFrontendTelemetryUsesDefaultsWhenSettingsMissing(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturn(null); $gateway = new SettingsFrontendTelemetryGateway(new SettingsMetadataGateway($settings)); self::assertFalse($gateway->isFrontendTelemetryEnabled()); self::assertSame(0.2, $gateway->getFrontendTelemetrySampleRate()); self::assertSame(['warn_once', 'ajax_error'], $gateway->getFrontendTelemetryAllowedEvents()); } public function testFrontendTelemetryRejectsOutOfRangeSampleRate(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $gateway = new SettingsFrontendTelemetryGateway(new SettingsMetadataGateway($settings)); self::assertFalse($gateway->setFrontendTelemetrySampleRate(1.5)); } public function testFrontendTelemetryNormalizesAllowedEvents(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->once()) ->method('set') ->with( 'frontend_telemetry_allowed_events', 'ajax_error,warn_once', 'setting.frontend_telemetry_allowed_events' ) ->willReturn(true); $gateway = new SettingsFrontendTelemetryGateway(new SettingsMetadataGateway($settings)); self::assertTrue($gateway->setFrontendTelemetryAllowedEvents(['frontend.warn_once', 'ajax_error', 'invalid'])); } public function testSmtpSecureReturnsNullForNone(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturnCallback(static function (string $key): ?string { if ($key === 'smtp_secure') { return 'none'; } return null; }); $gateway = new SettingsSmtpGateway(new SettingsMetadataGateway($settings)); self::assertNull($gateway->getSmtpSecure()); } public function testSmtpSecureRejectsInvalidValue(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $gateway = new SettingsSmtpGateway(new SettingsMetadataGateway($settings)); self::assertFalse($gateway->setSmtpSecure('starttls')); } public function testSmtpHostPersistsNullForEmptyString(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->once()) ->method('set') ->with('smtp_host', null, 'setting.smtp_host') ->willReturn(true); $gateway = new SettingsSmtpGateway(new SettingsMetadataGateway($settings)); self::assertTrue($gateway->setSmtpHost(' ')); } private function newAppGateway(SettingRepositoryInterface $settingRepository): SettingsAppGateway { $themeConfig = $this->createMock(ThemeConfigGateway::class); $themeConfig->method('all')->willReturn(['light' => 'Light', 'dark' => 'Dark']); return new SettingsAppGateway(new SettingsMetadataGateway($settingRepository), $themeConfig); } }