diff --git a/src/Cli/InitCommand.php b/src/Cli/InitCommand.php index 3b57628..4cecc1f 100644 --- a/src/Cli/InitCommand.php +++ b/src/Cli/InitCommand.php @@ -4,6 +4,7 @@ namespace Igancev\WorkReporter\Cli; +use Igancev\WorkReporter\Platform\HomeDirectory; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -55,7 +56,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); - $home = (string) getenv('HOME'); + $home = HomeDirectory::resolve(); $configDir = str_replace('~', $home, self::DEFAULT_CONFIG_DIR); $configPath = str_replace('~', $home, self::DEFAULT_CONFIG_PATH); diff --git a/src/Config/YamlConfigProvider.php b/src/Config/YamlConfigProvider.php index 543b2be..1cef4c3 100644 --- a/src/Config/YamlConfigProvider.php +++ b/src/Config/YamlConfigProvider.php @@ -8,6 +8,7 @@ use Igancev\WorkReporter\Config\SourceConfig\SourcesConfig; use Igancev\WorkReporter\Config\SourceConfig\SuperProductivityConfig; use Igancev\WorkReporter\Destination\DestinationType; +use Igancev\WorkReporter\Platform\HomeDirectory; use Igancev\WorkReporter\Source\SourceType; use Symfony\Component\Yaml\Exception\ParseException; use Symfony\Component\Yaml\Yaml; @@ -28,7 +29,7 @@ public function getConfig(): Config return $this->config; } - $path = str_replace("~", (string)getenv("HOME"), $this->configPath); + $path = str_replace("~", HomeDirectory::resolve(), $this->configPath); if (!file_exists($path)) { throw new ConfigException(sprintf( diff --git a/src/Platform/HomeDirectory.php b/src/Platform/HomeDirectory.php new file mode 100644 index 0000000..04f42fd --- /dev/null +++ b/src/Platform/HomeDirectory.php @@ -0,0 +1,37 @@ +originalHome = getenv('HOME'); + $this->originalUserProfile = getenv('USERPROFILE'); + $this->originalHomeDrive = getenv('HOMEDRIVE'); + $this->originalHomePath = getenv('HOMEPATH'); + } + + protected function tearDown(): void + { + $this->restoreEnv('HOME', $this->originalHome); + $this->restoreEnv('USERPROFILE', $this->originalUserProfile); + $this->restoreEnv('HOMEDRIVE', $this->originalHomeDrive); + $this->restoreEnv('HOMEPATH', $this->originalHomePath); + } + + public function testResolveReturnsHomeOnPosix(): void + { + if (PHP_OS_FAMILY === 'Windows') { + self::markTestSkipped('POSIX-only path; Windows uses USERPROFILE.'); + } + + // Arrange + putenv('HOME=/home/posix-user'); + + // Act + $home = HomeDirectory::resolve(); + + // Assert + self::assertSame('/home/posix-user', $home); + } + + public function testResolvePrefersUserProfileOnWindows(): void + { + if (PHP_OS_FAMILY !== 'Windows') { + self::markTestSkipped('Windows-only path; POSIX uses HOME.'); + } + + // Arrange + putenv('USERPROFILE=C:\\Users\\winuser'); + putenv('HOMEDRIVE=Z:'); + putenv('HOMEPATH=\\Z-path'); + + // Act + $home = HomeDirectory::resolve(); + + // Assert + self::assertSame('C:\\Users\\winuser', $home); + } + + public function testResolveFallsBackToHomeDrivePathOnWindowsWhenUserProfileEmpty(): void + { + if (PHP_OS_FAMILY !== 'Windows') { + self::markTestSkipped('Windows-only fallback path.'); + } + + // Arrange + putenv('USERPROFILE'); + putenv('HOMEDRIVE=C:'); + putenv('HOMEPATH=\\Users\\fallback'); + + // Act + $home = HomeDirectory::resolve(); + + // Assert + self::assertSame('C:\\Users\\fallback', $home); + } + + public function testResolveReturnsEmptyStringWhenEnvUnset(): void + { + // Arrange + if (PHP_OS_FAMILY === 'Windows') { + putenv('USERPROFILE'); + putenv('HOMEDRIVE'); + putenv('HOMEPATH'); + } else { + putenv('HOME'); + } + + // Act + $home = HomeDirectory::resolve(); + + // Assert + self::assertSame('', $home); + } + + private function restoreEnv(string $name, string|false $original): void + { + if ($original === false) { + putenv($name); + return; + } + putenv($name . '=' . $original); + } +}