createMock(SettingRepositoryInterface::class); $crypto = $crypto ?? $this->createMock(SettingsCryptoGatewayInterface::class); return new HelpdeskSettingsGateway(new SettingsMetadataGateway($settings), $crypto); } public function testGetAuthModeDefaultsToBasic(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturn(null); $gateway = $this->createGateway($settings); $this->assertSame('basic', $gateway->getAuthMode()); } public function testGetAuthModeReturnsOAuth2WhenSet(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturn('oauth2'); $gateway = $this->createGateway($settings); $this->assertSame('oauth2', $gateway->getAuthMode()); } public function testSetAuthModeRejectsInvalidMode(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $gateway = $this->createGateway($settings); $this->assertFalse($gateway->setAuthMode('invalid')); } public function testSetODataBaseUrlRejectsHttp(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $gateway = $this->createGateway($settings); $this->assertFalse($gateway->setODataBaseUrl('http://insecure.example.com')); } public function testSetODataBaseUrlAcceptsHttps(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->once()) ->method('set') ->with(HelpdeskSettingsGateway::KEY_ODATA_BASE_URL, 'https://bc.example.com/OData', 'helpdesk.bc_odata_base_url') ->willReturn(true); $gateway = $this->createGateway($settings); $this->assertTrue($gateway->setODataBaseUrl('https://bc.example.com/OData')); } public function testGetBasicPasswordReturnsNullWhenDecryptFails(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturn('encrypted-value'); $crypto = $this->createMock(SettingsCryptoGatewayInterface::class); $crypto->method('decryptString')->willThrowException(new \RuntimeException('bad key')); $gateway = $this->createGateway($settings, $crypto); $this->assertNull($gateway->getBasicPassword()); } public function testSetBasicPasswordEncryptsAndPersists(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->once()) ->method('set') ->with(HelpdeskSettingsGateway::KEY_BASIC_PASSWORD_ENC, 'enc-pw', 'helpdesk.bc_basic_password_enc') ->willReturn(true); $crypto = $this->createMock(SettingsCryptoGatewayInterface::class); $crypto->expects($this->once()) ->method('encryptString') ->with('my-password') ->willReturn('enc-pw'); $gateway = $this->createGateway($settings, $crypto); $this->assertTrue($gateway->setBasicPassword('my-password')); } public function testSetBasicPasswordClearsWhenEmpty(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->once()) ->method('set') ->with(HelpdeskSettingsGateway::KEY_BASIC_PASSWORD_ENC, null, 'helpdesk.bc_basic_password_enc') ->willReturn(true); $crypto = $this->createMock(SettingsCryptoGatewayInterface::class); $crypto->expects($this->never())->method('encryptString'); $gateway = $this->createGateway($settings, $crypto); $this->assertTrue($gateway->setBasicPassword('')); } public function testSetBasicPasswordReturnsFalseOnEncryptionFailure(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $crypto = $this->createMock(SettingsCryptoGatewayInterface::class); $crypto->method('encryptString')->willThrowException(new \RuntimeException('encryption failed')); $gateway = $this->createGateway($settings, $crypto); $this->assertFalse($gateway->setBasicPassword('my-password')); } public function testBuildEntityUrlConstructsCorrectUrl(): void { $callMap = [ [HelpdeskSettingsGateway::KEY_ODATA_BASE_URL, 'https://bc.example.com/ODataV4'], [HelpdeskSettingsGateway::KEY_COMPANY_NAME, 'Test Company'], ]; $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturnCallback(static function (string $key) use ($callMap): ?string { foreach ($callMap as [$k, $v]) { if ($k === $key) { return $v; } } return null; }); $gateway = $this->createGateway($settings); $url = $gateway->buildEntityUrl('Integration_Customer_Card'); $this->assertSame("https://bc.example.com/ODataV4/Company('Test%20Company')/Integration_Customer_Card", $url); } public function testValidateConfigurationReportsMissingBasicCredentials(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturn(null); $gateway = $this->createGateway($settings); $missing = $gateway->validateConfiguration(); $this->assertNotEmpty($missing); $this->assertContains('BC Basic Auth User', $missing); $this->assertContains('BC Basic Auth Password', $missing); } public function testIsConfiguredReturnsTrueWhenBasicCredentialsPresent(): void { $callMap = [ [HelpdeskSettingsGateway::KEY_AUTH_MODE, 'basic'], [HelpdeskSettingsGateway::KEY_BASIC_USER, 'testuser'], [HelpdeskSettingsGateway::KEY_BASIC_PASSWORD_ENC, 'encrypted'], ]; $settings = $this->createMock(SettingRepositoryInterface::class); $settings->method('getValue')->willReturnCallback(static function (string $key) use ($callMap): ?string { foreach ($callMap as [$k, $v]) { if ($k === $key) { return $v; } } return null; }); $crypto = $this->createMock(SettingsCryptoGatewayInterface::class); $crypto->method('decryptString')->willReturn('decrypted-password'); $gateway = $this->createGateway($settings, $crypto); $this->assertTrue($gateway->isConfigured()); } public function testSetOAuthTokenEndpointRejectsHttp(): void { $settings = $this->createMock(SettingRepositoryInterface::class); $settings->expects($this->never())->method('set'); $gateway = $this->createGateway($settings); $this->assertFalse($gateway->setOAuthTokenEndpoint('http://login.microsoftonline.com/token')); } }