Mercurial > hg > isophonics-drupal-site
comparison core/modules/dblog/tests/src/Functional/DbLogTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 129ea1e6d783 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\Tests\dblog\Functional; | |
4 | |
5 use Drupal\Component\Utility\Html; | |
6 use Drupal\Component\Utility\Unicode; | |
7 use Drupal\Core\Logger\RfcLogLevel; | |
8 use Drupal\Core\Url; | |
9 use Drupal\dblog\Controller\DbLogController; | |
10 use Drupal\Tests\BrowserTestBase; | |
11 use Drupal\Tests\Traits\Core\CronRunTrait; | |
12 | |
13 /** | |
14 * Generate events and verify dblog entries; verify user access to log reports | |
15 * based on permissions. | |
16 * | |
17 * @group dblog | |
18 */ | |
19 class DbLogTest extends BrowserTestBase { | |
20 use CronRunTrait; | |
21 | |
22 /** | |
23 * Modules to enable. | |
24 * | |
25 * @var array | |
26 */ | |
27 public static $modules = ['dblog', 'node', 'forum', 'help', 'block']; | |
28 | |
29 /** | |
30 * A user with some relevant administrative permissions. | |
31 * | |
32 * @var \Drupal\user\UserInterface | |
33 */ | |
34 protected $adminUser; | |
35 | |
36 /** | |
37 * A user without any permissions. | |
38 * | |
39 * @var \Drupal\user\UserInterface | |
40 */ | |
41 protected $webUser; | |
42 | |
43 /** | |
44 * {@inheritdoc} | |
45 */ | |
46 protected function setUp() { | |
47 parent::setUp(); | |
48 $this->drupalPlaceBlock('system_breadcrumb_block'); | |
49 $this->drupalPlaceBlock('page_title_block'); | |
50 | |
51 // Create users with specific permissions. | |
52 $this->adminUser = $this->drupalCreateUser(['administer site configuration', 'access administration pages', 'access site reports', 'administer users']); | |
53 $this->webUser = $this->drupalCreateUser([]); | |
54 } | |
55 | |
56 /** | |
57 * Tests Database Logging module functionality through interfaces. | |
58 * | |
59 * First logs in users, then creates database log events, and finally tests | |
60 * Database Logging module functionality through both the admin and user | |
61 * interfaces. | |
62 */ | |
63 public function testDbLog() { | |
64 // Log in the admin user. | |
65 $this->drupalLogin($this->adminUser); | |
66 | |
67 $row_limit = 100; | |
68 $this->verifyRowLimit($row_limit); | |
69 $this->verifyCron($row_limit); | |
70 $this->verifyEvents(); | |
71 $this->verifyReports(); | |
72 $this->verifyBreadcrumbs(); | |
73 $this->verifyLinkEscaping(); | |
74 // Verify the overview table sorting. | |
75 $orders = ['Date', 'Type', 'User']; | |
76 $sorts = ['asc', 'desc']; | |
77 foreach ($orders as $order) { | |
78 foreach ($sorts as $sort) { | |
79 $this->verifySort($sort, $order); | |
80 } | |
81 } | |
82 | |
83 // Log in the regular user. | |
84 $this->drupalLogin($this->webUser); | |
85 $this->verifyReports(403); | |
86 } | |
87 | |
88 /** | |
89 * Test individual log event page. | |
90 */ | |
91 public function testLogEventPage() { | |
92 // Login the admin user. | |
93 $this->drupalLogin($this->adminUser); | |
94 | |
95 // Since referrer and location links vary by how the tests are run, inject | |
96 // fake log data to test these. | |
97 $context = [ | |
98 'request_uri' => 'http://example.com?dblog=1', | |
99 'referer' => 'http://example.org?dblog=2', | |
100 'uid' => 0, | |
101 'channel' => 'testing', | |
102 'link' => 'foo/bar', | |
103 'ip' => '0.0.1.0', | |
104 'timestamp' => REQUEST_TIME, | |
105 ]; | |
106 \Drupal::service('logger.dblog')->log(RfcLogLevel::NOTICE, 'Test message', $context); | |
107 $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); | |
108 | |
109 // Verify the links appear correctly. | |
110 $this->drupalGet('admin/reports/dblog/event/' . $wid); | |
111 $this->assertLinkByHref($context['request_uri']); | |
112 $this->assertLinkByHref($context['referer']); | |
113 | |
114 // Verify hostname. | |
115 $this->assertRaw($context['ip'], 'Found hostname on the detail page.'); | |
116 | |
117 // Verify severity. | |
118 $this->assertText('Notice', 'The severity was properly displayed on the detail page.'); | |
119 } | |
120 | |
121 /** | |
122 * Verifies setting of the database log row limit. | |
123 * | |
124 * @param int $row_limit | |
125 * The row limit. | |
126 */ | |
127 private function verifyRowLimit($row_limit) { | |
128 // Change the database log row limit. | |
129 $edit = []; | |
130 $edit['dblog_row_limit'] = $row_limit; | |
131 $this->drupalPostForm('admin/config/development/logging', $edit, t('Save configuration')); | |
132 $this->assertResponse(200); | |
133 | |
134 // Check row limit variable. | |
135 $current_limit = $this->config('dblog.settings')->get('row_limit'); | |
136 $this->assertTrue($current_limit == $row_limit, format_string('[Cache] Row limit variable of @count equals row limit of @limit', ['@count' => $current_limit, '@limit' => $row_limit])); | |
137 } | |
138 | |
139 /** | |
140 * Verifies that cron correctly applies the database log row limit. | |
141 * | |
142 * @param int $row_limit | |
143 * The row limit. | |
144 */ | |
145 private function verifyCron($row_limit) { | |
146 // Generate additional log entries. | |
147 $this->generateLogEntries($row_limit + 10); | |
148 // Verify that the database log row count exceeds the row limit. | |
149 $count = db_query('SELECT COUNT(wid) FROM {watchdog}')->fetchField(); | |
150 $this->assertTrue($count > $row_limit, format_string('Dblog row count of @count exceeds row limit of @limit', ['@count' => $count, '@limit' => $row_limit])); | |
151 | |
152 // Get the number of enabled modules. Cron adds a log entry for each module. | |
153 $list = \Drupal::moduleHandler()->getImplementations('cron'); | |
154 $module_count = count($list); | |
155 $cron_detailed_count = $this->runCron(); | |
156 $this->assertTrue($cron_detailed_count == $module_count + 2, format_string('Cron added @count of @expected new log entries', ['@count' => $cron_detailed_count, '@expected' => $module_count + 2])); | |
157 | |
158 // Test disabling of detailed cron logging. | |
159 $this->config('system.cron')->set('logging', 0)->save(); | |
160 $cron_count = $this->runCron(); | |
161 $this->assertTrue($cron_count = 1, format_string('Cron added @count of @expected new log entries', ['@count' => $cron_count, '@expected' => 1])); | |
162 } | |
163 | |
164 /** | |
165 * Runs cron and returns number of new log entries. | |
166 * | |
167 * @return int | |
168 * Number of new watchdog entries. | |
169 */ | |
170 private function runCron() { | |
171 // Get last ID to compare against; log entries get deleted, so we can't | |
172 // reliably add the number of newly created log entries to the current count | |
173 // to measure number of log entries created by cron. | |
174 $last_id = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); | |
175 | |
176 // Run a cron job. | |
177 $this->cronRun(); | |
178 | |
179 // Get last ID after cron was run. | |
180 $current_id = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); | |
181 | |
182 return $current_id - $last_id; | |
183 } | |
184 | |
185 /** | |
186 * Generates a number of random database log events. | |
187 * | |
188 * @param int $count | |
189 * Number of watchdog entries to generate. | |
190 * @param array $options | |
191 * These options are used to override the defaults for the test. | |
192 * An associative array containing any of the following keys: | |
193 * - 'channel': String identifying the log channel to be output to. | |
194 * If the channel is not set, the default of 'custom' will be used. | |
195 * - 'message': String containing a message to be output to the log. | |
196 * A simple default message is used if not provided. | |
197 * - 'variables': Array of variables that match the message string. | |
198 * - 'severity': Log severity level as defined in logging_severity_levels. | |
199 * - 'link': String linking to view the result of the event. | |
200 * - 'user': String identifying the username. | |
201 * - 'uid': Int identifying the user id for the user. | |
202 * - 'request_uri': String identifying the location of the request. | |
203 * - 'referer': String identifying the referring url. | |
204 * - 'ip': String The ip address of the client machine triggering the log | |
205 * entry. | |
206 * - 'timestamp': Int unix timestamp. | |
207 */ | |
208 private function generateLogEntries($count, $options = []) { | |
209 global $base_root; | |
210 | |
211 // Prepare the fields to be logged | |
212 $log = $options + [ | |
213 'channel' => 'custom', | |
214 'message' => 'Dblog test log message', | |
215 'variables' => [], | |
216 'severity' => RfcLogLevel::NOTICE, | |
217 'link' => NULL, | |
218 'user' => $this->adminUser, | |
219 'uid' => $this->adminUser->id(), | |
220 'request_uri' => $base_root . \Drupal::request()->getRequestUri(), | |
221 'referer' => \Drupal::request()->server->get('HTTP_REFERER'), | |
222 'ip' => '127.0.0.1', | |
223 'timestamp' => REQUEST_TIME, | |
224 ]; | |
225 | |
226 $logger = $this->container->get('logger.dblog'); | |
227 $message = $log['message'] . ' Entry #'; | |
228 for ($i = 0; $i < $count; $i++) { | |
229 $log['message'] = $message . $i; | |
230 $logger->log($log['severity'], $log['message'], $log); | |
231 } | |
232 } | |
233 | |
234 /** | |
235 * Clear the entry logs by clicking on 'Clear log messages' button. | |
236 */ | |
237 protected function clearLogsEntries() { | |
238 $this->drupalGet(Url::fromRoute('dblog.confirm')); | |
239 } | |
240 | |
241 /** | |
242 * Filters the logs according to the specific severity and log entry type. | |
243 * | |
244 * @param string $type | |
245 * (optional) The log entry type. | |
246 * @param string $severity | |
247 * (optional) The log entry severity. | |
248 */ | |
249 protected function filterLogsEntries($type = NULL, $severity = NULL) { | |
250 $edit = []; | |
251 if (isset($type)) { | |
252 $edit['type[]'] = $type; | |
253 } | |
254 if (isset($severity)) { | |
255 $edit['severity[]'] = $severity; | |
256 } | |
257 $this->drupalPostForm(NULL, $edit, t('Filter')); | |
258 } | |
259 | |
260 /** | |
261 * Confirms that database log reports are displayed at the correct paths. | |
262 * | |
263 * @param int $response | |
264 * (optional) HTTP response code. Defaults to 200. | |
265 */ | |
266 private function verifyReports($response = 200) { | |
267 // View the database log help page. | |
268 $this->drupalGet('admin/help/dblog'); | |
269 $this->assertResponse($response); | |
270 if ($response == 200) { | |
271 $this->assertText(t('Database Logging'), 'DBLog help was displayed'); | |
272 } | |
273 | |
274 // View the database log report page. | |
275 $this->drupalGet('admin/reports/dblog'); | |
276 $this->assertResponse($response); | |
277 if ($response == 200) { | |
278 $this->assertText(t('Recent log messages'), 'DBLog report was displayed'); | |
279 } | |
280 | |
281 $this->drupalGet('admin/reports/dblog/confirm'); | |
282 $this->assertResponse($response); | |
283 if ($response == 200) { | |
284 $this->assertText(t('Are you sure you want to delete the recent logs?'), 'DBLog clear logs form was displayed'); | |
285 } | |
286 | |
287 // View the database log page-not-found report page. | |
288 $this->drupalGet('admin/reports/page-not-found'); | |
289 $this->assertResponse($response); | |
290 if ($response == 200) { | |
291 $this->assertText("Top 'page not found' errors", 'DBLog page-not-found report was displayed'); | |
292 } | |
293 | |
294 // View the database log access-denied report page. | |
295 $this->drupalGet('admin/reports/access-denied'); | |
296 $this->assertResponse($response); | |
297 if ($response == 200) { | |
298 $this->assertText("Top 'access denied' errors", 'DBLog access-denied report was displayed'); | |
299 } | |
300 | |
301 // View the database log event page. | |
302 $wid = db_query('SELECT MIN(wid) FROM {watchdog}')->fetchField(); | |
303 $this->drupalGet('admin/reports/dblog/event/' . $wid); | |
304 $this->assertResponse($response); | |
305 if ($response == 200) { | |
306 $this->assertText(t('Details'), 'DBLog event node was displayed'); | |
307 } | |
308 } | |
309 | |
310 /** | |
311 * Generates and then verifies breadcrumbs. | |
312 */ | |
313 private function verifyBreadcrumbs() { | |
314 // View the database log event page. | |
315 $wid = db_query('SELECT MIN(wid) FROM {watchdog}')->fetchField(); | |
316 $this->drupalGet('admin/reports/dblog/event/' . $wid); | |
317 $xpath = '//nav[@class="breadcrumb"]/ol/li[last()]/a'; | |
318 $this->assertEqual(current($this->xpath($xpath))->getText(), 'Recent log messages', 'DBLogs link displayed at breadcrumb in event page.'); | |
319 } | |
320 | |
321 /** | |
322 * Generates and then verifies various types of events. | |
323 */ | |
324 private function verifyEvents() { | |
325 // Invoke events. | |
326 $this->doUser(); | |
327 $this->drupalCreateContentType(['type' => 'article', 'name' => t('Article')]); | |
328 $this->drupalCreateContentType(['type' => 'page', 'name' => t('Basic page')]); | |
329 $this->doNode('article'); | |
330 $this->doNode('page'); | |
331 $this->doNode('forum'); | |
332 | |
333 // When a user account is canceled, any content they created remains but the | |
334 // uid = 0. Records in the watchdog table related to that user have the uid | |
335 // set to zero. | |
336 } | |
337 | |
338 /** | |
339 * Verifies the sorting functionality of the database logging reports table. | |
340 * | |
341 * @param string $sort | |
342 * The sort direction. | |
343 * @param string $order | |
344 * The order by which the table should be sorted. | |
345 */ | |
346 public function verifySort($sort = 'asc', $order = 'Date') { | |
347 $this->drupalGet('admin/reports/dblog', ['query' => ['sort' => $sort, 'order' => $order]]); | |
348 $this->assertResponse(200); | |
349 $this->assertText(t('Recent log messages'), 'DBLog report was displayed correctly and sorting went fine.'); | |
350 } | |
351 | |
352 /** | |
353 * Tests the escaping of links in the operation row of a database log detail | |
354 * page. | |
355 */ | |
356 private function verifyLinkEscaping() { | |
357 $link = \Drupal::l('View', Url::fromRoute('entity.node.canonical', ['node' => 1])); | |
358 $message = 'Log entry added to do the verifyLinkEscaping test.'; | |
359 $this->generateLogEntries(1, [ | |
360 'message' => $message, | |
361 'link' => $link, | |
362 ]); | |
363 | |
364 $result = db_query_range('SELECT wid FROM {watchdog} ORDER BY wid DESC', 0, 1); | |
365 $this->drupalGet('admin/reports/dblog/event/' . $result->fetchField()); | |
366 | |
367 // Check if the link exists (unescaped). | |
368 $this->assertRaw($link); | |
369 } | |
370 | |
371 /** | |
372 * Generates and then verifies some user events. | |
373 */ | |
374 private function doUser() { | |
375 // Set user variables. | |
376 $name = $this->randomMachineName(); | |
377 $pass = user_password(); | |
378 // Add a user using the form to generate an add user event (which is not | |
379 // triggered by drupalCreateUser). | |
380 $edit = []; | |
381 $edit['name'] = $name; | |
382 $edit['mail'] = $name . '@example.com'; | |
383 $edit['pass[pass1]'] = $pass; | |
384 $edit['pass[pass2]'] = $pass; | |
385 $edit['status'] = 1; | |
386 $this->drupalPostForm('admin/people/create', $edit, t('Create new account')); | |
387 $this->assertResponse(200); | |
388 // Retrieve the user object. | |
389 $user = user_load_by_name($name); | |
390 $this->assertTrue($user != NULL, format_string('User @name was loaded', ['@name' => $name])); | |
391 // pass_raw property is needed by drupalLogin. | |
392 $user->passRaw = $pass; | |
393 // Log in user. | |
394 $this->drupalLogin($user); | |
395 // Log out user. | |
396 $this->drupalLogout(); | |
397 // Fetch the row IDs in watchdog that relate to the user. | |
398 $result = db_query('SELECT wid FROM {watchdog} WHERE uid = :uid', [':uid' => $user->id()]); | |
399 foreach ($result as $row) { | |
400 $ids[] = $row->wid; | |
401 } | |
402 $count_before = (isset($ids)) ? count($ids) : 0; | |
403 $this->assertTrue($count_before > 0, format_string('DBLog contains @count records for @name', ['@count' => $count_before, '@name' => $user->getUsername()])); | |
404 | |
405 // Log in the admin user. | |
406 $this->drupalLogin($this->adminUser); | |
407 // Delete the user created at the start of this test. | |
408 // We need to POST here to invoke batch_process() in the internal browser. | |
409 $this->drupalPostForm('user/' . $user->id() . '/cancel', ['user_cancel_method' => 'user_cancel_reassign'], t('Cancel account')); | |
410 | |
411 // View the database log report. | |
412 $this->drupalGet('admin/reports/dblog'); | |
413 $this->assertResponse(200); | |
414 | |
415 // Verify that the expected events were recorded. | |
416 // Add user. | |
417 // Default display includes name and email address; if too long, the email | |
418 // address is replaced by three periods. | |
419 $this->assertLogMessage(t('New user: %name %email.', ['%name' => $name, '%email' => '<' . $user->getEmail() . '>']), 'DBLog event was recorded: [add user]'); | |
420 // Log in user. | |
421 $this->assertLogMessage(t('Session opened for %name.', ['%name' => $name]), 'DBLog event was recorded: [login user]'); | |
422 // Log out user. | |
423 $this->assertLogMessage(t('Session closed for %name.', ['%name' => $name]), 'DBLog event was recorded: [logout user]'); | |
424 // Delete user. | |
425 $message = t('Deleted user: %name %email.', ['%name' => $name, '%email' => '<' . $user->getEmail() . '>']); | |
426 $message_text = Unicode::truncate(Html::decodeEntities(strip_tags($message)), 56, TRUE, TRUE); | |
427 // Verify that the full message displays on the details page. | |
428 $link = FALSE; | |
429 if ($links = $this->xpath('//a[text()="' . $message_text . '"]')) { | |
430 // Found link with the message text. | |
431 $links = array_shift($links); | |
432 $value = $links->getAttribute('href'); | |
433 | |
434 // Extract link to details page. | |
435 $link = Unicode::substr($value, strpos($value, 'admin/reports/dblog/event/')); | |
436 $this->drupalGet($link); | |
437 // Check for full message text on the details page. | |
438 $this->assertRaw($message, 'DBLog event details was found: [delete user]'); | |
439 } | |
440 $this->assertTrue($link, 'DBLog event was recorded: [delete user]'); | |
441 // Visit random URL (to generate page not found event). | |
442 $not_found_url = $this->randomMachineName(60); | |
443 $this->drupalGet($not_found_url); | |
444 $this->assertResponse(404); | |
445 // View the database log page-not-found report page. | |
446 $this->drupalGet('admin/reports/page-not-found'); | |
447 $this->assertResponse(200); | |
448 // Check that full-length URL displayed. | |
449 $this->assertText($not_found_url, 'DBLog event was recorded: [page not found]'); | |
450 } | |
451 | |
452 /** | |
453 * Generates and then verifies some node events. | |
454 * | |
455 * @param string $type | |
456 * A node type (e.g., 'article', 'page' or 'forum'). | |
457 */ | |
458 private function doNode($type) { | |
459 // Create user. | |
460 $perm = ['create ' . $type . ' content', 'edit own ' . $type . ' content', 'delete own ' . $type . ' content']; | |
461 $user = $this->drupalCreateUser($perm); | |
462 // Log in user. | |
463 $this->drupalLogin($user); | |
464 | |
465 // Create a node using the form in order to generate an add content event | |
466 // (which is not triggered by drupalCreateNode). | |
467 $edit = $this->getContent($type); | |
468 $title = $edit['title[0][value]']; | |
469 $this->drupalPostForm('node/add/' . $type, $edit, t('Save')); | |
470 $this->assertResponse(200); | |
471 // Retrieve the node object. | |
472 $node = $this->drupalGetNodeByTitle($title); | |
473 $this->assertTrue($node != NULL, format_string('Node @title was loaded', ['@title' => $title])); | |
474 // Edit the node. | |
475 $edit = $this->getContentUpdate($type); | |
476 $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save')); | |
477 $this->assertResponse(200); | |
478 // Delete the node. | |
479 $this->drupalPostForm('node/' . $node->id() . '/delete', [], t('Delete')); | |
480 $this->assertResponse(200); | |
481 // View the node (to generate page not found event). | |
482 $this->drupalGet('node/' . $node->id()); | |
483 $this->assertResponse(404); | |
484 // View the database log report (to generate access denied event). | |
485 $this->drupalGet('admin/reports/dblog'); | |
486 $this->assertResponse(403); | |
487 | |
488 // Log in the admin user. | |
489 $this->drupalLogin($this->adminUser); | |
490 // View the database log report. | |
491 $this->drupalGet('admin/reports/dblog'); | |
492 $this->assertResponse(200); | |
493 | |
494 // Verify that node events were recorded. | |
495 // Was node content added? | |
496 $this->assertLogMessage(t('@type: added %title.', ['@type' => $type, '%title' => $title]), 'DBLog event was recorded: [content added]'); | |
497 // Was node content updated? | |
498 $this->assertLogMessage(t('@type: updated %title.', ['@type' => $type, '%title' => $title]), 'DBLog event was recorded: [content updated]'); | |
499 // Was node content deleted? | |
500 $this->assertLogMessage(t('@type: deleted %title.', ['@type' => $type, '%title' => $title]), 'DBLog event was recorded: [content deleted]'); | |
501 | |
502 // View the database log access-denied report page. | |
503 $this->drupalGet('admin/reports/access-denied'); | |
504 $this->assertResponse(200); | |
505 // Verify that the 'access denied' event was recorded. | |
506 $this->assertText('admin/reports/dblog', 'DBLog event was recorded: [access denied]'); | |
507 | |
508 // View the database log page-not-found report page. | |
509 $this->drupalGet('admin/reports/page-not-found'); | |
510 $this->assertResponse(200); | |
511 // Verify that the 'page not found' event was recorded. | |
512 $this->assertText('node/' . $node->id(), 'DBLog event was recorded: [page not found]'); | |
513 } | |
514 | |
515 /** | |
516 * Creates random content based on node content type. | |
517 * | |
518 * @param string $type | |
519 * Node content type (e.g., 'article'). | |
520 * | |
521 * @return array | |
522 * Random content needed by various node types. | |
523 */ | |
524 private function getContent($type) { | |
525 switch ($type) { | |
526 case 'forum': | |
527 $content = [ | |
528 'title[0][value]' => $this->randomMachineName(8), | |
529 'taxonomy_forums' => 1, | |
530 'body[0][value]' => $this->randomMachineName(32), | |
531 ]; | |
532 break; | |
533 | |
534 default: | |
535 $content = [ | |
536 'title[0][value]' => $this->randomMachineName(8), | |
537 'body[0][value]' => $this->randomMachineName(32), | |
538 ]; | |
539 break; | |
540 } | |
541 return $content; | |
542 } | |
543 | |
544 /** | |
545 * Creates random content as an update based on node content type. | |
546 * | |
547 * @param string $type | |
548 * Node content type (e.g., 'article'). | |
549 * | |
550 * @return array | |
551 * Random content needed by various node types. | |
552 */ | |
553 private function getContentUpdate($type) { | |
554 $content = [ | |
555 'body[0][value]' => $this->randomMachineName(32), | |
556 ]; | |
557 return $content; | |
558 } | |
559 | |
560 /** | |
561 * Tests the addition and clearing of log events through the admin interface. | |
562 * | |
563 * Logs in the admin user, creates a database log event, and tests the | |
564 * functionality of clearing the database log through the admin interface. | |
565 */ | |
566 public function testDBLogAddAndClear() { | |
567 global $base_root; | |
568 // Get a count of how many watchdog entries already exist. | |
569 $count = db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(); | |
570 $log = [ | |
571 'channel' => 'system', | |
572 'message' => 'Log entry added to test the doClearTest clear down.', | |
573 'variables' => [], | |
574 'severity' => RfcLogLevel::NOTICE, | |
575 'link' => NULL, | |
576 'user' => $this->adminUser, | |
577 'uid' => $this->adminUser->id(), | |
578 'request_uri' => $base_root . \Drupal::request()->getRequestUri(), | |
579 'referer' => \Drupal::request()->server->get('HTTP_REFERER'), | |
580 'ip' => '127.0.0.1', | |
581 'timestamp' => REQUEST_TIME, | |
582 ]; | |
583 // Add a watchdog entry. | |
584 $this->container->get('logger.dblog')->log($log['severity'], $log['message'], $log); | |
585 // Make sure the table count has actually been incremented. | |
586 $this->assertEqual($count + 1, db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(), format_string('\Drupal\dblog\Logger\DbLog->log() added an entry to the dblog :count', [':count' => $count])); | |
587 // Log in the admin user. | |
588 $this->drupalLogin($this->adminUser); | |
589 // Post in order to clear the database table. | |
590 $this->clearLogsEntries(); | |
591 // Confirm that the logs should be cleared. | |
592 $this->drupalPostForm(NULL, [], 'Confirm'); | |
593 // Count the rows in watchdog that previously related to the deleted user. | |
594 $count = db_query('SELECT COUNT(*) FROM {watchdog}')->fetchField(); | |
595 $this->assertEqual($count, 0, format_string('DBLog contains :count records after a clear.', [':count' => $count])); | |
596 } | |
597 | |
598 /** | |
599 * Tests the database log filter functionality at admin/reports/dblog. | |
600 */ | |
601 public function testFilter() { | |
602 $this->drupalLogin($this->adminUser); | |
603 | |
604 // Clear the log to ensure that only generated entries will be found. | |
605 db_delete('watchdog')->execute(); | |
606 | |
607 // Generate 9 random watchdog entries. | |
608 $type_names = []; | |
609 $types = []; | |
610 for ($i = 0; $i < 3; $i++) { | |
611 $type_names[] = $type_name = $this->randomMachineName(); | |
612 $severity = RfcLogLevel::EMERGENCY; | |
613 for ($j = 0; $j < 3; $j++) { | |
614 $types[] = $type = [ | |
615 'count' => $j + 1, | |
616 'type' => $type_name, | |
617 'severity' => $severity++, | |
618 ]; | |
619 $this->generateLogEntries($type['count'], [ | |
620 'channel' => $type['type'], | |
621 'severity' => $type['severity'], | |
622 ]); | |
623 } | |
624 } | |
625 | |
626 // View the database log page. | |
627 $this->drupalGet('admin/reports/dblog'); | |
628 | |
629 // Confirm that all the entries are displayed. | |
630 $count = $this->getTypeCount($types); | |
631 foreach ($types as $key => $type) { | |
632 $this->assertEqual($count[$key], $type['count'], 'Count matched'); | |
633 } | |
634 | |
635 // Filter by each type and confirm that entries with various severities are | |
636 // displayed. | |
637 foreach ($type_names as $type_name) { | |
638 $this->filterLogsEntries($type_name); | |
639 | |
640 // Count the number of entries of this type. | |
641 $type_count = 0; | |
642 foreach ($types as $type) { | |
643 if ($type['type'] == $type_name) { | |
644 $type_count += $type['count']; | |
645 } | |
646 } | |
647 | |
648 $count = $this->getTypeCount($types); | |
649 $this->assertEqual(array_sum($count), $type_count, 'Count matched'); | |
650 } | |
651 | |
652 // Set the filter to match each of the two filter-type attributes and | |
653 // confirm the correct number of entries are displayed. | |
654 foreach ($types as $type) { | |
655 $this->filterLogsEntries($type['type'], $type['severity']); | |
656 | |
657 $count = $this->getTypeCount($types); | |
658 $this->assertEqual(array_sum($count), $type['count'], 'Count matched'); | |
659 } | |
660 | |
661 $this->drupalGet('admin/reports/dblog', ['query' => ['order' => 'Type']]); | |
662 $this->assertResponse(200); | |
663 $this->assertText(t('Operations'), 'Operations text found'); | |
664 | |
665 // Clear all logs and make sure the confirmation message is found. | |
666 $this->clearLogsEntries(); | |
667 // Confirm that the logs should be cleared. | |
668 $this->drupalPostForm(NULL, [], 'Confirm'); | |
669 $this->assertText(t('Database log cleared.'), 'Confirmation message found'); | |
670 } | |
671 | |
672 /** | |
673 * Gets the database log event information from the browser page. | |
674 * | |
675 * @return array | |
676 * List of log events where each event is an array with following keys: | |
677 * - severity: (int) A database log severity constant. | |
678 * - type: (string) The type of database log event. | |
679 * - message: (string) The message for this database log event. | |
680 * - user: (string) The user associated with this database log event. | |
681 */ | |
682 protected function getLogEntries() { | |
683 $entries = []; | |
684 if ($table = $this->getLogsEntriesTable()) { | |
685 foreach ($table as $row) { | |
686 $cells = $row->findAll('css', 'td'); | |
687 $entries[] = [ | |
688 'severity' => $this->getSeverityConstant($row->getAttribute('class')), | |
689 'type' => $cells[1]->getText(), | |
690 'message' => $cells[3]->getText(), | |
691 'user' => $cells[4]->getText(), | |
692 ]; | |
693 } | |
694 } | |
695 return $entries; | |
696 } | |
697 | |
698 /** | |
699 * Find the Logs table in the DOM. | |
700 * | |
701 * @return \SimpleXMLElement[] | |
702 * The return value of a xpath search. | |
703 */ | |
704 protected function getLogsEntriesTable() { | |
705 return $this->xpath('.//table[@id="admin-dblog"]/tbody/tr'); | |
706 } | |
707 | |
708 /** | |
709 * Gets the count of database log entries by database log event type. | |
710 * | |
711 * @param array $types | |
712 * The type information to compare against. | |
713 * | |
714 * @return array | |
715 * The count of each type keyed by the key of the $types array. | |
716 */ | |
717 protected function getTypeCount(array $types) { | |
718 $entries = $this->getLogEntries(); | |
719 $count = array_fill(0, count($types), 0); | |
720 foreach ($entries as $entry) { | |
721 foreach ($types as $key => $type) { | |
722 if ($entry['type'] == $type['type'] && $entry['severity'] == $type['severity']) { | |
723 $count[$key]++; | |
724 break; | |
725 } | |
726 } | |
727 } | |
728 return $count; | |
729 } | |
730 | |
731 /** | |
732 * Gets the watchdog severity constant corresponding to the CSS class. | |
733 * | |
734 * @param string $class | |
735 * CSS class attribute. | |
736 * | |
737 * @return int|null | |
738 * The watchdog severity constant or NULL if not found. | |
739 */ | |
740 protected function getSeverityConstant($class) { | |
741 $map = array_flip(DbLogController::getLogLevelClassMap()); | |
742 | |
743 // Find the class that contains the severity. | |
744 $classes = explode(' ', $class); | |
745 foreach ($classes as $class) { | |
746 if (isset($map[$class])) { | |
747 return $map[$class]; | |
748 } | |
749 } | |
750 return NULL; | |
751 } | |
752 | |
753 /** | |
754 * Confirms that a log message appears on the database log overview screen. | |
755 * | |
756 * This function should only be used for the admin/reports/dblog page, because | |
757 * it checks for the message link text truncated to 56 characters. Other log | |
758 * pages have no detail links so they contain the full message text. | |
759 * | |
760 * @param string $log_message | |
761 * The database log message to check. | |
762 * @param string $message | |
763 * The message to pass to simpletest. | |
764 */ | |
765 protected function assertLogMessage($log_message, $message) { | |
766 $message_text = Unicode::truncate(Html::decodeEntities(strip_tags($log_message)), 56, TRUE, TRUE); | |
767 $this->assertLink($message_text, 0, $message); | |
768 } | |
769 | |
770 /** | |
771 * Tests that the details page displays correctly for a temporary user. | |
772 */ | |
773 public function testTemporaryUser() { | |
774 // Create a temporary user. | |
775 $tempuser = $this->drupalCreateUser(); | |
776 $tempuser_uid = $tempuser->id(); | |
777 | |
778 // Log in as the admin user. | |
779 $this->drupalLogin($this->adminUser); | |
780 | |
781 // Generate a single watchdog entry. | |
782 $this->generateLogEntries(1, ['user' => $tempuser, 'uid' => $tempuser_uid]); | |
783 $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); | |
784 | |
785 // Check if the full message displays on the details page. | |
786 $this->drupalGet('admin/reports/dblog/event/' . $wid); | |
787 $this->assertText('Dblog test log message'); | |
788 | |
789 // Delete the user. | |
790 user_delete($tempuser->id()); | |
791 $this->drupalGet('user/' . $tempuser_uid); | |
792 $this->assertResponse(404); | |
793 | |
794 // Check if the full message displays on the details page. | |
795 $this->drupalGet('admin/reports/dblog/event/' . $wid); | |
796 $this->assertText('Dblog test log message'); | |
797 } | |
798 | |
799 /** | |
800 * Make sure HTML tags are filtered out in the log overview links. | |
801 */ | |
802 public function testOverviewLinks() { | |
803 $this->drupalLogin($this->adminUser); | |
804 $this->generateLogEntries(1, ['message' => "<script>alert('foo');</script><strong>Lorem</strong> ipsum dolor sit amet, consectetur adipiscing & elit."]); | |
805 $this->drupalGet('admin/reports/dblog'); | |
806 $this->assertResponse(200); | |
807 // Make sure HTML tags are filtered out. | |
808 $this->assertRaw('title="alert('foo');Lorem'); | |
809 $this->assertNoRaw("<script>alert('foo');</script>"); | |
810 | |
811 // Make sure HTML tags are filtered out in admin/reports/dblog/event/ too. | |
812 $this->generateLogEntries(1, ['message' => "<script>alert('foo');</script> <strong>Lorem ipsum</strong>"]); | |
813 $wid = db_query('SELECT MAX(wid) FROM {watchdog}')->fetchField(); | |
814 $this->drupalGet('admin/reports/dblog/event/' . $wid); | |
815 $this->assertNoRaw("<script>alert('foo');</script>"); | |
816 $this->assertRaw("alert('foo'); <strong>Lorem ipsum</strong>"); | |
817 } | |
818 | |
819 /** | |
820 * Test sorting for entries with the same timestamp. | |
821 */ | |
822 public function testSameTimestampEntries() { | |
823 $this->drupalLogin($this->adminUser); | |
824 | |
825 $this->generateLogEntries(1, ['timestamp' => 1498062000, 'type' => 'same_time', 'message' => 'First']); | |
826 $this->generateLogEntries(1, ['timestamp' => 1498062000, 'type' => 'same_time', 'message' => 'Second']); | |
827 $this->generateLogEntries(1, ['timestamp' => 1498062000, 'type' => 'same_time', 'message' => 'Third']); | |
828 | |
829 $this->drupalGet('admin/reports/dblog'); | |
830 | |
831 $entries = $this->getLogEntries(); | |
832 $this->assertEquals($entries[0]['message'], 'Third Entry #0'); | |
833 $this->assertEquals($entries[1]['message'], 'Second Entry #0'); | |
834 $this->assertEquals($entries[2]['message'], 'First Entry #0'); | |
835 } | |
836 | |
837 } |