CommandTest.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP Project
  13. * @since 3.6.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Console;
  17. use AssertionError;
  18. use Cake\Command\Command;
  19. use Cake\Console\CommandInterface;
  20. use Cake\Console\ConsoleIo;
  21. use Cake\Console\ConsoleOptionParser;
  22. use Cake\Console\Exception\StopException;
  23. use Cake\Console\TestSuite\StubConsoleOutput;
  24. use Cake\ORM\Locator\TableLocator;
  25. use Cake\ORM\Table;
  26. use Cake\TestSuite\TestCase;
  27. use Mockery;
  28. use TestApp\Command\AbortCommand;
  29. use TestApp\Command\AutoLoadModelCommand;
  30. use TestApp\Command\DemoCommand;
  31. use TestApp\Command\NonInteractiveCommand;
  32. /**
  33. * Test case for Console\Command
  34. */
  35. class CommandTest extends TestCase
  36. {
  37. /**
  38. * test orm locator is setup
  39. */
  40. public function testConstructorSetsLocator(): void
  41. {
  42. $command = new Command();
  43. $result = $command->getTableLocator();
  44. $this->assertInstanceOf(TableLocator::class, $result);
  45. }
  46. /**
  47. * test loadModel is configured properly
  48. */
  49. public function testConstructorAutoLoadModel(): void
  50. {
  51. // No deprecation as AutoLoadModelCommand class defines Posts property
  52. $command = new AutoLoadModelCommand();
  53. $this->assertInstanceOf(Table::class, $command->fetchTable());
  54. }
  55. /**
  56. * Test name
  57. */
  58. public function testSetName(): void
  59. {
  60. $command = new Command();
  61. $this->assertSame($command, $command->setName('routes show'));
  62. $this->assertSame('routes show', $command->getName());
  63. $this->assertSame('routes', $command->getRootName());
  64. }
  65. /**
  66. * Test invalid name
  67. */
  68. public function testSetNameInvalid(): void
  69. {
  70. $this->expectException(AssertionError::class);
  71. $this->expectExceptionMessage('The name \'routes_show\' is missing a space. Names should look like `cake routes`');
  72. $command = new Command();
  73. $command->setName('routes_show');
  74. }
  75. /**
  76. * Test invalid name
  77. */
  78. public function testSetNameInvalidLeadingSpace(): void
  79. {
  80. $this->expectException(AssertionError::class);
  81. $command = new Command();
  82. $command->setName(' routes_show');
  83. }
  84. /**
  85. * Test option parser fetching
  86. */
  87. public function testGetOptionParser(): void
  88. {
  89. $command = new Command();
  90. $command->setName('cake routes show');
  91. $parser = $command->getOptionParser();
  92. $this->assertInstanceOf(ConsoleOptionParser::class, $parser);
  93. $this->assertSame('routes show', $parser->getCommand());
  94. }
  95. /**
  96. * Test that initialize is called.
  97. */
  98. public function testRunCallsInitialize(): void
  99. {
  100. /** @var \Cake\Console\Command|\PHPUnit\Framework\MockObject\MockObject $command */
  101. $command = $this->getMockBuilder(Command::class)
  102. ->onlyMethods(['initialize'])
  103. ->getMock();
  104. $command->setName('cake example');
  105. $command->expects($this->once())->method('initialize');
  106. $command->run([], $this->getMockIo(new StubConsoleOutput()));
  107. }
  108. /**
  109. * Test run() outputs help
  110. */
  111. public function testRunOutputHelp(): void
  112. {
  113. $command = new Command();
  114. $command->setName('cake demo');
  115. $output = new StubConsoleOutput();
  116. $this->assertSame(
  117. CommandInterface::CODE_SUCCESS,
  118. $command->run(['-h'], $this->getMockIo($output))
  119. );
  120. $messages = implode("\n", $output->messages());
  121. $this->assertStringNotContainsString('Demo', $messages);
  122. $this->assertStringContainsString('cake demo [-h]', $messages);
  123. }
  124. /**
  125. * Test run() outputs help
  126. */
  127. public function testRunOutputHelpLongOption(): void
  128. {
  129. $command = new Command();
  130. $command->setName('cake demo');
  131. $output = new StubConsoleOutput();
  132. $this->assertSame(
  133. CommandInterface::CODE_SUCCESS,
  134. $command->run(['--help'], $this->getMockIo($output))
  135. );
  136. $messages = implode("\n", $output->messages());
  137. $this->assertStringNotContainsString('Demo', $messages);
  138. $this->assertStringContainsString('cake demo [-h]', $messages);
  139. }
  140. /**
  141. * Test run() sets output level
  142. */
  143. public function testRunVerboseOption(): void
  144. {
  145. $command = new DemoCommand();
  146. $command->setName('cake demo');
  147. $output = new StubConsoleOutput();
  148. $this->assertNull($command->run(['--verbose'], $this->getMockIo($output)));
  149. $messages = implode("\n", $output->messages());
  150. $this->assertStringContainsString('Verbose!', $messages);
  151. $this->assertStringContainsString('Demo Command!', $messages);
  152. $this->assertStringContainsString('Quiet!', $messages);
  153. $this->assertStringNotContainsString('cake demo [-h]', $messages);
  154. }
  155. /**
  156. * Test run() sets output level
  157. */
  158. public function testRunQuietOption(): void
  159. {
  160. $command = new DemoCommand();
  161. $command->setName('cake demo');
  162. $output = new StubConsoleOutput();
  163. $this->assertNull($command->run(['--quiet'], $this->getMockIo($output)));
  164. $messages = implode("\n", $output->messages());
  165. $this->assertStringContainsString('Quiet!', $messages);
  166. $this->assertStringNotContainsString('Verbose!', $messages);
  167. $this->assertStringNotContainsString('Demo Command!', $messages);
  168. }
  169. /**
  170. * Test run() sets option parser failure
  171. */
  172. public function testRunOptionParserFailure(): void
  173. {
  174. /** @var \Cake\Console\Command|\PHPUnit\Framework\MockObject\MockObject $command */
  175. $command = $this->getMockBuilder(Command::class)
  176. ->onlyMethods(['getOptionParser'])
  177. ->getMock();
  178. $parser = new ConsoleOptionParser('cake example');
  179. $parser->addArgument('name', ['required' => true]);
  180. $command->method('getOptionParser')->willReturn($parser);
  181. $output = new StubConsoleOutput();
  182. $result = $command->run([], $this->getMockIo($output));
  183. $this->assertSame(CommandInterface::CODE_ERROR, $result);
  184. $messages = implode("\n", $output->messages());
  185. $this->assertStringContainsString(
  186. 'Error: Missing required argument. The `name` argument is required',
  187. $messages
  188. );
  189. }
  190. /**
  191. * Test abort()
  192. */
  193. public function testAbort(): void
  194. {
  195. $this->expectException(StopException::class);
  196. $this->expectExceptionCode(1);
  197. $command = new Command();
  198. $command->abort();
  199. }
  200. /**
  201. * Test abort()
  202. */
  203. public function testAbortCustomCode(): void
  204. {
  205. $this->expectException(StopException::class);
  206. $this->expectExceptionCode(99);
  207. $command = new Command();
  208. $command->abort(99);
  209. }
  210. /**
  211. * test executeCommand with a string class
  212. */
  213. public function testExecuteCommandString(): void
  214. {
  215. $output = new StubConsoleOutput();
  216. $command = new Command();
  217. $result = $command->executeCommand(DemoCommand::class, [], $this->getMockIo($output));
  218. $this->assertNull($result);
  219. $this->assertEquals(['Quiet!', 'Demo Command!'], $output->messages());
  220. }
  221. /**
  222. * test executeCommand with arguments
  223. */
  224. public function testExecuteCommandArguments(): void
  225. {
  226. $output = new StubConsoleOutput();
  227. $command = new Command();
  228. $command->executeCommand(DemoCommand::class, ['Jane'], $this->getMockIo($output));
  229. $this->assertEquals(['Quiet!', 'Demo Command!', 'Jane'], $output->messages());
  230. }
  231. /**
  232. * test executeCommand with arguments
  233. */
  234. public function testExecuteCommandArgumentsOptions(): void
  235. {
  236. $output = new StubConsoleOutput();
  237. $command = new Command();
  238. $command->executeCommand(DemoCommand::class, ['--quiet', 'Jane'], $this->getMockIo($output));
  239. $this->assertEquals(['Quiet!'], $output->messages());
  240. }
  241. /**
  242. * test executeCommand with an instance
  243. */
  244. public function testExecuteCommandInstance(): void
  245. {
  246. $output = new StubConsoleOutput();
  247. $command = new Command();
  248. $result = $command->executeCommand(new DemoCommand(), [], $this->getMockIo($output));
  249. $this->assertNull($result);
  250. $this->assertEquals(['Quiet!', 'Demo Command!'], $output->messages());
  251. }
  252. /**
  253. * test executeCommand with an abort
  254. */
  255. public function testExecuteCommandAbort(): void
  256. {
  257. $output = new StubConsoleOutput();
  258. $command = new Command();
  259. $result = $command->executeCommand(AbortCommand::class, [], $this->getMockIo($output));
  260. $this->assertSame(127, $result);
  261. $this->assertEquals(['<error>Command aborted</error>'], $output->messages());
  262. }
  263. /**
  264. * Test that noninteractive commands use defaults where applicable.
  265. */
  266. public function testExecuteCommandNonInteractive(): void
  267. {
  268. $output = new StubConsoleOutput();
  269. $command = new Command();
  270. $command->executeCommand(NonInteractiveCommand::class, ['--quiet'], $this->getMockIo($output));
  271. $this->assertEquals(['Result: Default!'], $output->messages());
  272. }
  273. /**
  274. * @param \Cake\Console\ConsoleOutput $output
  275. * @return \Cake\Console\ConsoleIo|\Mockery\MockInterface
  276. */
  277. protected function getMockIo($output)
  278. {
  279. return Mockery::mock(ConsoleIo::class, [$output, $output, null, null])->makePartial();
  280. }
  281. }