Backup of db before drupal security update
[yaffs-website] / web / core / modules / update / src / Tests / UpdateContribTest.php
1 <?php
2
3 namespace Drupal\update\Tests;
4
5 use Drupal\Core\Url;
6 use Drupal\Core\Utility\ProjectInfo;
7
8 /**
9  * Tests how the Update Manager module handles contributed modules and themes in
10  * a series of functional tests using mock XML data.
11  *
12  * @group update
13  */
14 class UpdateContribTest extends UpdateTestBase {
15
16   /**
17    * Modules to enable.
18    *
19    * @var array
20    */
21   public static $modules = ['update_test', 'update', 'aaa_update_test', 'bbb_update_test', 'ccc_update_test'];
22
23   protected function setUp() {
24     parent::setUp();
25     $admin_user = $this->drupalCreateUser(['administer site configuration']);
26     $this->drupalLogin($admin_user);
27   }
28
29   /**
30    * Tests when there is no available release data for a contrib module.
31    */
32   public function testNoReleasesAvailable() {
33     $system_info = [
34       '#all' => [
35         'version' => '8.0.0',
36       ],
37       'aaa_update_test' => [
38         'project' => 'aaa_update_test',
39         'version' => '8.x-1.0',
40         'hidden' => FALSE,
41       ],
42     ];
43     $this->config('update_test.settings')->set('system_info', $system_info)->save();
44     $this->refreshUpdateStatus(['drupal' => '0.0', 'aaa_update_test' => 'no-releases']);
45     $this->drupalGet('admin/reports/updates');
46     // Cannot use $this->standardTests() because we need to check for the
47     // 'No available releases found' string.
48     $this->assertRaw('<h3>' . t('Drupal core') . '</h3>');
49     $this->assertRaw(\Drupal::l(t('Drupal'), Url::fromUri('http://example.com/project/drupal')));
50     $this->assertText(t('Up to date'));
51     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
52     $this->assertNoText(t('Update available'));
53     $this->assertText(t('No available releases found'));
54     $this->assertNoRaw(\Drupal::l(t('AAA Update test'), Url::fromUri('http://example.com/project/aaa_update_test')));
55
56     $available = update_get_available();
57     $this->assertFalse(isset($available['aaa_update_test']['fetch_status']), 'Results are cached even if no releases are available.');
58   }
59
60   /**
61    * Tests the basic functionality of a contrib module on the status report.
62    */
63   public function testUpdateContribBasic() {
64     $project_link = \Drupal::l(t('AAA Update test'), Url::fromUri('http://example.com/project/aaa_update_test'));
65     $system_info = [
66       '#all' => [
67         'version' => '8.0.0',
68       ],
69       'aaa_update_test' => [
70         'project' => 'aaa_update_test',
71         'version' => '8.x-1.0',
72         'hidden' => FALSE,
73       ],
74     ];
75     $this->config('update_test.settings')->set('system_info', $system_info)->save();
76     $this->refreshUpdateStatus(
77       [
78         'drupal' => '0.0',
79         'aaa_update_test' => '1_0',
80       ]
81     );
82     $this->standardTests();
83     $this->assertText(t('Up to date'));
84     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
85     $this->assertNoText(t('Update available'));
86     $this->assertRaw($project_link, 'Link to aaa_update_test project appears.');
87
88     // Since aaa_update_test is installed the fact it is hidden and in the
89     // Testing package means it should not appear.
90     $system_info['aaa_update_test']['hidden'] = TRUE;
91     $this->config('update_test.settings')->set('system_info', $system_info)->save();
92     $this->refreshUpdateStatus(
93       [
94         'drupal' => '0.0',
95         'aaa_update_test' => '1_0',
96       ]
97     );
98     $this->assertNoRaw($project_link, 'Link to aaa_update_test project does not appear.');
99
100     // A hidden and installed project not in the Testing package should appear.
101     $system_info['aaa_update_test']['package'] = 'aaa_update_test';
102     $this->config('update_test.settings')->set('system_info', $system_info)->save();
103     $this->refreshUpdateStatus(
104       [
105         'drupal' => '0.0',
106         'aaa_update_test' => '1_0',
107       ]
108     );
109     $this->assertRaw($project_link, 'Link to aaa_update_test project appears.');
110   }
111
112   /**
113    * Tests that contrib projects are ordered by project name.
114    *
115    * If a project contains multiple modules, we want to make sure that the
116    * available updates report is sorted by the parent project names, not by the
117    * names of the modules included in each project. In this test case, we have
118    * two contrib projects, "BBB Update test" and "CCC Update test". However, we
119    * have a module called "aaa_update_test" that's part of the "CCC Update test"
120    * project. We need to make sure that we see the "BBB" project before the
121    * "CCC" project, even though "CCC" includes a module that's processed first
122    * if you sort alphabetically by module name (which is the order we see things
123    * inside system_rebuild_module_data() for example).
124    */
125   public function testUpdateContribOrder() {
126     // We want core to be version 8.0.0.
127     $system_info = [
128       '#all' => [
129         'version' => '8.0.0',
130       ],
131       // All the rest should be visible as contrib modules at version 8.x-1.0.
132
133       // aaa_update_test needs to be part of the "CCC Update test" project,
134       // which would throw off the report if we weren't properly sorting by
135       // the project names.
136       'aaa_update_test' => [
137         'project' => 'ccc_update_test',
138         'version' => '8.x-1.0',
139         'hidden' => FALSE,
140       ],
141
142       // This should be its own project, and listed first on the report.
143       'bbb_update_test' => [
144         'project' => 'bbb_update_test',
145         'version' => '8.x-1.0',
146         'hidden' => FALSE,
147       ],
148
149       // This will contain both aaa_update_test and ccc_update_test, and
150       // should come after the bbb_update_test project.
151       'ccc_update_test' => [
152         'project' => 'ccc_update_test',
153         'version' => '8.x-1.0',
154         'hidden' => FALSE,
155       ],
156     ];
157     $this->config('update_test.settings')->set('system_info', $system_info)->save();
158     $this->refreshUpdateStatus(['drupal' => '0.0', '#all' => '1_0']);
159     $this->standardTests();
160     // We're expecting the report to say all projects are up to date.
161     $this->assertText(t('Up to date'));
162     $this->assertNoText(t('Update available'));
163     // We want to see all 3 module names listed, since they'll show up either
164     // as project names or as modules under the "Includes" listing.
165     $this->assertText(t('AAA Update test'));
166     $this->assertText(t('BBB Update test'));
167     $this->assertText(t('CCC Update test'));
168     // We want aaa_update_test included in the ccc_update_test project, not as
169     // its own project on the report.
170     $this->assertNoRaw(\Drupal::l(t('AAA Update test'), Url::fromUri('http://example.com/project/aaa_update_test')), 'Link to aaa_update_test project does not appear.');
171     // The other two should be listed as projects.
172     $this->assertRaw(\Drupal::l(t('BBB Update test'), Url::fromUri('http://example.com/project/bbb_update_test')), 'Link to bbb_update_test project appears.');
173     $this->assertRaw(\Drupal::l(t('CCC Update test'), Url::fromUri('http://example.com/project/ccc_update_test')), 'Link to bbb_update_test project appears.');
174
175     // We want to make sure we see the BBB project before the CCC project.
176     // Instead of just searching for 'BBB Update test' or something, we want
177     // to use the full markup that starts the project entry itself, so that
178     // we're really testing that the project listings are in the right order.
179     $bbb_project_link = '<div class="project-update__title"><a href="http://example.com/project/bbb_update_test">BBB Update test</a>';
180     $ccc_project_link = '<div class="project-update__title"><a href="http://example.com/project/ccc_update_test">CCC Update test</a>';
181     $this->assertTrue(strpos($this->getRawContent(), $bbb_project_link) < strpos($this->getRawContent(), $ccc_project_link), "'BBB Update test' project is listed before the 'CCC Update test' project");
182   }
183
184   /**
185    * Tests that subthemes are notified about security updates for base themes.
186    */
187   public function testUpdateBaseThemeSecurityUpdate() {
188     // @todo https://www.drupal.org/node/2338175 base themes have to be
189     //  installed.
190     // Only install the subtheme, not the base theme.
191     \Drupal::service('theme_handler')->install(['update_test_subtheme']);
192
193     // Define the initial state for core and the subtheme.
194     $system_info = [
195       // We want core to be version 8.0.0.
196       '#all' => [
197         'version' => '8.0.0',
198       ],
199       // Show the update_test_basetheme
200       'update_test_basetheme' => [
201         'project' => 'update_test_basetheme',
202         'version' => '8.x-1.0',
203         'hidden' => FALSE,
204       ],
205       // Show the update_test_subtheme
206       'update_test_subtheme' => [
207         'project' => 'update_test_subtheme',
208         'version' => '8.x-1.0',
209         'hidden' => FALSE,
210       ],
211     ];
212     $this->config('update_test.settings')->set('system_info', $system_info)->save();
213     $xml_mapping = [
214       'drupal' => '0.0',
215       'update_test_subtheme' => '1_0',
216       'update_test_basetheme' => '1_1-sec',
217     ];
218     $this->refreshUpdateStatus($xml_mapping);
219     $this->assertText(t('Security update required!'));
220     $this->assertRaw(\Drupal::l(t('Update test base theme'), Url::fromUri('http://example.com/project/update_test_basetheme')), 'Link to the Update test base theme project appears.');
221   }
222
223   /**
224    * Tests that disabled themes are only shown when desired.
225    *
226    * @todo https://www.drupal.org/node/2338175 extensions can not be hidden and
227    *   base themes have to be installed.
228    */
229   public function testUpdateShowDisabledThemes() {
230     $update_settings = $this->config('update.settings');
231     // Make sure all the update_test_* themes are disabled.
232     $extension_config = $this->config('core.extension');
233     foreach ($extension_config->get('theme') as $theme => $weight) {
234       if (preg_match('/^update_test_/', $theme)) {
235         $extension_config->clear("theme.$theme");
236       }
237     }
238     $extension_config->save();
239
240     // Define the initial state for core and the test contrib themes.
241     $system_info = [
242       // We want core to be version 8.0.0.
243       '#all' => [
244         'version' => '8.0.0',
245       ],
246       // The update_test_basetheme should be visible and up to date.
247       'update_test_basetheme' => [
248         'project' => 'update_test_basetheme',
249         'version' => '8.x-1.1',
250         'hidden' => FALSE,
251       ],
252       // The update_test_subtheme should be visible and up to date.
253       'update_test_subtheme' => [
254         'project' => 'update_test_subtheme',
255         'version' => '8.x-1.0',
256         'hidden' => FALSE,
257       ],
258     ];
259     // When there are contributed modules in the site's file system, the
260     // total number of attempts made in the test may exceed the default value
261     // of update_max_fetch_attempts. Therefore this variable is set very high
262     // to avoid test failures in those cases.
263     $update_settings->set('fetch.max_attempts', 99999)->save();
264     $this->config('update_test.settings')->set('system_info', $system_info)->save();
265     $xml_mapping = [
266       'drupal' => '0.0',
267       'update_test_subtheme' => '1_0',
268       'update_test_basetheme' => '1_1-sec',
269     ];
270     $base_theme_project_link = \Drupal::l(t('Update test base theme'), Url::fromUri('http://example.com/project/update_test_basetheme'));
271     $sub_theme_project_link = \Drupal::l(t('Update test subtheme'), Url::fromUri('http://example.com/project/update_test_subtheme'));
272     foreach ([TRUE, FALSE] as $check_disabled) {
273       $update_settings->set('check.disabled_extensions', $check_disabled)->save();
274       $this->refreshUpdateStatus($xml_mapping);
275       // In neither case should we see the "Themes" heading for installed
276       // themes.
277       $this->assertNoText(t('Themes'));
278       if ($check_disabled) {
279         $this->assertText(t('Uninstalled themes'));
280         $this->assertRaw($base_theme_project_link, 'Link to the Update test base theme project appears.');
281         $this->assertRaw($sub_theme_project_link, 'Link to the Update test subtheme project appears.');
282       }
283       else {
284         $this->assertNoText(t('Uninstalled themes'));
285         $this->assertNoRaw($base_theme_project_link, 'Link to the Update test base theme project does not appear.');
286         $this->assertNoRaw($sub_theme_project_link, 'Link to the Update test subtheme project does not appear.');
287       }
288     }
289   }
290
291   /**
292    * Tests updates with a hidden base theme.
293    */
294   public function testUpdateHiddenBaseTheme() {
295     module_load_include('compare.inc', 'update');
296
297     // Install the subtheme.
298     \Drupal::service('theme_handler')->install(['update_test_subtheme']);
299
300     // Add a project and initial state for base theme and subtheme.
301     $system_info = [
302       // Hide the update_test_basetheme.
303       'update_test_basetheme' => [
304         'project' => 'update_test_basetheme',
305         'hidden' => TRUE,
306       ],
307       // Show the update_test_subtheme.
308       'update_test_subtheme' => [
309         'project' => 'update_test_subtheme',
310         'hidden' => FALSE,
311       ],
312     ];
313     $this->config('update_test.settings')->set('system_info', $system_info)->save();
314     $projects = \Drupal::service('update.manager')->getProjects();
315     $theme_data = \Drupal::service('theme_handler')->rebuildThemeData();
316     $project_info = new ProjectInfo();
317     $project_info->processInfoList($projects, $theme_data, 'theme', TRUE);
318
319     $this->assertTrue(!empty($projects['update_test_basetheme']), 'Valid base theme (update_test_basetheme) was found.');
320   }
321
322   /**
323    * Makes sure that if we fetch from a broken URL, sane things happen.
324    */
325   public function testUpdateBrokenFetchURL() {
326     $system_info = [
327       '#all' => [
328         'version' => '8.0.0',
329       ],
330       'aaa_update_test' => [
331         'project' => 'aaa_update_test',
332         'version' => '8.x-1.0',
333         'hidden' => FALSE,
334       ],
335       'bbb_update_test' => [
336         'project' => 'bbb_update_test',
337         'version' => '8.x-1.0',
338         'hidden' => FALSE,
339       ],
340       'ccc_update_test' => [
341         'project' => 'ccc_update_test',
342         'version' => '8.x-1.0',
343         'hidden' => FALSE,
344       ],
345     ];
346     $this->config('update_test.settings')->set('system_info', $system_info)->save();
347
348     // Ensure that the update information is correct before testing.
349     $this->drupalGet('admin/reports/updates');
350
351     $xml_mapping = [
352       'drupal' => '0.0',
353       'aaa_update_test' => '1_0',
354       'bbb_update_test' => 'does-not-exist',
355       'ccc_update_test' => '1_0',
356     ];
357     $this->refreshUpdateStatus($xml_mapping);
358
359     $this->assertText(t('Up to date'));
360     // We're expecting the report to say most projects are up to date, so we
361     // hope that 'Up to date' is not unique.
362     $this->assertNoUniqueText(t('Up to date'));
363     // It should say we failed to get data, not that we're missing an update.
364     $this->assertNoText(t('Update available'));
365
366     // We need to check that this string is found as part of a project row, not
367     // just in the "Failed to get available update data" message at the top of
368     // the page.
369     $this->assertRaw('<div class="project-update__status">' . t('Failed to get available update data'));
370
371     // We should see the output messages from fetching manually.
372     $this->assertUniqueText(t('Checked available update data for 3 projects.'));
373     $this->assertUniqueText(t('Failed to get available update data for one project.'));
374
375     // The other two should be listed as projects.
376     $this->assertRaw(\Drupal::l(t('AAA Update test'), Url::fromUri('http://example.com/project/aaa_update_test')), 'Link to aaa_update_test project appears.');
377     $this->assertNoRaw(\Drupal::l(t('BBB Update test'), Url::fromUri('http://example.com/project/bbb_update_test')), 'Link to bbb_update_test project does not appear.');
378     $this->assertRaw(\Drupal::l(t('CCC Update test'), Url::fromUri('http://example.com/project/ccc_update_test')), 'Link to bbb_update_test project appears.');
379   }
380
381   /**
382    * Checks that hook_update_status_alter() works to change a status.
383    *
384    * We provide the same external data as if aaa_update_test 8.x-1.0 were
385    * installed and that was the latest release. Then we use
386    * hook_update_status_alter() to try to mark this as missing a security
387    * update, then assert if we see the appropriate warnings on the right pages.
388    */
389   public function testHookUpdateStatusAlter() {
390     $update_test_config = $this->config('update_test.settings');
391     $update_admin_user = $this->drupalCreateUser(['administer site configuration', 'administer software updates']);
392     $this->drupalLogin($update_admin_user);
393
394     $system_info = [
395       '#all' => [
396         'version' => '8.0.0',
397       ],
398       'aaa_update_test' => [
399         'project' => 'aaa_update_test',
400         'version' => '8.x-1.0',
401         'hidden' => FALSE,
402       ],
403     ];
404     $update_test_config->set('system_info', $system_info)->save();
405     $update_status = [
406       'aaa_update_test' => [
407         'status' => UPDATE_NOT_SECURE,
408       ],
409     ];
410     $update_test_config->set('update_status', $update_status)->save();
411     $this->refreshUpdateStatus(
412       [
413         'drupal' => '0.0',
414         'aaa_update_test' => '1_0',
415       ]
416     );
417     $this->drupalGet('admin/reports/updates');
418     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
419     $this->assertText(t('Security update required!'));
420     $this->assertRaw(\Drupal::l(t('AAA Update test'), Url::fromUri('http://example.com/project/aaa_update_test')), 'Link to aaa_update_test project appears.');
421
422     // Visit the reports page again without the altering and make sure the
423     // status is back to normal.
424     $update_test_config->set('update_status', [])->save();
425     $this->drupalGet('admin/reports/updates');
426     $this->assertRaw('<h3>' . t('Modules') . '</h3>');
427     $this->assertNoText(t('Security update required!'));
428     $this->assertRaw(\Drupal::l(t('AAA Update test'), Url::fromUri('http://example.com/project/aaa_update_test')), 'Link to aaa_update_test project appears.');
429
430     // Turn the altering back on and visit the Update manager UI.
431     $update_test_config->set('update_status', $update_status)->save();
432     $this->drupalGet('admin/modules/update');
433     $this->assertText(t('Security update'));
434
435     // Turn the altering back off and visit the Update manager UI.
436     $update_test_config->set('update_status', [])->save();
437     $this->drupalGet('admin/modules/update');
438     $this->assertNoText(t('Security update'));
439   }
440
441 }