CommandTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  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 Cake\Command\Command;
  18. use Cake\Console\ConsoleIo;
  19. use Cake\Console\ConsoleOptionParser;
  20. use Cake\Console\Exception\StopException;
  21. use Cake\Console\TestSuite\StubConsoleOutput;
  22. use Cake\ORM\Locator\TableLocator;
  23. use Cake\ORM\Table;
  24. use Cake\TestSuite\TestCase;
  25. use InvalidArgumentException;
  26. use TestApp\Command\AbortCommand;
  27. use TestApp\Command\AutoLoadModelCommand;
  28. use TestApp\Command\DemoCommand;
  29. use TestApp\Command\NonInteractiveCommand;
  30. /**
  31. * Test case for Console\Command
  32. */
  33. class CommandTest extends TestCase
  34. {
  35. /**
  36. * test orm locator is setup
  37. */
  38. public function testConstructorSetsLocator(): void
  39. {
  40. $command = new Command();
  41. $result = $command->getTableLocator();
  42. $this->assertInstanceOf(TableLocator::class, $result);
  43. }
  44. /**
  45. * test loadModel is configured properly
  46. */
  47. public function testConstructorLoadModelDynamicProperty(): void
  48. {
  49. $this->deprecated(function () {
  50. $command = new Command();
  51. $command->loadModel('Comments');
  52. $this->assertInstanceOf(Table::class, $command->Comments);
  53. });
  54. }
  55. /**
  56. * test loadModel is configured properly
  57. */
  58. public function testConstructorAutoLoadModel(): void
  59. {
  60. // No deprecation as AutoLoadModelCommand class defines Posts property
  61. $command = new AutoLoadModelCommand();
  62. $this->assertInstanceOf(Table::class, $command->Posts);
  63. }
  64. /**
  65. * Test name
  66. */
  67. public function testSetName(): void
  68. {
  69. $command = new Command();
  70. $this->assertSame($command, $command->setName('routes show'));
  71. $this->assertSame('routes show', $command->getName());
  72. $this->assertSame('routes', $command->getRootName());
  73. }
  74. /**
  75. * Test invalid name
  76. */
  77. public function testSetNameInvalid(): void
  78. {
  79. $this->expectException(InvalidArgumentException::class);
  80. $this->expectExceptionMessage('The name \'routes_show\' is missing a space. Names should look like `cake routes`');
  81. $command = new Command();
  82. $command->setName('routes_show');
  83. }
  84. /**
  85. * Test invalid name
  86. */
  87. public function testSetNameInvalidLeadingSpace(): void
  88. {
  89. $this->expectException(InvalidArgumentException::class);
  90. $command = new Command();
  91. $command->setName(' routes_show');
  92. }
  93. /**
  94. * Test option parser fetching
  95. */
  96. public function testGetOptionParser(): void
  97. {
  98. $command = new Command();
  99. $command->setName('cake routes show');
  100. $parser = $command->getOptionParser();
  101. $this->assertInstanceOf(ConsoleOptionParser::class, $parser);
  102. $this->assertSame('routes show', $parser->getCommand());
  103. }
  104. /**
  105. * Test that initialize is called.
  106. */
  107. public function testRunCallsInitialize(): void
  108. {
  109. /** @var \Cake\Console\Command|\PHPUnit\Framework\MockObject\MockObject $command */
  110. $command = $this->getMockBuilder(Command::class)
  111. ->onlyMethods(['initialize'])
  112. ->getMock();
  113. $command->setName('cake example');
  114. $command->expects($this->once())->method('initialize');
  115. $command->run([], $this->getMockIo(new StubConsoleOutput()));
  116. }
  117. /**
  118. * Test run() outputs help
  119. */
  120. public function testRunOutputHelp(): void
  121. {
  122. $command = new Command();
  123. $command->setName('cake demo');
  124. $output = new StubConsoleOutput();
  125. $this->assertSame(
  126. Command::CODE_SUCCESS,
  127. $command->run(['-h'], $this->getMockIo($output))
  128. );
  129. $messages = implode("\n", $output->messages());
  130. $this->assertStringNotContainsString('Demo', $messages);
  131. $this->assertStringContainsString('cake demo [-h]', $messages);
  132. }
  133. /**
  134. * Test run() outputs help
  135. */
  136. public function testRunOutputHelpLongOption(): void
  137. {
  138. $command = new Command();
  139. $command->setName('cake demo');
  140. $output = new StubConsoleOutput();
  141. $this->assertSame(
  142. Command::CODE_SUCCESS,
  143. $command->run(['--help'], $this->getMockIo($output))
  144. );
  145. $messages = implode("\n", $output->messages());
  146. $this->assertStringNotContainsString('Demo', $messages);
  147. $this->assertStringContainsString('cake demo [-h]', $messages);
  148. }
  149. /**
  150. * Test run() sets output level
  151. */
  152. public function testRunVerboseOption(): void
  153. {
  154. $command = new DemoCommand();
  155. $command->setName('cake demo');
  156. $output = new StubConsoleOutput();
  157. $this->assertNull($command->run(['--verbose'], $this->getMockIo($output)));
  158. $messages = implode("\n", $output->messages());
  159. $this->assertStringContainsString('Verbose!', $messages);
  160. $this->assertStringContainsString('Demo Command!', $messages);
  161. $this->assertStringContainsString('Quiet!', $messages);
  162. $this->assertStringNotContainsString('cake demo [-h]', $messages);
  163. }
  164. /**
  165. * Test run() sets output level
  166. */
  167. public function testRunQuietOption(): void
  168. {
  169. $command = new DemoCommand();
  170. $command->setName('cake demo');
  171. $output = new StubConsoleOutput();
  172. $this->assertNull($command->run(['--quiet'], $this->getMockIo($output)));
  173. $messages = implode("\n", $output->messages());
  174. $this->assertStringContainsString('Quiet!', $messages);
  175. $this->assertStringNotContainsString('Verbose!', $messages);
  176. $this->assertStringNotContainsString('Demo Command!', $messages);
  177. }
  178. /**
  179. * Test run() sets option parser failure
  180. */
  181. public function testRunOptionParserFailure(): void
  182. {
  183. /** @var \Cake\Console\Command|\PHPUnit\Framework\MockObject\MockObject $command */
  184. $command = $this->getMockBuilder(Command::class)
  185. ->onlyMethods(['getOptionParser'])
  186. ->getMock();
  187. $parser = new ConsoleOptionParser('cake example');
  188. $parser->addArgument('name', ['required' => true]);
  189. $command->method('getOptionParser')->will($this->returnValue($parser));
  190. $output = new StubConsoleOutput();
  191. $result = $command->run([], $this->getMockIo($output));
  192. $this->assertSame(Command::CODE_ERROR, $result);
  193. $messages = implode("\n", $output->messages());
  194. $this->assertStringContainsString(
  195. 'Error: Missing required argument. The `name` argument is required',
  196. $messages
  197. );
  198. }
  199. /**
  200. * Test abort()
  201. */
  202. public function testAbort(): void
  203. {
  204. $this->expectException(StopException::class);
  205. $this->expectExceptionCode(1);
  206. $command = new Command();
  207. $command->abort();
  208. }
  209. /**
  210. * Test abort()
  211. */
  212. public function testAbortCustomCode(): void
  213. {
  214. $this->expectException(StopException::class);
  215. $this->expectExceptionCode(99);
  216. $command = new Command();
  217. $command->abort(99);
  218. }
  219. /**
  220. * test executeCommand with a string class
  221. */
  222. public function testExecuteCommandString(): void
  223. {
  224. $output = new StubConsoleOutput();
  225. $command = new Command();
  226. $result = $command->executeCommand(DemoCommand::class, [], $this->getMockIo($output));
  227. $this->assertNull($result);
  228. $this->assertEquals(['Quiet!', 'Demo Command!'], $output->messages());
  229. }
  230. /**
  231. * test executeCommand with an invalid string class
  232. */
  233. public function testExecuteCommandStringInvalid(): void
  234. {
  235. $this->expectException(InvalidArgumentException::class);
  236. $this->expectExceptionMessage("Command class 'Nope' does not exist");
  237. $command = new Command();
  238. $command->executeCommand('Nope', [], $this->getMockIo(new StubConsoleOutput()));
  239. }
  240. /**
  241. * test executeCommand with arguments
  242. */
  243. public function testExecuteCommandArguments(): void
  244. {
  245. $output = new StubConsoleOutput();
  246. $command = new Command();
  247. $command->executeCommand(DemoCommand::class, ['Jane'], $this->getMockIo($output));
  248. $this->assertEquals(['Quiet!', 'Demo Command!', 'Jane'], $output->messages());
  249. }
  250. /**
  251. * test executeCommand with arguments
  252. */
  253. public function testExecuteCommandArgumentsOptions(): void
  254. {
  255. $output = new StubConsoleOutput();
  256. $command = new Command();
  257. $command->executeCommand(DemoCommand::class, ['--quiet', 'Jane'], $this->getMockIo($output));
  258. $this->assertEquals(['Quiet!'], $output->messages());
  259. }
  260. /**
  261. * test executeCommand with an instance
  262. */
  263. public function testExecuteCommandInstance(): void
  264. {
  265. $output = new StubConsoleOutput();
  266. $command = new Command();
  267. $result = $command->executeCommand(new DemoCommand(), [], $this->getMockIo($output));
  268. $this->assertNull($result);
  269. $this->assertEquals(['Quiet!', 'Demo Command!'], $output->messages());
  270. }
  271. /**
  272. * test executeCommand with an abort
  273. */
  274. public function testExecuteCommandAbort(): void
  275. {
  276. $output = new StubConsoleOutput();
  277. $command = new Command();
  278. $result = $command->executeCommand(AbortCommand::class, [], $this->getMockIo($output));
  279. $this->assertSame(127, $result);
  280. $this->assertEquals(['<error>Command aborted</error>'], $output->messages());
  281. }
  282. /**
  283. * test executeCommand with an invalid instance
  284. */
  285. public function testExecuteCommandInstanceInvalid(): void
  286. {
  287. $this->expectException(InvalidArgumentException::class);
  288. $this->expectExceptionMessage("Command 'stdClass' is not a subclass");
  289. $command = new Command();
  290. $command->executeCommand(new \stdClass(), [], $this->getMockIo(new StubConsoleOutput()));
  291. }
  292. /**
  293. * Test that noninteractive commands use defaults where applicable.
  294. */
  295. public function testExecuteCommandNonInteractive(): void
  296. {
  297. $output = new StubConsoleOutput();
  298. $command = new Command();
  299. $command->executeCommand(NonInteractiveCommand::class, ['--quiet'], $this->getMockIo($output));
  300. $this->assertEquals(['Result: Default!'], $output->messages());
  301. }
  302. /**
  303. * @param \Cake\Console\ConsoleOutput $output
  304. * @return \Cake\Console\ConsoleIo|\PHPUnit\Framework\MockObject\MockObject
  305. */
  306. protected function getMockIo($output)
  307. {
  308. $io = $this->getMockBuilder(ConsoleIo::class)
  309. ->setConstructorArgs([$output, $output, null, null])
  310. ->addMethods(['in'])
  311. ->getMock();
  312. return $io;
  313. }
  314. }