The clean & modern RSS server that doesn't give you any crap. https://thearsse.com/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TestCLI.php 9.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <?php
  2. /** @license MIT
  3. * Copyright 2017 J. King, Dustin Wilson et al.
  4. * See LICENSE and AUTHORS files for details */
  5. declare(strict_types=1);
  6. namespace JKingWeb\Arsse\TestCase\CLI;
  7. use JKingWeb\Arsse\Arsse;
  8. use JKingWeb\Arsse\Conf;
  9. use JKingWeb\Arsse\User;
  10. use JKingWeb\Arsse\Database;
  11. use JKingWeb\Arsse\Service;
  12. use JKingWeb\Arsse\CLI;
  13. use Phake;
  14. /** @covers \JKingWeb\Arsse\CLI */
  15. class TestCLI extends \JKingWeb\Arsse\Test\AbstractTest {
  16. public function setUp() {
  17. self::clearData(false);
  18. }
  19. public function assertConsole(CLI $cli, string $command, int $exitStatus, string $output = "", bool $pattern = false) {
  20. $argv = \Clue\Arguments\split($command);
  21. $output = strlen($output) ? $output.\PHP_EOL : "";
  22. if ($pattern) {
  23. $this->expectOutputRegex($output);
  24. } else {
  25. $this->expectOutputString($output);
  26. }
  27. $this->assertSame($exitStatus, $cli->dispatch($argv));
  28. }
  29. public function assertLoaded(bool $loaded) {
  30. $r = new \ReflectionClass(Arsse::class);
  31. $props = array_keys($r->getStaticProperties());
  32. foreach ($props as $prop) {
  33. if ($loaded) {
  34. $this->assertNotNull(Arsse::$$prop, "Global $prop object should be loaded");
  35. } else {
  36. $this->assertNull(Arsse::$$prop, "Global $prop object should not be loaded");
  37. }
  38. }
  39. }
  40. public function testPrintVersion() {
  41. $this->assertConsole(new CLI, "arsse.php --version", 0, Arsse::VERSION);
  42. $this->assertLoaded(false);
  43. }
  44. /** @dataProvider provideHelpText */
  45. public function testPrintHelp(string $cmd, string $name) {
  46. $this->assertConsole(new CLI, $cmd, 0, str_replace("arsse.php", $name, CLI::USAGE));
  47. $this->assertLoaded(false);
  48. }
  49. public function provideHelpText() {
  50. return [
  51. ["arsse.php --help", "arsse.php"],
  52. ["arsse --help", "arsse"],
  53. ["thearsse --help", "thearsse"],
  54. ];
  55. }
  56. public function testStartTheDaemon() {
  57. $srv = Phake::mock(Service::class);
  58. $cli = Phake::partialMock(CLI::class);
  59. Phake::when($srv)->watch->thenReturn(new \DateTimeImmutable);
  60. Phake::when($cli)->getService->thenReturn($srv);
  61. $this->assertConsole($cli, "arsse.php daemon", 0);
  62. $this->assertLoaded(true);
  63. Phake::verify($srv)->watch(true);
  64. Phake::verify($cli)->getService;
  65. }
  66. /** @dataProvider provideFeedUpdates */
  67. public function testRefreshAFeed(string $cmd, int $exitStatus, string $output) {
  68. Arsse::$db = Phake::mock(Database::class);
  69. Phake::when(Arsse::$db)->feedUpdate(1, true)->thenReturn(true);
  70. Phake::when(Arsse::$db)->feedUpdate(2, true)->thenThrow(new \JKingWeb\Arsse\Feed\Exception("http://example.com/", new \PicoFeed\Client\InvalidUrlException));
  71. $this->assertConsole(new CLI, $cmd, $exitStatus, $output);
  72. $this->assertLoaded(true);
  73. Phake::verify(Arsse::$db)->feedUpdate;
  74. }
  75. public function provideFeedUpdates() {
  76. return [
  77. ["arsse.php feed refresh 1", 0, ""],
  78. ["arsse.php feed refresh 2", 10502, ""],
  79. ];
  80. }
  81. /** @dataProvider provideDefaultConfigurationSaves */
  82. public function testSaveTheDefaultConfiguration(string $cmd, int $exitStatus, string $file) {
  83. $conf = Phake::mock(Conf::class);
  84. $cli = Phake::partialMock(CLI::class);
  85. Phake::when($conf)->exportFile("php://output", true)->thenReturn(true);
  86. Phake::when($conf)->exportFile("good.conf", true)->thenReturn(true);
  87. Phake::when($conf)->exportFile("bad.conf", true)->thenThrow(new \JKingWeb\Arsse\Conf\Exception("fileUnwritable"));
  88. Phake::when($cli)->getConf->thenReturn($conf);
  89. $this->assertConsole($cli, $cmd, $exitStatus);
  90. $this->assertLoaded(false);
  91. Phake::verify($conf)->exportFile($file, true);
  92. }
  93. public function provideDefaultConfigurationSaves() {
  94. return [
  95. ["arsse.php conf save-defaults", 0, "php://output"],
  96. ["arsse.php conf save-defaults -", 0, "php://output"],
  97. ["arsse.php conf save-defaults good.conf", 0, "good.conf"],
  98. ["arsse.php conf save-defaults bad.conf", 10304, "bad.conf"],
  99. ];
  100. }
  101. /** @dataProvider provideUserList */
  102. public function testListUsers(string $cmd, array $list, int $exitStatus, string $output) {
  103. // Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
  104. Arsse::$user = $this->createMock(User::class);
  105. Arsse::$user->method("list")->willReturn($list);
  106. $this->assertConsole(new CLI, $cmd, $exitStatus, $output);
  107. }
  108. public function provideUserList() {
  109. $list = ["john.doe@example.com", "jane.doe@example.com"];
  110. $str = implode(PHP_EOL, $list);
  111. return [
  112. ["arsse.php user list", $list, 0, $str],
  113. ["arsse.php user", $list, 0, $str],
  114. ["arsse.php user list", [], 0, ""],
  115. ["arsse.php user", [], 0, ""],
  116. ];
  117. }
  118. /** @dataProvider provideUserAdditions */
  119. public function testAddAUser(string $cmd, int $exitStatus, string $output) {
  120. // Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
  121. Arsse::$user = $this->createMock(User::class);
  122. Arsse::$user->method("add")->will($this->returnCallback(function($user, $pass = null) {
  123. switch ($user) {
  124. case "john.doe@example.com":
  125. throw new \JKingWeb\Arsse\User\Exception("alreadyExists");
  126. case "jane.doe@example.com":
  127. return is_null($pass) ? "random password" : $pass;
  128. }
  129. }));
  130. $this->assertConsole(new CLI, $cmd, $exitStatus, $output);
  131. }
  132. public function provideUserAdditions() {
  133. return [
  134. ["arsse.php user add john.doe@example.com", 10403, ""],
  135. ["arsse.php user add jane.doe@example.com", 0, "random password"],
  136. ["arsse.php user add jane.doe@example.com superman", 0, ""],
  137. ];
  138. }
  139. /** @dataProvider provideUserAuthentication */
  140. public function testAuthenticateAUser(string $cmd, int $exitStatus, string $output) {
  141. // Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
  142. Arsse::$user = $this->createMock(User::class);
  143. Arsse::$user->method("auth")->will($this->returnCallback(function($user, $pass) {
  144. return (
  145. ($user == "john.doe@example.com" && $pass == "secret") ||
  146. ($user == "jane.doe@example.com" && $pass == "superman")
  147. );
  148. }));
  149. $this->assertConsole(new CLI, $cmd, $exitStatus, $output);
  150. }
  151. public function provideUserAuthentication() {
  152. $l = new \JKingWeb\Arsse\Lang;
  153. return [
  154. ["arsse.php user auth john.doe@example.com secret", 0, $l("CLI.Auth.Success")],
  155. ["arsse.php user auth john.doe@example.com superman", 1, $l("CLI.Auth.Failure")],
  156. ["arsse.php user auth jane.doe@example.com secret", 1, $l("CLI.Auth.Failure")],
  157. ["arsse.php user auth jane.doe@example.com superman", 0, $l("CLI.Auth.Success")],
  158. ];
  159. }
  160. /** @dataProvider provideUserRemovals */
  161. public function testRemoveAUser(string $cmd, int $exitStatus, string $output) {
  162. // Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
  163. Arsse::$user = $this->createMock(User::class);
  164. Arsse::$user->method("remove")->will($this->returnCallback(function($user) {
  165. if ($user == "john.doe@example.com") {
  166. return true;
  167. }
  168. throw new \JKingWeb\Arsse\User\Exception("doesNotExist");
  169. }));
  170. $this->assertConsole(new CLI, $cmd, $exitStatus, $output);
  171. }
  172. public function provideUserRemovals() {
  173. return [
  174. ["arsse.php user remove john.doe@example.com", 0, ""],
  175. ["arsse.php user remove jane.doe@example.com", 10402, ""],
  176. ];
  177. }
  178. /** @dataProvider provideUserPasswordChanges */
  179. public function testChangeAUserPassword(string $cmd, int $exitStatus, string $output) {
  180. // Phake is somehow unable to mock the User class correctly, so we use PHPUnit's mocks instead
  181. Arsse::$user = $this->createMock(User::class);
  182. Arsse::$user->method("passwordSet")->will($this->returnCallback(function($user, $pass = null) {
  183. switch ($user) {
  184. case "jane.doe@example.com":
  185. throw new \JKingWeb\Arsse\User\Exception("doesNotExist");
  186. case "john.doe@example.com":
  187. return is_null($pass) ? "random password" : $pass;
  188. }
  189. }));
  190. $this->assertConsole(new CLI, $cmd, $exitStatus, $output);
  191. }
  192. public function provideUserPasswordChanges() {
  193. return [
  194. ["arsse.php user set-pass john.doe@example.com", 0, "random password"],
  195. ["arsse.php user set-pass john.doe@example.com superman", 0, ""],
  196. ["arsse.php user set-pass jane.doe@example.com", 10402, ""],
  197. ];
  198. }
  199. }