UpdateScriptTest.php 16.9 KB
Newer Older
1
2
3
4
<?php

/**
 * @file
5
 * Contains \Drupal\system\Tests\Update\UpdateScriptTest.
6
7
8
9
 */

namespace Drupal\system\Tests\Update;

10
use Drupal\Core\Url;
11
use Drupal\language\Entity\ConfigurableLanguage;
12
13
14
use Drupal\simpletest\WebTestBase;

/**
15
16
17
 * Tests the update script access and functionality.
 *
 * @group Update
18
19
 */
class UpdateScriptTest extends WebTestBase {
20
21
22
23
24
25

  /**
   * Modules to enable.
   *
   * @var array
   */
26
  public static $modules = array('update_script_test', 'dblog', 'language');
27

28
29
30
  /**
   * {@inheritdoc}
   */
31
32
  protected $dumpHeaders = TRUE;

33
34
35
36
37
38
39
40
41
42
43
44
45
  /**
   * URL to the update.php script.
   *
   * @var string
   */
  private $updateUrl;

  /**
   * A user with the necessary permissions to administer software updates.
   *
   * @var \Drupal\user\UserInterface
   */
  private $updateUser;
46

47
  protected function setUp() {
48
    parent::setUp();
49
    $this->updateUrl = Url::fromRoute('system.db_update');
50
    $this->updateUser = $this->drupalCreateUser(array('administer software updates', 'access site in maintenance mode'));
51
    \Drupal::service('entity.definition_update_manager')->applyUpdates();
52
53
  }

54
55
56
57
58
59
60
  /**
   * Tests access to the update script.
   */
  function testUpdateAccess() {
    // Try accessing update.php without the proper permission.
    $regular_user = $this->drupalCreateUser();
    $this->drupalLogin($regular_user);
61
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
62
63
64
65
    $this->assertResponse(403);

    // Try accessing update.php as an anonymous user.
    $this->drupalLogout();
66
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
67
68
69
    $this->assertResponse(403);

    // Access the update page with the proper permission.
70
71
    $this->drupalLogin($this->updateUser);
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
72
73
74
    $this->assertResponse(200);

    // Access the update page as user 1.
75
    $this->drupalLogin($this->rootUser);
76
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
77
78
79
80
81
82
83
    $this->assertResponse(200);
  }

  /**
   * Tests that requirements warnings and errors are correctly displayed.
   */
  function testRequirements() {
84
    $update_script_test_config = $this->config('update_script_test.settings');
85
    $this->drupalLogin($this->updateUser);
86
87
88

    // If there are no requirements warnings or errors, we expect to be able to
    // go through the update process uninterrupted.
89
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
90
    $this->clickLink(t('Continue'));
91
    $this->assertText(t('No pending updates.'), 'End of update process was reached.');
92
93
94
95
96
97
98
99
100
    // Confirm that all caches were cleared.
    $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared when there were no requirements warnings or errors.');

    // If there is a requirements warning, we expect it to be initially
    // displayed, but clicking the link to proceed should allow us to go
    // through the rest of the update process uninterrupted.

    // First, run this test with pending updates to make sure they can be run
    // successfully.
101
    $update_script_test_config->set('requirement_type', REQUIREMENT_WARNING)->save();
102
    drupal_set_installed_schema_version('update_script_test', drupal_get_installed_schema_version('update_script_test') - 1);
103
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
104
105
106
    $this->assertText('This is a requirements warning provided by the update_script_test module.');
    $this->clickLink('try again');
    $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
107
108
    $this->clickLink(t('Continue'));
    $this->clickLink(t('Apply pending updates'));
109
    $this->assertText(t('The update_script_test_update_8001() update was executed successfully.'), 'End of update process was reached.');
110
111
112
113
    // Confirm that all caches were cleared.
    $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared after resolving a requirements warning and applying updates.');

    // Now try again without pending updates to make sure that works too.
114
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
115
116
117
    $this->assertText('This is a requirements warning provided by the update_script_test module.');
    $this->clickLink('try again');
    $this->assertNoText('This is a requirements warning provided by the update_script_test module.');
118
    $this->clickLink(t('Continue'));
119
    $this->assertText(t('No pending updates.'), 'End of update process was reached.');
120
121
122
123
124
125
    // Confirm that all caches were cleared.
    $this->assertText(t('hook_cache_flush() invoked for update_script_test.module.'), 'Caches were cleared after applying updates and re-running the script.');

    // If there is a requirements error, it should be displayed even after
    // clicking the link to proceed (since the problem that triggered the error
    // has not been fixed).
126
    $update_script_test_config->set('requirement_type', REQUIREMENT_ERROR)->save();
127
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
128
129
130
131
132
133
134
135
136
137
138
139
    $this->assertText('This is a requirements error provided by the update_script_test module.');
    $this->clickLink('try again');
    $this->assertText('This is a requirements error provided by the update_script_test module.');
  }

  /**
   * Tests the effect of using the update script on the theme system.
   */
  function testThemeSystem() {
    // Since visiting update.php triggers a rebuild of the theme system from an
    // unusual maintenance mode environment, we check that this rebuild did not
    // put any incorrect information about the themes into the database.
140
    $original_theme_data = $this->config('core.extension')->get('theme');
141
142
    $this->drupalLogin($this->updateUser);
    $this->drupalGet($this->updateUrl, ['external' => TRUE]);
143
    $final_theme_data = $this->config('core.extension')->get('theme');
144
    $this->assertEqual($original_theme_data, $final_theme_data, 'Visiting update.php does not alter the information about themes stored in the database.');
145
146
147
148
149
150
151
  }

  /**
   * Tests update.php when there are no updates to apply.
   */
  function testNoUpdateFunctionality() {
    // Click through update.php with 'administer software updates' permission.
152
153
    $this->drupalLogin($this->updateUser);
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
154
    $this->clickLink(t('Continue'));
155
156
    $this->assertText(t('No pending updates.'));
    $this->assertNoLink('Administration pages');
157
    $this->assertNoLinkByHrefInMainRegion('update.php', 0);
158
159
160
161
162
163
    $this->clickLink('Front page');
    $this->assertResponse(200);

    // Click through update.php with 'access administration pages' permission.
    $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages'));
    $this->drupalLogin($admin_user);
164
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
165
    $this->clickLink(t('Continue'));
166
    $this->assertText(t('No pending updates.'));
167
    $this->assertLink('Administration pages');
168
    $this->assertNoLinkByHrefInMainRegion('update.php', 1);
169
170
171
172
173
174
175
176
    $this->clickLink('Administration pages');
    $this->assertResponse(200);
  }

  /**
   * Tests update.php after performing a successful update.
   */
  function testSuccessfulUpdateFunctionality() {
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
    $initial_maintenance_mode = $this->container->get('state')->get('system.maintenance_mode');
    $this->assertFalse($initial_maintenance_mode, 'Site is not in maintenance mode.');
    $this->updateScriptTest($initial_maintenance_mode);
    $final_maintenance_mode = $this->container->get('state')->get('system.maintenance_mode');
    $this->assertEqual($final_maintenance_mode, $initial_maintenance_mode, 'Maintenance mode should not have changed after database updates.');

    // Reset the static cache to ensure we have the most current setting.
    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
    $this->assertEqual($schema_version, 8001, 'update_script_test schema version is 8001 after updating.');

    // Set the installed schema version to one less than the current update.
    drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
    $this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');

    // Click through update.php with 'access administration pages' and
    // 'access site reports' permissions.
    $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages', 'access site reports', 'access site in maintenance mode'));
    $this->drupalLogin($admin_user);
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
    $this->clickLink(t('Continue'));
    $this->clickLink(t('Apply pending updates'));
    $this->assertText('Updates were attempted.');
    $this->assertLink('logged');
    $this->assertLink('Administration pages');
202
    $this->assertNoLinkByHrefInMainRegion('update.php', 1);
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
    $this->clickLink('Administration pages');
    $this->assertResponse(200);
  }

  /**
   * Tests update.php while in maintenance mode.
   */
  function testMaintenanceModeUpdateFunctionality() {
    $this->container->get('state')
      ->set('system.maintenance_mode', TRUE);
    $initial_maintenance_mode = $this->container->get('state')
      ->get('system.maintenance_mode');
    $this->assertTrue($initial_maintenance_mode, 'Site is in maintenance mode.');
    $this->updateScriptTest($initial_maintenance_mode);
    $final_maintenance_mode = $this->container->get('state')
      ->get('system.maintenance_mode');
    $this->assertEqual($final_maintenance_mode, $initial_maintenance_mode, 'Maintenance mode should not have changed after database updates.');
  }

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
 /**
  * Tests perfoming updates with update.php in a multilingual environment.
  */
  function testSuccessfulMultilingualUpdateFunctionality() {
    // Add some custom languages.
    foreach (array('aa', 'bb') as $language_code) {
      ConfigurableLanguage::create(array(
          'id' => $language_code,
          'label' => $this->randomMachineName(),
        ))->save();
     }

    $config = \Drupal::service('config.factory')->getEditable('language.negotiation');
    // Ensure path prefix is used to determine the language.
    $config->set('url.source', 'path_prefix');
    // Ensure that there's a path prefix set for english as well.
    $config->set('url.prefixes.en', 'en');
    $config->save();

    // Reset the static cache to ensure we have the most current setting.
    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
    $this->assertEqual($schema_version, 8001, 'update_script_test schema version is 8001 after updating.');

    // Set the installed schema version to one less than the current update.
    drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
    $this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');

    // Create admin user.
    $admin_user = $this->drupalCreateUser(array('administer software updates', 'access administration pages', 'access site reports', 'access site in maintenance mode', 'administer site configuration'));
    $this->drupalLogin($admin_user);

    // Visit status report page and ensure, that link to update.php has no path prefix set.
    $this->drupalGet('en/admin/reports/status', array('external' => TRUE));
    $this->assertResponse(200);
    $this->assertLinkByHref('/update.php');
    $this->assertNoLinkByHref('en/update.php');

    // Click through update.php with 'access administration pages' and
    // 'access site reports' permissions.
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
    $this->clickLink(t('Continue'));
    $this->clickLink(t('Apply pending updates'));
    $this->assertText('Updates were attempted.');
    $this->assertLink('logged');
    $this->assertLink('Administration pages');
    $this->assertNoLinkByHrefInMainRegion('update.php', 1);
    $this->clickLink('Administration pages');
    $this->assertResponse(200);
  }

273
274
275
276
  /**
   * Helper function to run updates via the browser.
   */
  protected function updateScriptTest($maintenance_mode) {
277
278
279
280
281
282
283
284
    $schema_version = drupal_get_installed_schema_version('update_script_test');
    $this->assertEqual($schema_version, 8001, 'update_script_test is initially installed with schema version 8001.');

    // Set the installed schema version to one less than the current update.
    drupal_set_installed_schema_version('update_script_test', $schema_version - 1);
    $schema_version = drupal_get_installed_schema_version('update_script_test', TRUE);
    $this->assertEqual($schema_version, 8000, 'update_script_test schema version overridden to 8000.');

285
    // Click through update.php with 'administer software updates' permission.
286
    $this->drupalLogin($this->updateUser);
287
288
289
290
291
292
    if ($maintenance_mode) {
      $this->assertText('Operating in maintenance mode.');
    }
    else {
      $this->assertNoText('Operating in maintenance mode.');
    }
293
    $this->drupalGet($this->updateUrl, array('external' => TRUE));
294
295
    $this->clickLink(t('Continue'));
    $this->clickLink(t('Apply pending updates'));
296
297

    // Verify that updates were completed successfully.
298
299
    $this->assertText('Updates were attempted.');
    $this->assertLink('site');
300
301
302
303
304
305
306
    $this->assertText('The update_script_test_update_8001() update was executed successfully.');

    // Verify that no 7.x updates were run.
    $this->assertNoText('The update_script_test_update_7200() update was executed successfully.');
    $this->assertNoText('The update_script_test_update_7201() update was executed successfully.');

    // Verify that there are no links to different parts of the workflow.
307
    $this->assertNoLink('Administration pages');
308
    $this->assertNoLinkByHrefInMainRegion('update.php', 0);
309
    $this->assertNoLink('logged');
310
311

    // Verify the front page can be visited following the upgrade.
312
313
314
    $this->clickLink('Front page');
    $this->assertResponse(200);
  }
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

  /**
   * Returns the Drupal 7 system table schema.
   */
  public function getSystemSchema() {
    return array(
      'description' => "A list of all modules, themes, and theme engines that are or have been installed in Drupal's file system.",
      'fields' => array(
        'filename' => array(
          'description' => 'The path of the primary file for this item, relative to the Drupal root; e.g. modules/node/node.module.',
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
          'default' => '',
        ),
        'name' => array(
          'description' => 'The name of the item; e.g. node.',
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
          'default' => '',
        ),
        'type' => array(
          'description' => 'The type of the item, either module, theme, or theme_engine.',
          'type' => 'varchar',
          'length' => 12,
          'not null' => TRUE,
          'default' => '',
        ),
        'owner' => array(
          'description' => "A theme's 'parent' . Can be either a theme or an engine.",
          'type' => 'varchar',
          'length' => 255,
          'not null' => TRUE,
          'default' => '',
        ),
        'status' => array(
          'description' => 'Boolean indicating whether or not this item is enabled.',
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'bootstrap' => array(
          'description' => "Boolean indicating whether this module is loaded during Drupal's early bootstrapping phase (e.g. even before the page cache is consulted).",
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'schema_version' => array(
          'description' => "The module's database schema version number. -1 if the module is not installed (its tables do not exist); \Drupal::CORE_MINIMUM_SCHEMA_VERSION or the largest N of the module's hook_update_N() function that has either been run or existed when the module was first installed.",
          'type' => 'int',
          'not null' => TRUE,
          'default' => -1,
          'size' => 'small',
        ),
        'weight' => array(
          'description' => "The order in which this module's hooks should be invoked relative to other modules. Equal-weighted modules are ordered by name.",
          'type' => 'int',
          'not null' => TRUE,
          'default' => 0,
        ),
        'info' => array(
          'description' => "A serialized array containing information from the module's .info file; keys can include name, description, package, version, core, dependencies, and php.",
          'type' => 'blob',
          'not null' => FALSE,
        ),
      ),
      'primary key' => array('filename'),
      'indexes' => array(
        'system_list' => array('status', 'bootstrap', 'type', 'weight', 'name'),
        'type_name' => array('type', 'name'),
      ),
    );
  }
389
}