Skip to content

Commit 8930916

Browse files
committed
wip: Refactor RefactorCommand
1 parent d1e58b6 commit 8930916

2 files changed

Lines changed: 136 additions & 100 deletions

File tree

src/Console/Command/RefactorCommand.php

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818

1919
namespace FastForward\DevTools\Console\Command;
2020

21+
use Composer\Command\BaseCommand;
22+
use FastForward\DevTools\Process\ProcessBuilderInterface;
23+
use FastForward\DevTools\Process\ProcessQueueInterface;
24+
use Symfony\Component\Config\FileLocatorInterface;
2125
use Symfony\Component\Console\Attribute\AsCommand;
2226
use Symfony\Component\Console\Input\InputInterface;
2327
use Symfony\Component\Console\Input\InputOption;
@@ -34,13 +38,28 @@
3438
aliases: ['rector'],
3539
help: 'This command runs Rector to refactor your code.'
3640
)]
37-
final class RefactorCommand extends AbstractCommand
41+
final class RefactorCommand extends BaseCommand
3842
{
3943
/**
4044
* @var string the default Rector configuration file
4145
*/
4246
public const string CONFIG = 'rector.php';
4347

48+
/**
49+
* Creates a new RefactorCommand instance.
50+
*
51+
* @param FileLocatorInterface $fileLocator the file locator
52+
* @param ProcessBuilderInterface $processBuilder the process builder
53+
* @param ProcessQueueInterface $processQueue the process queue
54+
*/
55+
public function __construct(
56+
private readonly FileLocatorInterface $fileLocator,
57+
private readonly ProcessBuilderInterface $processBuilder,
58+
private readonly ProcessQueueInterface $processQueue,
59+
) {
60+
parent::__construct();
61+
}
62+
4463
/**
4564
* Configures the refactor command options and description.
4665
*
@@ -57,6 +76,13 @@ protected function configure(): void
5776
shortcut: 'f',
5877
mode: InputOption::VALUE_NONE,
5978
description: 'Automatically fix code refactoring issues.'
79+
)
80+
->addOption(
81+
name: 'config',
82+
shortcut: 'c',
83+
mode: InputOption::VALUE_OPTIONAL,
84+
description: 'The path to the Rector configuration file.',
85+
default: self::CONFIG
6086
);
6187
}
6288

@@ -75,14 +101,19 @@ protected function execute(InputInterface $input, OutputInterface $output): int
75101
{
76102
$output->writeln('<info>Running Rector for code refactoring...</info>');
77103

78-
$command = new Process([
79-
$this->getAbsolutePath('vendor/bin/rector'),
80-
'process',
81-
'--config',
82-
parent::getConfigFile(self::CONFIG),
83-
$input->getOption('fix') ? null : '--dry-run',
84-
]);
104+
$processBuilder = $this->processBuilder
105+
->withArgument('process')
106+
->withArgument('--config')
107+
->withArgument($this->fileLocator->locate(self::CONFIG));
108+
109+
if (! $input->getOption('fix')) {
110+
$processBuilder = $processBuilder->withArgument('--dry-run');
111+
}
112+
113+
$this->processQueue->add(
114+
$processBuilder->build('vendor/bin/rector')
115+
);
85116

86-
return parent::runProcess($command, $output);
117+
return $this->processQueue->run($output);
87118
}
88119
}

tests/Console/Command/RefactorCommandTest.php

Lines changed: 96 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -19,138 +19,143 @@
1919
namespace FastForward\DevTools\Tests\Console\Command;
2020

2121
use FastForward\DevTools\Console\Command\RefactorCommand;
22+
use FastForward\DevTools\Process\ProcessBuilderInterface;
23+
use FastForward\DevTools\Process\ProcessQueueInterface;
2224
use PHPUnit\Framework\Attributes\CoversClass;
2325
use PHPUnit\Framework\Attributes\Test;
26+
use PHPUnit\Framework\TestCase;
27+
use Prophecy\Argument;
2428
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;
33+
use Symfony\Component\Console\Output\OutputInterface;
2534
use Symfony\Component\Process\Process;
2635

27-
use function Safe\getcwd;
28-
2936
#[CoversClass(RefactorCommand::class)]
30-
final class RefactorCommandTest extends AbstractCommandTestCase
37+
final class RefactorCommandTest extends TestCase
3138
{
3239
use ProphecyTrait;
3340

34-
/**
35-
* @return string
36-
*/
37-
protected function getCommandClass(): string
38-
{
39-
return RefactorCommand::class;
40-
}
41+
/** @var ObjectProphecy<FileLocatorInterface> */
42+
private ObjectProphecy $fileLocator;
4143

42-
/**
43-
* @return string
44-
*/
45-
protected function getCommandName(): string
46-
{
47-
return 'refactor';
48-
}
44+
/** @var ObjectProphecy<ProcessBuilderInterface> */
45+
private ObjectProphecy $processBuilder;
4946

50-
/**
51-
* @return string
52-
*/
53-
protected function getCommandDescription(): string
54-
{
55-
return 'Runs Rector for code refactoring.';
56-
}
47+
/** @var ObjectProphecy<ProcessQueueInterface> */
48+
private ObjectProphecy $processQueue;
5749

58-
/**
59-
* @return string
60-
*/
61-
protected function getCommandHelp(): string
62-
{
63-
return 'This command runs Rector to refactor your code.';
64-
}
50+
/** @var ObjectProphecy<InputInterface> */
51+
private ObjectProphecy $input;
52+
53+
/** @var ObjectProphecy<OutputInterface> */
54+
private ObjectProphecy $output;
55+
56+
/** @var ObjectProphecy<Process> */
57+
private ObjectProphecy $process;
58+
59+
private RefactorCommand $command;
60+
61+
private const string CONFIG_PATH = '/path/to/rector.php';
6562

66-
/**
67-
* @return void
68-
*/
6963
protected function setUp(): void
7064
{
71-
parent::setUp();
65+
$this->fileLocator = $this->prophesize(FileLocatorInterface::class);
66+
$this->processBuilder = $this->prophesize(ProcessBuilderInterface::class);
67+
$this->processQueue = $this->prophesize(ProcessQueueInterface::class);
68+
$this->input = $this->prophesize(InputInterface::class);
69+
$this->output = $this->prophesize(OutputInterface::class);
70+
$this->process = $this->prophesize(Process::class);
71+
72+
$this->fileLocator->locate(RefactorCommand::CONFIG)
73+
->willReturn(self::CONFIG_PATH);
74+
75+
$this->input->getOption('fix')->willReturn(false);
7276

73-
$this->withConfigFile(RefactorCommand::CONFIG);
77+
$this->processBuilder->withArgument(Argument::cetera())
78+
->willReturn($this->processBuilder->reveal());
79+
80+
$this->processBuilder->build('vendor/bin/rector')
81+
->willReturn($this->process->reveal());
82+
83+
$this->processQueue->run($this->output->reveal())
84+
->willReturn(RefactorCommand::SUCCESS);
85+
86+
$this->command = new RefactorCommand(
87+
$this->fileLocator->reveal(),
88+
$this->processBuilder->reveal(),
89+
$this->processQueue->reveal()
90+
);
7491
}
7592

76-
/**
77-
* @return void
78-
*/
7993
#[Test]
80-
public function executeWithLocalConfigWillRunRectorProcessWithDevToolsConfigFile(): void
94+
public function commandWillSetExpectedNameDescriptionAndHelp(): void
8195
{
82-
$this->withConfigFile(RefactorCommand::CONFIG, true);
83-
84-
$this->willRunProcessWithCallback(function (Process $process): bool {
85-
$commandLine = $process->getCommandLine();
86-
87-
$path = getcwd() . '/' . RefactorCommand::CONFIG;
96+
self::assertSame('refactor', $this->command->getName());
97+
self::assertSame('Runs Rector for code refactoring.', $this->command->getDescription());
98+
self::assertSame('This command runs Rector to refactor your code.', $this->command->getHelp());
99+
self::assertSame(['rector'], $this->command->getAliases());
100+
}
88101

89-
return str_contains($commandLine, 'vendor/bin/rector')
90-
&& str_contains($commandLine, 'process')
91-
&& str_contains($commandLine, '--config')
92-
&& str_contains($commandLine, $path)
93-
&& str_contains($commandLine, '--dry-run');
94-
});
102+
#[Test]
103+
public function commandWillHaveExpectedOptions(): void
104+
{
105+
$definition = $this->command->getDefinition();
95106

96-
$this->invokeExecute();
107+
self::assertTrue($definition->hasOption('fix'));
108+
self::assertTrue($definition->hasOption('config'));
97109
}
98110

99-
/**
100-
* @return void
101-
*/
102111
#[Test]
103-
public function executeWithoutLocalConfigWillRunRectorProcessWithDevToolsConfigFile(): void
112+
public function executeWillRunRectorProcessWithDryRunWhenFixIsFalse(): void
104113
{
105-
$this->withConfigFile(RefactorCommand::CONFIG);
114+
$this->processBuilder->withArgument('process')
115+
->shouldBeCalledOnce()
116+
->willReturn($this->processBuilder->reveal());
106117

107-
$this->willRunProcessWithCallback(function (Process $process): bool {
108-
$commandLine = $process->getCommandLine();
109-
$path = getcwd() . '/' . RefactorCommand::CONFIG;
118+
$this->processBuilder->withArgument('--config')
119+
->shouldBeCalledOnce()
120+
->willReturn($this->processBuilder->reveal());
110121

111-
return str_contains($commandLine, 'vendor/bin/rector')
112-
&& str_contains($commandLine, 'process')
113-
&& str_contains($commandLine, '--config')
114-
&& str_contains($commandLine, $path)
115-
&& str_contains($commandLine, '--dry-run');
116-
});
122+
$this->processBuilder->withArgument(self::CONFIG_PATH)
123+
->shouldBeCalledOnce()
124+
->willReturn($this->processBuilder->reveal());
117125

118-
$this->invokeExecute();
126+
$this->processBuilder->withArgument('--dry-run')
127+
->shouldBeCalledOnce()
128+
->willReturn($this->processBuilder->reveal());
129+
130+
$this->processQueue->add($this->process->reveal())
131+
->shouldBeCalledOnce();
132+
133+
$result = $this->executeCommand();
134+
135+
self::assertSame(RefactorCommand::SUCCESS, $result);
119136
}
120137

121-
/**
122-
* @return void
123-
*/
124138
#[Test]
125-
public function executeWithFixOptionWillRunRectorProcessWithoutDryRunOption(): void
139+
public function executeWillRunRectorProcessWithoutDryRunWhenFixIsTrue(): void
126140
{
127141
$this->input->getOption('fix')
128-
->willReturn(true)
129-
->shouldBeCalledOnce();
142+
->willReturn(true);
130143

131-
$this->withConfigFile(RefactorCommand::CONFIG, true);
144+
$this->processBuilder->withArgument('--dry-run')
145+
->shouldNotBeCalled();
132146

133-
$this->willRunProcessWithCallback(function (Process $process): bool {
134-
$commandLine = $process->getCommandLine();
147+
$this->processQueue->add($this->process->reveal())
148+
->shouldBeCalledOnce();
135149

136-
return str_contains($commandLine, 'vendor/bin/rector')
137-
&& str_contains($commandLine, 'process')
138-
&& str_contains($commandLine, '--config')
139-
&& str_contains($commandLine, getcwd() . '/' . RefactorCommand::CONFIG)
140-
&& ! str_contains($commandLine, '--dry-run');
141-
});
150+
$result = $this->executeCommand();
142151

143-
$this->invokeExecute();
152+
self::assertSame(RefactorCommand::SUCCESS, $result);
144153
}
145154

146-
/**
147-
* @return void
148-
*/
149-
#[Test]
150-
public function executeWillReturnFailureIfProcessFails(): void
155+
private function executeCommand(): int
151156
{
152-
$this->willRunProcessWithCallback(static fn(): true => true, false);
157+
$reflectionMethod = new ReflectionMethod($this->command, 'execute');
153158

154-
self::assertSame(RefactorCommand::FAILURE, $this->invokeExecute());
159+
return $reflectionMethod->invoke($this->command, $this->input->reveal(), $this->output->reveal());
155160
}
156161
}

0 commit comments

Comments
 (0)