diff --git a/src/Console/Commands/InstallCommand.php b/src/Console/Commands/InstallCommand.php index 69ff996..4287ec3 100644 --- a/src/Console/Commands/InstallCommand.php +++ b/src/Console/Commands/InstallCommand.php @@ -122,7 +122,7 @@ protected function runStack(): void protected function promptForModules(): void { - if ($this->option('all-modules') || $this->option('modules') || $this->option('dev') || $this->isCI()) { + if ($this->option('all-modules') || $this->option('modules') !== null || $this->option('dev') || $this->isCI()) { return; } @@ -410,9 +410,8 @@ protected function resolveModuleSelection(array $available): array return collect($available) ->filter(function (string $package) use ($requested) { - $name = strtolower(Str::after($package, '/')); - - return $requested->contains($name); + return $requested->contains(strtolower($package)) + || $requested->contains(strtolower(Str::after($package, '/'))); }) ->values() ->all(); diff --git a/src/Environments/DockerEnvironment.php b/src/Environments/DockerEnvironment.php index 3a2b827..7c67042 100644 --- a/src/Environments/DockerEnvironment.php +++ b/src/Environments/DockerEnvironment.php @@ -186,9 +186,10 @@ public function buildContainerArgs(InstallCommand $command): array $args[] = $stack; } - $modules = $command->getSelectedModules(); - if (! empty($modules)) { - $args[] = '--modules='.implode(',', $modules); + if ($modules = $command->option('modules')) { + $args[] = '--modules='.$modules; + } else { + $args[] = '--modules='.implode(',', $command->getSelectedModules()); } foreach (['fresh', 'dev', 'all-modules'] as $opt) { diff --git a/tests/Feature/Environments/DockerEnvironmentTest.php b/tests/Feature/Environments/DockerEnvironmentTest.php index 31e0a86..41598b4 100644 --- a/tests/Feature/Environments/DockerEnvironmentTest.php +++ b/tests/Feature/Environments/DockerEnvironmentTest.php @@ -75,12 +75,11 @@ public function test_forwards_selected_modules_as_comma_list(): void $this->assertContains('--modules=saucebase/auth,saucebase/billing', $args); } - public function test_omits_modules_arg_when_none_selected(): void + public function test_includes_empty_modules_arg_when_none_selected(): void { $args = $this->buildArgs(modules: []); - $matched = array_filter($args, fn (string $a) => str_starts_with($a, '--modules=')); - $this->assertEmpty($matched); + $this->assertContains('--modules=', $args); } public function test_forwards_fresh_flag(): void @@ -111,6 +110,21 @@ public function test_forwards_all_modules_flag(): void $this->assertContains('--all-modules', $args); } + public function test_forwards_modules_option_when_set_directly(): void + { + $args = $this->buildArgs(modules: [], options: ['modules' => 'saucebase/auth,saucebase/billing']); + + $this->assertContains('--modules=saucebase/auth,saucebase/billing', $args); + } + + public function test_always_includes_modules_arg_even_when_none_selected(): void + { + $args = $this->buildArgs(modules: [], options: []); + + $matched = array_filter($args, fn (string $a) => str_starts_with($a, '--modules=')); + $this->assertNotEmpty($matched, '--modules= must always be present so the container skips the interactive prompt'); + } + public function test_multiple_flags_are_all_forwarded(): void { $args = $this->buildArgs( diff --git a/tests/Feature/InstallCommandTest.php b/tests/Feature/InstallCommandTest.php index d45bd00..8860f96 100644 --- a/tests/Feature/InstallCommandTest.php +++ b/tests/Feature/InstallCommandTest.php @@ -520,6 +520,50 @@ public function test_all_modules_without_stack_returns_all(): void $this->assertSame(['saucebase/auth', 'saucebase/react-only'], $result); } + // ------------------------------------------------------------------------- + // resolveModuleSelection — --modules= matching + // ------------------------------------------------------------------------- + + public function test_resolve_matches_full_package_name(): void + { + $cmd = new TestableInstallCommand; + $cmd->fakeOptions = ['modules' => 'saucebase/auth']; + + $result = $cmd->exposedResolveModuleSelection(['saucebase/auth', 'saucebase/billing']); + + $this->assertSame(['saucebase/auth'], $result); + } + + public function test_resolve_matches_short_name(): void + { + $cmd = new TestableInstallCommand; + $cmd->fakeOptions = ['modules' => 'auth']; + + $result = $cmd->exposedResolveModuleSelection(['saucebase/auth', 'saucebase/billing']); + + $this->assertSame(['saucebase/auth'], $result); + } + + public function test_resolve_matches_mixed_case_short_name(): void + { + $cmd = new TestableInstallCommand; + $cmd->fakeOptions = ['modules' => 'Auth']; + + $result = $cmd->exposedResolveModuleSelection(['saucebase/auth', 'saucebase/billing']); + + $this->assertSame(['saucebase/auth'], $result); + } + + public function test_resolve_matches_multiple_full_package_names(): void + { + $cmd = new TestableInstallCommand; + $cmd->fakeOptions = ['modules' => 'saucebase/auth,saucebase/billing']; + + $result = $cmd->exposedResolveModuleSelection(['saucebase/auth', 'saucebase/billing', 'saucebase/settings']); + + $this->assertSame(['saucebase/auth', 'saucebase/billing'], $result); + } + // ------------------------------------------------------------------------- // Helpers // -------------------------------------------------------------------------