ConsoleIntegrationTestTraitTest.php 8.3 KB

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