From cadc5d8a85248af8bc1cb4bb47ad13250d889fa1 Mon Sep 17 00:00:00 2001 From: Ivan Gantsev Date: Mon, 25 May 2026 21:52:36 +0300 Subject: [PATCH] refactor: use getters with validation in SourcesConfig and DestinationsConfig --- src/Config/Config.php | 8 ---- .../DestinationConfig/DestinationsConfig.php | 11 ++++- src/Config/SourceConfig/SourcesConfig.php | 20 ++++++++- .../ConcreteDestinationFactory.php | 9 ++-- src/Source/ConcreteSourceFactory.php | 10 +---- tests/Unit/Config/ConfigTest.php | 44 ++++++++++++++++--- tests/Unit/Config/YamlConfigProviderTest.php | 32 ++++++-------- 7 files changed, 86 insertions(+), 48 deletions(-) diff --git a/src/Config/Config.php b/src/Config/Config.php index b97eba1..15f9f79 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -24,13 +24,5 @@ public function __construct( if (!property_exists($this->sources, $this->source->value)) { throw new LogicException("Source {$this->source->value} is not supported"); } - - if ($this->destinations->{$this->destination->value} === null) { - throw new ConfigException("Destination {$this->destination->value} is not configured"); - } - - if ($this->sources->{$this->source->value} === null) { - throw new ConfigException("Source {$this->source->value} is not configured"); - } } } diff --git a/src/Config/DestinationConfig/DestinationsConfig.php b/src/Config/DestinationConfig/DestinationsConfig.php index 371fb87..3419832 100644 --- a/src/Config/DestinationConfig/DestinationsConfig.php +++ b/src/Config/DestinationConfig/DestinationsConfig.php @@ -2,10 +2,19 @@ namespace Igancev\WorkReporter\Config\DestinationConfig; +use Igancev\WorkReporter\Config\ConfigException; + readonly class DestinationsConfig { public function __construct( - public ?YouTrackConfig $youTrack = null, + private ?YouTrackConfig $youTrack = null, ) { } + + public function getYouTrack(): YouTrackConfig + { + return $this->youTrack ?? throw new ConfigException( + 'Destination youTrack is not configured' + ); + } } diff --git a/src/Config/SourceConfig/SourcesConfig.php b/src/Config/SourceConfig/SourcesConfig.php index 0aae488..7307b9a 100644 --- a/src/Config/SourceConfig/SourcesConfig.php +++ b/src/Config/SourceConfig/SourcesConfig.php @@ -2,11 +2,27 @@ namespace Igancev\WorkReporter\Config\SourceConfig; +use Igancev\WorkReporter\Config\ConfigException; + readonly class SourcesConfig { public function __construct( - public ?SuperProductivityConfig $superProductivity = null, - public ?PlainJsonConfig $plainJson = null, + private ?SuperProductivityConfig $superProductivity = null, + private ?PlainJsonConfig $plainJson = null, ) { } + + public function getPlainJson(): PlainJsonConfig + { + return $this->plainJson ?? throw new ConfigException( + 'Source plainJson is not configured' + ); + } + + public function getSuperProductivity(): SuperProductivityConfig + { + return $this->superProductivity ?? throw new ConfigException( + 'Source superProductivity is not configured' + ); + } } diff --git a/src/Destination/ConcreteDestinationFactory.php b/src/Destination/ConcreteDestinationFactory.php index 661c4ab..f40259b 100644 --- a/src/Destination/ConcreteDestinationFactory.php +++ b/src/Destination/ConcreteDestinationFactory.php @@ -30,10 +30,7 @@ public function build(DestinationType $destination): Destination */ private function buildYouTrackDestination(): YouTrackDestination { - $youtrackConfigDestinations = $this->configProvider->getConfig()->destinations->youTrack; - if ($youtrackConfigDestinations === null) { - throw new DestinationException("Definition of YouTrack destination missing in configuration"); - } + $youtrackConfig = $this->configProvider->getConfig()->destinations->getYouTrack(); return new YouTrackDestination( new HttpClientBuilder() @@ -41,8 +38,8 @@ private function buildYouTrackDestination(): YouTrackDestination new UnlimitedConnectionPool(new DefaultConnectionFactory(new DnsSocketConnector())) ) ->build(), - $youtrackConfigDestinations->url, - $youtrackConfigDestinations->token, + $youtrackConfig->url, + $youtrackConfig->token, ); } } diff --git a/src/Source/ConcreteSourceFactory.php b/src/Source/ConcreteSourceFactory.php index 7a75bf3..3c733af 100644 --- a/src/Source/ConcreteSourceFactory.php +++ b/src/Source/ConcreteSourceFactory.php @@ -28,10 +28,7 @@ public function build(SourceType $source): TimeEntriesSource */ private function buildPlainJsonSource(): PlainJsonTimeEntriesSource { - $config = $this->configProvider->getConfig()->sources->plainJson; - if ($config === null) { - throw new SourceException("PlainJson source configuration is missing"); - } + $config = $this->configProvider->getConfig()->sources->getPlainJson(); return new PlainJsonTimeEntriesSource($config->filePath); } @@ -41,10 +38,7 @@ private function buildPlainJsonSource(): PlainJsonTimeEntriesSource */ private function buildFromSuperProductivitySource(): SuperProductivitySyncSource { - $config = $this->configProvider->getConfig()->sources->superProductivity; - if ($config === null) { - throw new SourceException("SuperProductivity source configuration is missing"); - } + $config = $this->configProvider->getConfig()->sources->getSuperProductivity(); return new SuperProductivitySyncSource($config->syncFilePath); } diff --git a/tests/Unit/Config/ConfigTest.php b/tests/Unit/Config/ConfigTest.php index 9bfd73f..4f33b43 100644 --- a/tests/Unit/Config/ConfigTest.php +++ b/tests/Unit/Config/ConfigTest.php @@ -5,6 +5,7 @@ namespace Tests\Unit\Config; use Igancev\WorkReporter\Config\Config; +use Igancev\WorkReporter\Config\ConfigException; use Igancev\WorkReporter\Config\DestinationConfig\DestinationsConfig; use Igancev\WorkReporter\Config\DestinationConfig\YouTrackConfig; use Igancev\WorkReporter\Config\SourceConfig\PlainJsonConfig; @@ -46,19 +47,52 @@ public function testConfigStoresAllProperties(): void $this->assertSame($destinationsConfig, $config->destinations); } - public function testSourcesConfigDefaults(): void + public function testSourcesConfigGetPlainJsonThrowsWhenNull(): void { $config = new SourcesConfig(); - $this->assertNull($config->superProductivity); - $this->assertNull($config->plainJson); + $this->expectException(ConfigException::class); + $this->expectExceptionMessage('Source plainJson is not configured'); + + $config->getPlainJson(); + } + + public function testSourcesConfigGetSuperProductivityThrowsWhenNull(): void + { + $config = new SourcesConfig(); + + $this->expectException(ConfigException::class); + $this->expectExceptionMessage('Source superProductivity is not configured'); + + $config->getSuperProductivity(); } - public function testDestinationsConfigDefaults(): void + public function testDestinationsConfigGetYouTrackThrowsWhenNull(): void { $config = new DestinationsConfig(); - $this->assertNull($config->youTrack); + $this->expectException(ConfigException::class); + $this->expectExceptionMessage('Destination youTrack is not configured'); + + $config->getYouTrack(); + } + + public function testSourcesConfigGettersReturnValues(): void + { + $plainJson = new PlainJsonConfig('/tmp/plain.json'); + $superProductivity = new SuperProductivityConfig('/tmp/sync'); + $config = new SourcesConfig($superProductivity, $plainJson); + + $this->assertSame($plainJson, $config->getPlainJson()); + $this->assertSame($superProductivity, $config->getSuperProductivity()); + } + + public function testDestinationsConfigGetterReturnsValue(): void + { + $youTrack = new YouTrackConfig('http://yt.local', 'token'); + $config = new DestinationsConfig($youTrack); + + $this->assertSame($youTrack, $config->getYouTrack()); } public function testPlainJsonConfigStoresFilePath(): void diff --git a/tests/Unit/Config/YamlConfigProviderTest.php b/tests/Unit/Config/YamlConfigProviderTest.php index fc70542..418c102 100644 --- a/tests/Unit/Config/YamlConfigProviderTest.php +++ b/tests/Unit/Config/YamlConfigProviderTest.php @@ -63,16 +63,13 @@ public function testParsesFullConfig(): void $this->assertSame(SourceType::SuperProductivity, $config->source); $this->assertSame(DestinationType::YouTrack, $config->destination); - $this->assertNotNull($config->sources->superProductivity); - $this->assertSame('/tmp/sp_sync', $config->sources->superProductivity->syncFilePath); - $this->assertNotNull($config->sources->plainJson); - $this->assertSame('/tmp/plain.json', $config->sources->plainJson->filePath); - $this->assertNotNull($config->destinations->youTrack); - $this->assertSame('http://localhost:8080', $config->destinations->youTrack->url); - $this->assertSame('test-token-123', $config->destinations->youTrack->token); + $this->assertSame('/tmp/sp_sync', $config->sources->getSuperProductivity()->syncFilePath); + $this->assertSame('/tmp/plain.json', $config->sources->getPlainJson()->filePath); + $this->assertSame('http://localhost:8080', $config->destinations->getYouTrack()->url); + $this->assertSame('test-token-123', $config->destinations->getYouTrack()->token); } - public function testThrowsExceptionWhenDestinationNotConfigured(): void + public function testCreatesConfigWithNullDestinationWhenNotConfigured(): void { $configPath = $this->tempDir . '/config.yaml'; file_put_contents($configPath, <<expectException(ConfigException::class); - $this->expectExceptionMessage('Destination youTrack is not configured'); - $provider = new YamlConfigProvider($configPath); - $provider->getConfig(); + $config = $provider->getConfig(); + + $this->assertSame(DestinationType::YouTrack, $config->destination); } public function testCachesConfigOnSubsequentCalls(): void @@ -401,9 +397,9 @@ public function testParsesConfigWithOnlySuperProductivitySource(): void $provider = new YamlConfigProvider($configPath); $config = $provider->getConfig(); - $this->assertNotNull($config->sources->superProductivity); - $this->assertNull($config->sources->plainJson); - $this->assertSame('/tmp/sync', $config->sources->superProductivity->syncFilePath); + $this->assertSame('/tmp/sync', $config->sources->getSuperProductivity()->syncFilePath); + $this->expectException(ConfigException::class); + $config->sources->getPlainJson(); } public function testParsesConfigWithOnlyPlainJsonSource(): void @@ -424,8 +420,8 @@ public function testParsesConfigWithOnlyPlainJsonSource(): void $provider = new YamlConfigProvider($configPath); $config = $provider->getConfig(); - $this->assertNull($config->sources->superProductivity); - $this->assertNotNull($config->sources->plainJson); - $this->assertSame('/tmp/data.json', $config->sources->plainJson->filePath); + $this->assertSame('/tmp/data.json', $config->sources->getPlainJson()->filePath); + $this->expectException(ConfigException::class); + $config->sources->getSuperProductivity(); } }