1919namespace FastForward \DevTools \Tests \Console \Command ;
2020
2121use FastForward \DevTools \Console \Command \DependenciesCommand ;
22+ use FastForward \DevTools \Process \ProcessBuilderInterface ;
23+ use FastForward \DevTools \Process \ProcessQueueInterface ;
2224use PHPUnit \Framework \Attributes \CoversClass ;
2325use PHPUnit \Framework \Attributes \Test ;
26+ use PHPUnit \Framework \TestCase ;
2427use Prophecy \Argument ;
28+ use Prophecy \PhpUnit \ProphecyTrait ;
29+ use Prophecy \Prophecy \ObjectProphecy ;
30+ use ReflectionMethod ;
31+ use Symfony \Component \Config \FileLocatorInterface ;
32+ use Symfony \Component \Console \Input \InputInterface ;
2533use Symfony \Component \Console \Output \OutputInterface ;
2634use Symfony \Component \Process \Process ;
2735
28- use function Safe \getcwd ;
29- use function str_contains ;
30-
3136#[CoversClass(DependenciesCommand::class)]
32- final class DependenciesCommandTest extends AbstractCommandTestCase
37+ final class DependenciesCommandTest extends TestCase
3338{
34- /**
35- * @return string
36- */
37- protected function getCommandClass (): string
38- {
39- return DependenciesCommand::class;
40- }
39+ use ProphecyTrait;
4140
42- /**
43- * @return string
44- */
45- protected function getCommandName (): string
46- {
47- return 'dependencies ' ;
48- }
41+ private ObjectProphecy $ fileLocator ;
4942
50- /**
51- * @return string
52- */
53- protected function getCommandDescription (): string
54- {
55- return 'Analyzes missing and unused Composer dependencies. ' ;
56- }
43+ private ObjectProphecy $ processBuilder ;
44+
45+ private ObjectProphecy $ processQueue ;
46+
47+ private ObjectProphecy $ input ;
48+
49+ private ObjectProphecy $ output ;
50+
51+ private ObjectProphecy $ processUnused ;
52+
53+ private ObjectProphecy $ processDepAnalyser ;
54+
55+ private DependenciesCommand $ command ;
5756
5857 /**
59- * @return string
58+ * @return void
6059 */
61- protected function getCommandHelp (): string
60+ protected function setUp (): void
6261 {
63- return 'This command runs composer-dependency-analyser and composer-unused to report missing and unused Composer dependencies. ' ;
62+ $ this ->fileLocator = $ this ->prophesize (FileLocatorInterface::class);
63+ $ this ->processBuilder = $ this ->prophesize (ProcessBuilderInterface::class);
64+ $ this ->processQueue = $ this ->prophesize (ProcessQueueInterface::class);
65+ $ this ->input = $ this ->prophesize (InputInterface::class);
66+ $ this ->output = $ this ->prophesize (OutputInterface::class);
67+ $ this ->processUnused = $ this ->prophesize (Process::class);
68+ $ this ->processDepAnalyser = $ this ->prophesize (Process::class);
69+
70+ $ this ->processBuilder ->withArgument (Argument::any ())
71+ ->willReturn ($ this ->processBuilder ->reveal ());
72+ $ this ->processBuilder ->withArgument (Argument::any (), Argument::any ())
73+ ->willReturn ($ this ->processBuilder ->reveal ());
74+
75+ $ this ->processBuilder ->build (Argument::any ())
76+ ->willReturn ($ this ->processUnused ->reveal ());
77+
78+ $ this ->command = new DependenciesCommand (
79+ $ this ->processBuilder ->reveal (),
80+ $ this ->processQueue ->reveal (),
81+ $ this ->fileLocator ->reveal (),
82+ );
6483 }
6584
6685 /**
6786 * @return void
6887 */
69- protected function setUp (): void
88+ #[Test]
89+ public function commandWillSetExpectedNameDescriptionAndHelp (): void
7090 {
71- parent ::setUp ();
72-
73- $ this ->output ->writeln (Argument::type ('string ' ));
74-
75- $ cwd = getcwd ();
76- $ this ->filesystem ->exists ($ cwd . '/composer.json ' )->willReturn (true );
91+ self ::assertSame ('dependencies ' , $ this ->command ->getName ());
92+ self ::assertSame (
93+ 'Analyzes missing and unused Composer dependencies. ' ,
94+ $ this ->command ->getDescription ()
95+ );
96+ self ::assertSame (
97+ 'This command runs composer-dependency-analyser and composer-unused to report missing and unused Composer dependencies. ' ,
98+ $ this ->command ->getHelp ()
99+ );
77100 }
78101
79102 /**
80103 * @return void
81104 */
82105 #[Test]
83- public function executeWillReturnSuccessWhenBothToolsSucceed (): void
106+ public function commandCanBeConstructedWithDependencies (): void
84107 {
85- $ processUnused = $ this ->prophesize (Process::class);
86- $ processUnused ->isSuccessful ()
87- ->willReturn (true );
88-
89- $ processDepAnalyser = $ this ->prophesize (Process::class);
90- $ processDepAnalyser ->isSuccessful ()
91- ->willReturn (true );
92-
93- $ this ->processHelper
94- ->run (Argument::type (OutputInterface::class), Argument::that (
95- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-unused ' )
96- ), Argument::cetera ())
97- ->willReturn ($ processUnused ->reveal ())
98- ->shouldBeCalled ();
99-
100- $ this ->processHelper
101- ->run (Argument::type (OutputInterface::class), Argument::that (
102- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-dependency-analyser ' )
103- ), Argument::cetera ())
104- ->willReturn ($ processDepAnalyser ->reveal ())
105- ->shouldBeCalled ();
106-
107- $ this ->output ->writeln ('<info>Running dependency analysis...</info> ' )
108- ->shouldBeCalled ();
109-
110- self ::assertSame (DependenciesCommand::SUCCESS , $ this ->invokeExecute ());
108+ self ::assertInstanceOf (DependenciesCommand::class, $ this ->command );
111109 }
112110
113111 /**
114112 * @return void
115113 */
116114 #[Test]
117- public function executeWillReturnFailureWhenFirstToolFails (): void
115+ public function executeWillReturnSuccessWhenBothToolsSucceed (): void
118116 {
119- $ processUnused = $ this ->prophesize (Process::class);
120- $ processUnused ->isSuccessful ()
121- ->willReturn (false );
117+ $ this ->fileLocator ->locate ('composer.json ' )
118+ ->willReturn ('/path/to/composer.json ' );
122119
123- $ processDepAnalyser = $ this ->prophesize (Process::class);
124- $ processDepAnalyser ->isSuccessful ()
125- ->willReturn (true );
126-
127- $ this ->processHelper
128- ->run (Argument::type (OutputInterface::class), Argument::that (
129- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-unused ' )
130- ), Argument::cetera ())
131- ->willReturn ($ processUnused ->reveal ())
120+ $ this ->processBuilder ->build ('vendor/bin/composer-unused ' )
121+ ->willReturn ($ this ->processUnused ->reveal ());
122+ $ this ->processBuilder ->build ('vendor/bin/composer-dependency-analyser ' )
123+ ->willReturn ($ this ->processDepAnalyser ->reveal ());
124+
125+ $ this ->processQueue ->add ($ this ->processUnused ->reveal ())
126+ ->shouldBeCalled ();
127+ $ this ->processQueue ->add ($ this ->processDepAnalyser ->reveal ())
132128 ->shouldBeCalled ();
133129
134- $ this ->processHelper
135- ->run (Argument::type (OutputInterface::class), Argument::that (
136- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-dependency-analyser ' )
137- ), Argument::cetera ())
138- ->willReturn ($ processDepAnalyser ->reveal ())
130+ $ this ->processQueue ->run ($ this ->output ->reveal ())
131+ ->willReturn (ProcessQueueInterface::SUCCESS )
139132 ->shouldBeCalled ();
140133
141134 $ this ->output ->writeln ('<info>Running dependency analysis...</info> ' )
142135 ->shouldBeCalled ();
143136
144- self ::assertSame (DependenciesCommand::FAILURE , $ this ->invokeExecute ());
137+ $ result = $ this ->executeCommand ();
138+
139+ self ::assertSame (DependenciesCommand::SUCCESS , $ result );
145140 }
146141
147142 /**
148143 * @return void
149144 */
150145 #[Test]
151- public function executeWillReturnFailureWhenSecondToolFails (): void
146+ public function executeWillReturnFailureWhenProcessQueueFails (): void
152147 {
153- $ processUnused = $ this ->prophesize (Process::class);
154- $ processUnused ->isSuccessful ()
155- ->willReturn (true );
148+ $ this ->fileLocator ->locate ('composer.json ' )
149+ ->willReturn ('/path/to/composer.json ' );
156150
157- $ processDepAnalyser = $ this ->prophesize (Process::class);
158- $ processDepAnalyser ->isSuccessful ()
159- ->willReturn (false );
160-
161- $ this ->processHelper
162- ->run (Argument::type (OutputInterface::class), Argument::that (
163- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-unused ' )
164- ), Argument::cetera ())
165- ->willReturn ($ processUnused ->reveal ())
151+ $ this ->processBuilder ->build ('vendor/bin/composer-unused ' )
152+ ->willReturn ($ this ->processUnused ->reveal ());
153+ $ this ->processBuilder ->build ('vendor/bin/composer-dependency-analyser ' )
154+ ->willReturn ($ this ->processDepAnalyser ->reveal ());
155+
156+ $ this ->processQueue ->add ($ this ->processUnused ->reveal ())
157+ ->shouldBeCalled ();
158+ $ this ->processQueue ->add ($ this ->processDepAnalyser ->reveal ())
166159 ->shouldBeCalled ();
167160
168- $ this ->processHelper
169- ->run (Argument::type (OutputInterface::class), Argument::that (
170- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-dependency-analyser ' )
171- ), Argument::cetera ())
172- ->willReturn ($ processDepAnalyser ->reveal ())
161+ $ this ->processQueue ->run ($ this ->output ->reveal ())
162+ ->willReturn (ProcessQueueInterface::FAILURE )
173163 ->shouldBeCalled ();
174164
175165 $ this ->output ->writeln ('<info>Running dependency analysis...</info> ' )
176166 ->shouldBeCalled ();
177167
178- self ::assertSame (DependenciesCommand::FAILURE , $ this ->invokeExecute ());
168+ $ result = $ this ->executeCommand ();
169+
170+ self ::assertSame (DependenciesCommand::FAILURE , $ result );
179171 }
180172
181173 /**
@@ -184,43 +176,50 @@ public function executeWillReturnFailureWhenSecondToolFails(): void
184176 #[Test]
185177 public function executeWillCallBothDependencyToolsWithComposerJson (): void
186178 {
187- $ cwd = getcwd ();
188- $ composerJsonPath = $ cwd . '/composer.json ' ;
179+ $ composerJsonPath = '/path/to/composer.json ' ;
189180
190- $ this ->filesystem -> exists ( $ composerJsonPath )
191- ->willReturn (true );
181+ $ this ->fileLocator -> locate ( ' composer.json ' )
182+ ->willReturn ($ composerJsonPath );
192183
193184 $ processUnused = $ this ->prophesize (Process::class);
194- $ processUnused ->isSuccessful ()
195- ->willReturn (true );
196185 $ processUnused ->getCommandLine ()
197186 ->willReturn ('vendor/bin/composer-unused ' . $ composerJsonPath . ' --no-progress ' );
198187
199188 $ processDepAnalyser = $ this ->prophesize (Process::class);
200- $ processDepAnalyser ->isSuccessful ()
201- ->willReturn (true );
202189 $ processDepAnalyser ->getCommandLine ()
203190 ->willReturn (
204191 'vendor/bin/composer-dependency-analyser --composer-json= ' . $ composerJsonPath . ' --ignore-unused-deps --ignore-prod-only-in-dev-deps '
205192 );
206193
207- $ this ->processHelper
208- ->run (Argument::type (OutputInterface::class), Argument::that (
209- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-unused ' )
210- ), Argument::cetera ())
211- ->willReturn ($ processUnused ->reveal ())
194+ $ this ->processBuilder ->build ('vendor/bin/composer-unused ' )
195+ ->willReturn ($ processUnused ->reveal ());
196+ $ this ->processBuilder ->build ('vendor/bin/composer-dependency-analyser ' )
197+ ->willReturn ($ processDepAnalyser ->reveal ());
198+
199+ $ this ->processQueue ->add ($ processUnused ->reveal ())
200+ ->shouldBeCalled ();
201+ $ this ->processQueue ->add ($ processDepAnalyser ->reveal ())
212202 ->shouldBeCalled ();
213203
214- $ this ->processHelper
215- ->run (Argument::type (OutputInterface::class), Argument::that (
216- static fn (Process $ p ): bool => str_contains ($ p ->getCommandLine (), 'composer-dependency-analyser ' )
217- ), Argument::cetera ())
218- ->willReturn ($ processDepAnalyser ->reveal ())
204+ $ this ->processQueue ->run ($ this ->output ->reveal ())
205+ ->willReturn (ProcessQueueInterface::SUCCESS )
219206 ->shouldBeCalled ();
220207
221208 $ this ->output ->writeln ('<info>Running dependency analysis...</info> ' )
222209 ->shouldBeCalled ();
223210
224- self ::assertSame (DependenciesCommand::SUCCESS , $ this ->invokeExecute ());
211+ $ result = $ this ->executeCommand ();
212+
213+ self ::assertSame (DependenciesCommand::SUCCESS , $ result );
214+ }
215+
216+ /**
217+ * @return int
218+ */
219+ private function executeCommand (): int
220+ {
221+ $ reflectionMethod = new ReflectionMethod ($ this ->command , 'execute ' );
222+
223+ return $ reflectionMethod ->invoke ($ this ->command , $ this ->input ->reveal (), $ this ->output ->reveal ());
225224 }
226- }
225+ }
0 commit comments