CommandTest.php 11 KB

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