RoundCube Webmail
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.

298 lines
8.0 KiB

  1. <?php
  2. namespace Roundcube\Tests;
  3. use PHPUnit\Framework\TestCase;
  4. /**
  5. * Test class to test rcmail_action_mail_index
  6. */
  7. class ActionTestCase extends TestCase
  8. {
  9. private static $files = [];
  10. #[\Override]
  11. public static function setUpBeforeClass(): void
  12. {
  13. // reset some interfering globals set in other tests
  14. $_SERVER['REQUEST_URI'] = '';
  15. $rcmail = \rcmail::get_instance();
  16. $rcmail->load_gui();
  17. }
  18. #[\Override]
  19. public static function tearDownAfterClass(): void
  20. {
  21. foreach (self::$files as $file) {
  22. unlink($file);
  23. }
  24. self::$files = [];
  25. $rcmail = \rcmail::get_instance();
  26. $rcmail->shutdown();
  27. \html::$doctype = 'xhtml';
  28. $_FILES = [];
  29. }
  30. #[\Override]
  31. protected function setUp(): void
  32. {
  33. $_GET = [];
  34. $_POST = [];
  35. $_REQUEST = [];
  36. }
  37. /**
  38. * Initialize the testing suite
  39. */
  40. public static function init()
  41. {
  42. self::initSession();
  43. self::initDB();
  44. self::initUser();
  45. self::mockStorage();
  46. }
  47. /**
  48. * Initialize "mocked" output class
  49. */
  50. protected static function initOutput($mode, $task, $action, $framed = false)
  51. {
  52. $rcmail = \rcmail::get_instance();
  53. $rcmail->task = $task;
  54. $rcmail->action = $action;
  55. if ($mode == \rcmail_action::MODE_AJAX) {
  56. return $rcmail->output = new OutputJsonMock();
  57. }
  58. $rcmail->output = new OutputHtmlMock($task, $framed);
  59. if ($framed) {
  60. $rcmail->comm_path .= '&_framed=1';
  61. $rcmail->output->set_env('framed', true);
  62. }
  63. $rcmail->output->set_env('task', $task);
  64. $rcmail->output->set_env('action', $action);
  65. $rcmail->output->set_env('comm_path', $rcmail->comm_path);
  66. $rcmail->output->set_charset(RCUBE_CHARSET);
  67. return $rcmail->output;
  68. }
  69. /**
  70. * Wipe and re-initialize database
  71. */
  72. public static function initDB($file = null)
  73. {
  74. $rcmail = \rcmail::get_instance();
  75. $dsn = \rcube_db::parse_dsn($rcmail->config->get('db_dsnw'));
  76. $db = $rcmail->get_dbh();
  77. if ($file) {
  78. self::loadSQLScript($db, $file);
  79. return;
  80. }
  81. if ($dsn['phptype'] == 'mysql' || $dsn['phptype'] == 'mysqli') {
  82. // drop all existing tables first
  83. $db->query('SET FOREIGN_KEY_CHECKS=0');
  84. $sql_res = $db->query('SHOW TABLES');
  85. while ($sql_arr = $db->fetch_array($sql_res)) {
  86. $table = reset($sql_arr);
  87. $db->query("DROP TABLE {$table}");
  88. }
  89. // init database with schema
  90. system(sprintf('cat %s %s | mysql -h %s -u %s --password=%s %s',
  91. realpath(INSTALL_PATH . '/SQL/mysql.initial.sql'),
  92. realpath(TESTS_DIR . 'src/sql/init.sql'),
  93. escapeshellarg($dsn['hostspec']),
  94. escapeshellarg($dsn['username']),
  95. escapeshellarg($dsn['password']),
  96. escapeshellarg($dsn['database'])
  97. ));
  98. } elseif ($dsn['phptype'] == 'sqlite') {
  99. $db->closeConnection();
  100. // delete database file
  101. @unlink($dsn['database']);
  102. // load sample test data
  103. self::loadSQLScript($db, 'init');
  104. }
  105. }
  106. /**
  107. * Set the $rcmail->user property
  108. */
  109. public static function initUser()
  110. {
  111. $rcmail = \rcmail::get_instance();
  112. $rcmail->set_user(new \rcube_user(1));
  113. }
  114. /**
  115. * Set the $rcmail->session property
  116. */
  117. public static function initSession()
  118. {
  119. $rcmail = \rcmail::get_instance();
  120. $rcmail->session = new \rcube_session_php($rcmail->config);
  121. }
  122. /**
  123. * Set the $rcmail->storage property
  124. *
  125. * @return StorageMock The storage object
  126. */
  127. public static function mockStorage()
  128. {
  129. $rcmail = \rcmail::get_instance();
  130. $rcmail->storage = new StorageMock(); // @phpstan-ignore-line
  131. return $rcmail->storage;
  132. }
  133. /**
  134. * Create a temp file
  135. */
  136. protected function createTempFile($content = '')
  137. {
  138. $file = \rcube_utils::temp_filename('tests');
  139. if ($content !== '') {
  140. file_put_contents($file, $content);
  141. }
  142. self::$files[] = $file;
  143. return $file;
  144. }
  145. /**
  146. * Prepare fake file upload handling
  147. */
  148. protected function fakeUpload($name = '_file', $is_array = true, $error = 0)
  149. {
  150. $content = base64_decode(\rcmail_output::BLANK_GIF);
  151. $file = [
  152. 'name' => 'test.gif',
  153. 'type' => 'image/gif',
  154. 'tmp_name' => $this->createTempFile($content),
  155. 'error' => $error,
  156. 'size' => strlen($content),
  157. 'id' => 'i' . microtime(true),
  158. ];
  159. // Attachments handling plugins use move_uploaded_file() which does not work
  160. // here. We'll add a fake hook handler for our purposes.
  161. $rcmail = \rcmail::get_instance();
  162. $rcmail->plugins->register_hook('attachment_upload', static function ($att) use ($file) {
  163. $att['status'] = true;
  164. $att['id'] = $file['id'];
  165. return $att;
  166. });
  167. $_FILES = [];
  168. if ($is_array) {
  169. $_FILES[$name] = [
  170. 'name' => [$file['name']],
  171. 'type' => [$file['type']],
  172. 'tmp_name' => [$file['tmp_name']],
  173. 'error' => [$file['error']],
  174. 'size' => [$file['size']],
  175. 'id' => [$file['id']],
  176. ];
  177. } else {
  178. $_FILES[$name] = $file;
  179. }
  180. return $file;
  181. }
  182. /**
  183. * Create file upload record
  184. */
  185. protected function fileUpload($group)
  186. {
  187. $content = base64_decode(\rcmail_output::BLANK_GIF);
  188. $file = [
  189. 'name' => 'test.gif',
  190. 'type' => 'image/gif',
  191. 'size' => strlen($content),
  192. 'group' => $group,
  193. 'id' => 'i' . microtime(true),
  194. ];
  195. // Attachments handling plugins use move_uploaded_file() which does not work
  196. // here. We'll add a fake hook handler for our purposes.
  197. $rcmail = \rcmail::get_instance();
  198. $rcmail->plugins->register_hook('attachment_upload', static function ($att) use ($file) {
  199. $att['status'] = true;
  200. $att['id'] = $file['id'];
  201. return $att;
  202. });
  203. $rcmail->insert_uploaded_file($file);
  204. $upload = \rcmail::get_instance()->get_uploaded_file($file['id']);
  205. $this->assertTrue(is_array($upload));
  206. return $upload;
  207. }
  208. /**
  209. * Load an execute specified SQL script
  210. */
  211. protected static function loadSQLScript($db, $name)
  212. {
  213. // load sample test data
  214. // Note: exec_script() does not really work with these queries
  215. $sql = file_get_contents(TESTS_DIR . "src/sql/{$name}.sql");
  216. $sql = preg_split('/;\n/', $sql, -1, \PREG_SPLIT_NO_EMPTY);
  217. foreach ($sql as $query) {
  218. $result = $db->query($query);
  219. if ($error = $db->is_error($result)) {
  220. \rcube::raise_error($error, false, true);
  221. }
  222. }
  223. }
  224. /**
  225. * Call the action's run() method and handle exit exception
  226. */
  227. protected function runAndAssert($action, $expected_code, $args = [])
  228. {
  229. // Reset output in case we execute the method multiple times in a single test
  230. $rcmail = \rcmail::get_instance();
  231. $rcmail->output->reset(true);
  232. // reset some static props
  233. setProperty($action, 'edit_form', null);
  234. try {
  235. StderrMock::start();
  236. $action->run($args);
  237. StderrMock::stop();
  238. } catch (ExitException $e) {
  239. $this->assertSame($expected_code, $e->getCode());
  240. } catch (\Exception $e) {
  241. if ($e->getMessage() == 'Error raised' && $expected_code == OutputHtmlMock::E_EXIT) {
  242. return;
  243. }
  244. echo StderrMock::$output;
  245. throw $e;
  246. }
  247. }
  248. }