CommandTest.php 10 KB

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