ConsoleIntegrationTestTraitTest.php 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : 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. * @since 3.5.0
  13. * @license https://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Console\TestSuite;
  16. use Cake\Console\CommandInterface;
  17. use Cake\Console\TestSuite\ConsoleIntegrationTestTrait;
  18. use Cake\Console\TestSuite\MissingConsoleInputException;
  19. use Cake\TestSuite\TestCase;
  20. use PHPUnit\Framework\AssertionFailedError;
  21. use PHPUnit\Framework\Attributes\DataProvider;
  22. use stdClass;
  23. class ConsoleIntegrationTestTraitTest extends TestCase
  24. {
  25. use ConsoleIntegrationTestTrait;
  26. /**
  27. * setUp
  28. */
  29. public function setUp(): void
  30. {
  31. parent::setUp();
  32. $this->setAppNamespace();
  33. }
  34. /**
  35. * tests exec when using the command runner
  36. */
  37. public function testExecWithCommandRunner(): void
  38. {
  39. $this->exec('');
  40. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  41. $this->assertOutputContains('Current Paths');
  42. $this->assertExitSuccess();
  43. }
  44. /**
  45. * tests exec
  46. */
  47. public function testExec(): void
  48. {
  49. $this->exec('sample');
  50. $this->assertOutputContains('SampleCommand');
  51. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  52. }
  53. /**
  54. * tests that exec catches a StopException
  55. */
  56. public function testExecCommandWithStopException(): void
  57. {
  58. $this->exec('abort_command');
  59. $this->assertExitCode(127);
  60. $this->assertErrorContains('Command aborted');
  61. }
  62. /**
  63. * tests that exec with a format specifier
  64. */
  65. public function testExecCommandWithFormatSpecifier(): void
  66. {
  67. $this->exec('format_specifier_command');
  68. $this->assertOutputContains('format specifier');
  69. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  70. }
  71. /**
  72. * tests exec clears output
  73. */
  74. public function testExecClearsOutput(): void
  75. {
  76. $this->exec('sample');
  77. $this->assertOutputContains('SampleCommand');
  78. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  79. $this->exec('abort');
  80. $this->assertOutputNotContains('SampleCommand');
  81. $this->assertErrorContains('Command aborted');
  82. $this->assertExitCode(127);
  83. }
  84. /**
  85. * tests a valid core command
  86. */
  87. public function testExecCoreCommand(): void
  88. {
  89. $this->exec('routes');
  90. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  91. }
  92. /**
  93. * tests exec with an arg and an option
  94. */
  95. public function testExecWithArgsAndOption(): void
  96. {
  97. $this->exec('integration arg --opt="some string"');
  98. $this->assertErrorEmpty();
  99. $this->assertOutputContains('arg: arg');
  100. $this->assertOutputContains('opt: some string');
  101. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  102. }
  103. /**
  104. * tests exec with an arg and an option
  105. */
  106. public function testExecWithJsonArg(): void
  107. {
  108. $this->exec("integration '{\"key\":\"value\"}'");
  109. $this->assertErrorEmpty();
  110. $this->assertOutputContains('arg: {"key":"value"}');
  111. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  112. }
  113. /**
  114. * tests exec with missing required argument
  115. */
  116. public function testExecWithMissingRequiredArg(): void
  117. {
  118. $this->exec('integration');
  119. $this->assertErrorContains('Missing required argument');
  120. $this->assertErrorContains('`arg` argument is required');
  121. $this->assertExitCode(CommandInterface::CODE_ERROR);
  122. }
  123. /**
  124. * tests exec with input
  125. */
  126. public function testExecWithInput(): void
  127. {
  128. $this->exec('bridge', ['javascript']);
  129. $this->assertErrorContains('No!');
  130. $this->assertExitCode(CommandInterface::CODE_ERROR);
  131. }
  132. /**
  133. * tests exec with fewer inputs than questions
  134. */
  135. public function testExecWithMissingInput(): void
  136. {
  137. $this->expectException(MissingConsoleInputException::class);
  138. $this->expectExceptionMessage('no more input');
  139. $this->exec('bridge', ['cake']);
  140. }
  141. /**
  142. * tests exec with multiple inputs
  143. */
  144. public function testExecWithMultipleInput(): void
  145. {
  146. $this->exec('bridge', ['cake', 'blue']);
  147. $this->assertOutputContains('You may pass');
  148. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  149. }
  150. public function testExecWithMockServiceDependencies(): void
  151. {
  152. $this->mockService(stdClass::class, function () {
  153. return json_decode('{"console-mock":true}');
  154. });
  155. $this->exec('dependency');
  156. $this->assertOutputContains('constructor inject: {"console-mock":true}');
  157. $this->assertExitCode(CommandInterface::CODE_SUCCESS);
  158. }
  159. /**
  160. * tests assertOutputRegExp assertion
  161. */
  162. public function testAssertOutputRegExp(): void
  163. {
  164. $this->exec('sample');
  165. $this->assertOutputRegExp('/^[A-Z]+/mi');
  166. }
  167. /**
  168. * tests commandStringToArgs
  169. */
  170. public function testCommandStringToArgs(): void
  171. {
  172. $result = $this->commandStringToArgs('command --something=nothing --with-spaces="quote me on that" \'quoted \"arg\"\'');
  173. $expected = [
  174. 'command',
  175. '--something=nothing',
  176. '--with-spaces=quote me on that',
  177. 'quoted \"arg\"',
  178. ];
  179. $this->assertSame($expected, $result);
  180. $json = json_encode(['key' => '"val"', 'this' => true]);
  181. $result = $this->commandStringToArgs(" --json='{$json}'");
  182. $expected = [
  183. '--json=' . $json,
  184. ];
  185. $this->assertSame($expected, $result);
  186. }
  187. /**
  188. * tests failure messages for assertions
  189. *
  190. * @param string $assertion Assertion method
  191. * @param string $message Expected failure message
  192. * @param string $command Command to test
  193. * @param mixed ...$rest
  194. */
  195. #[DataProvider('assertionFailureMessagesProvider')]
  196. public function testAssertionFailureMessages($assertion, $message, $command, ...$rest): void
  197. {
  198. $this->expectException(AssertionFailedError::class);
  199. $this->expectExceptionMessageMatches('#' . $message . '.?#');
  200. $this->exec($command);
  201. call_user_func_array($this->$assertion(...), $rest);
  202. }
  203. /**
  204. * data provider for assertion failure messages
  205. *
  206. * @return array
  207. */
  208. public static function assertionFailureMessagesProvider(): array
  209. {
  210. return [
  211. 'assertExitCode' => ['assertExitCode', 'Failed asserting that `1` matches exit code `0`', 'routes', CommandInterface::CODE_ERROR],
  212. 'assertOutputEmpty' => ['assertOutputEmpty', 'Failed asserting that output is empty', 'routes'],
  213. 'assertOutputContains' => ['assertOutputContains', "Failed asserting that 'missing' is in output", 'routes', 'missing'],
  214. 'assertOutputNotContains' => ['assertOutputNotContains', "Failed asserting that 'controller' is not in output", 'routes', 'controller'],
  215. 'assertOutputRegExp' => ['assertOutputRegExp', 'Failed asserting that `/missing/` PCRE pattern found in output', 'routes', '/missing/'],
  216. 'assertOutputContainsRow' => ['assertOutputContainsRow', 'Failed asserting that `.*` row was in output', 'routes', ['test', 'missing']],
  217. 'assertErrorContains' => ['assertErrorContains', "Failed asserting that 'test' is in error output", 'routes', 'test'],
  218. 'assertErrorRegExp' => ['assertErrorRegExp', 'Failed asserting that `/test/` PCRE pattern found in error output', 'routes', '/test/'],
  219. ];
  220. }
  221. /**
  222. * Test the debugOutput helper
  223. *
  224. * @return void
  225. */
  226. public function testDebugOutput(): void
  227. {
  228. $this->exec('sample');
  229. $temp = tempnam(TMP, 'debug-output');
  230. $f = fopen($temp, 'w');
  231. $this->debugOutput($f);
  232. $f = fopen($temp, 'r');
  233. $result = fread($f, 1024);
  234. $file = __FILE__;
  235. $line = __LINE__ - 5;
  236. $expected = <<<TEXT
  237. $file on $line
  238. ########## debugOutput() ##########
  239. Exit Code
  240. 0
  241. STDOUT
  242. This is the main method called from SampleCommand
  243. STDERR
  244. ###################################
  245. TEXT;
  246. $this->assertEquals($expected, str_replace("\r\n", "\n", $result));
  247. }
  248. }