6efe96e19d7757b6e1836ce8ecd06c28aaf695e7
[yaffs-website] / vendor / drush / drush / tests / UpdateDBTest.php
1 <?php
2
3 namespace Unish;
4
5 use Webmozart\PathUtil\Path;
6
7 /**
8  *  @group slow
9  *  @group commands
10  */
11 class UpdateDBTest extends CommandUnishTestCase
12 {
13     use TestModuleHelperTrait;
14
15     protected $pathPostUpdate;
16
17     public function testUpdateDBStatus()
18     {
19         $this->setUpDrupal(1, true);
20         $this->drush('pm:enable', ['devel']);
21         $this->drush('updatedb:status', [], ['format' => 'json']);
22         $out = $this->getOutputFromJSON();
23         $this->assertNull($out);
24
25         // Force a pending update.
26         $this->drush('php-script', ['updatedb_script'], ['script-path' => __DIR__ . '/resources']);
27
28         // Assert that pending hook_update_n appears
29         $this->drush('updatedb:status', [], ['format' => 'json']);
30         $out = $this->getOutputFromJSON('devel_update_8002');
31         $this->assertEquals('Add enforced dependencies to system.menu.devel', trim($out->description));
32
33         // Run hook_update_n
34         $this->drush('updatedb', []);
35
36         // Assert that we ran hook_update_n properly
37         $this->drush('updatedb:status', [], ['format' => 'json']);
38         $out = $this->getOutputFromJSON();
39         $this->assertNull($out);
40
41         // Assure that a pending post-update is reported.
42         $this->pathPostUpdate = $this->getSut() . '/web/modules/unish/devel/devel.post_update.php';
43         copy(__DIR__ . '/resources/devel.post_update.php', $this->pathPostUpdate);
44         $this->drush('updatedb:status', [], ['format' => 'json']);
45         $out = $this->getOutputFromJSON('devel-post-null_op');
46         $this->assertEquals('This is a test of the emergency broadcast system.', trim($out->description));
47     }
48
49     /**
50      * Tests that updatedb command returns properly a failure.
51      */
52     public function testFailedUpdate()
53     {
54         $sites = $this->setUpDrupal(1, true);
55         $options = [
56             'yes' => null,
57             'root' => $this->webroot(),
58             'uri' => key($sites),
59         ];
60         $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8'));
61         $this->drush('pm-enable', ['woot'], $options);
62
63         // Force a pending update.
64         $this->drush('php-script', ['updatedb_script'], ['script-path' => __DIR__ . '/resources']);
65
66         // Force re-run of woot_update_8101().
67         $this->drush('php:eval', array('drupal_set_installed_schema_version("woot", 8100)'), $options);
68
69         // Force re-run of the post-update woot_post_update_failing().
70         $this->forcePostUpdate('woot_post_update_failing', $options);
71
72         // Run updates.
73         $this->drush('updatedb', [], $options, null, null, self::EXIT_ERROR);
74
75         $expected_output = <<<LOG
76  -------- ----------- --------------- -----------------------
77   Module   Update ID   Type            Description
78  -------- ----------- --------------- -----------------------
79   woot     8101        hook_update_n   Good update.
80   woot     8102        hook_update_n   Failing update.
81   woot     8103        hook_update_n   Another good update.
82   woot     failing     post-update     Failing post-update.
83  -------- ----------- --------------- -----------------------
84
85  // Do you wish to run the specified pending updates?: yes.
86 LOG;
87         $this->assertOutputEquals(preg_replace('#  *#', ' ', $this->simplifyOutput($expected_output)));
88
89         $expected_error_output = <<<LOG
90  [notice] Update started: woot_update_8101
91  [notice] This is the update message from woot_update_8101
92  [ok] Update completed: woot_update_8101
93  [notice] Update started: woot_update_8102
94  [error] This is the exception message thrown in woot_update_8102
95  [error] Update failed: woot_update_8102
96  [error] Update aborted by: woot_update_8102
97  [error] Finished performing updates.
98 LOG;
99
100         $this->assertErrorOutputEquals(preg_replace('#  *#', ' ', $this->simplifyOutput($expected_error_output)));
101     }
102
103     /**
104      * Tests that a failed post-update is handled correctly.
105      */
106     public function testFailedPostUpdate()
107     {
108         $sites = $this->setUpDrupal(1, true);
109         $options = [
110             'yes' => null,
111             'root' => $this->webroot(),
112             'uri' => key($sites),
113         ];
114         $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8'));
115         $this->drush('pm-enable', ['woot'], $options);
116
117         // Force re-run of woot_update_8103().
118         $this->drush('php:eval', array('drupal_set_installed_schema_version("woot", 8102)'), $options);
119
120         // Force re-run of post-update hooks.
121         $this->forcePostUpdate('woot_post_update_a', $options);
122         $this->forcePostUpdate('woot_post_update_failing', $options);
123
124         // Run updates.
125         $this->drush('updatedb', [], $options, null, null, self::EXIT_ERROR);
126
127         $expected_output = <<<LOG
128  -------- ----------- --------------- -------------------------
129   Module   Update ID    Type            Description
130  -------- ----------- --------------- -------------------------
131   woot     8103         hook_update_n   Another good update.
132   woot     a            post-update     Successful post-update.
133   woot     failing      post-update     Failing post-update.
134  -------- ----------- --------------- -------------------------
135
136  // Do you wish to run the specified pending updates?: yes.
137 LOG;
138         $this->assertOutputEquals(preg_replace('#  *#', ' ', $this->simplifyOutput($expected_output)));
139
140         $expected_error_output = <<<LOG
141  [notice] Update started: woot_update_8103
142  [notice] This is the update message from woot_update_8103
143  [ok] Update completed: woot_update_8103
144  [notice] Update started: woot_post_update_a
145  [notice] This is the update message from woot_post_update_a
146  [ok] Update completed: woot_post_update_a
147  [notice] Update started: woot_post_update_failing
148  [error]  This is the exception message thrown in woot_post_update_failing
149  [error]  Update failed: woot_post_update_failing
150  [error]  Update aborted by: woot_post_update_failing
151  [error] Finished performing updates.
152 LOG;
153
154         $this->assertErrorOutputEquals(preg_replace('#  *#', ' ', $this->simplifyOutput($expected_error_output)));
155     }
156
157     /**
158      * Tests that the updatedb command works when new services are introduced.
159      *
160      * This is a regression test for a bug that prevented the updatedb command
161      * from running when the update introduces a new module, and introduces a
162      * new service in an existing module that has a dependency on the new
163      * module.
164      *
165      * @see https://github.com/drush-ops/drush/issues/3193
166      * @see https://www.drupal.org/project/drupal/issues/2863986
167      */
168     public function testUpdateModuleWithServiceDependency()
169     {
170         $root = $this->webroot();
171         $sites = $this->setUpDrupal(1, true);
172         $options = [
173             'yes' => null,
174             'root' => $root,
175             'uri' => key($sites),
176             'include' => __DIR__,
177         ];
178         $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8'));
179         $this->drush('pm-enable', ['woot'], $options);
180
181         // Force re-run of the post-update woot_post_update_install_devel().
182         $this->forcePostUpdate('woot_post_update_install_devel', $options);
183
184         // Force a flush of the dependency injection container, so that we can
185         // test that the container can be correctly rebuilt even if new services
186         // are introduced that depend on modules that are not enabled yet.
187         $this->drush('unit-invalidate-container', [], $options);
188
189         // Introduce a new service in the Woot module that depends on a service
190         // in the Devel module (which is not yet enabled).
191         $filename = Path::join($root, 'modules/unish/woot/woot.services.yml');
192         $serviceDefinition = <<<YAML_FRAGMENT
193   woot.depending_service:
194     class: Drupal\woot\DependingService
195     arguments: ['@devel.dumper']
196 YAML_FRAGMENT;
197         file_put_contents($filename, $serviceDefinition, FILE_APPEND);
198
199         $filename = Path::join($root, 'modules/unish/woot/woot.info.yml');
200         $moduleDependency = <<<YAML_FRAGMENT
201 dependencies:
202   - devel
203 YAML_FRAGMENT;
204         file_put_contents($filename, $moduleDependency, FILE_APPEND);
205
206         // Run updates.
207         $this->drush('updatedb');
208
209         // Assert that the updates were run correctly.
210         $this->drush('updatedb:status', [], ['format' => 'json']);
211         $out = $this->getOutputFromJSON();
212         $this->assertNull($out);
213     }
214
215     /**
216      * Tests that updates and post-updated can be executed successfully.
217      */
218     public function testSuccessfulUpdate()
219     {
220         $sites = $this->setUpDrupal(1, true);
221         $options = [
222             'yes' => null,
223             'root' => $this->webroot(),
224             'uri' => key($sites),
225         ];
226         $this->setupModulesForTests(['woot'], Path::join(__DIR__, 'resources/modules/d8'));
227         $this->drush('pm-enable', ['woot'], $options);
228
229         // Force re-run of woot_update_8103() which is expected to be completed successfully.
230         $this->drush('php:eval', array('drupal_set_installed_schema_version("woot", 8102)'), $options);
231
232         // Force re-run of post-update hooks which are expected to be completed successfully.
233         $this->forcePostUpdate('woot_post_update_a', $options);
234         $this->forcePostUpdate('woot_post_update_render', $options);
235
236         // Run updates.
237         $this->drush('updatedb', [], $options, null, null, self::EXIT_SUCCESS);
238
239         $expected_output = <<<LOG
240  -------- ----------- --------------- -------------------------
241   Module   Update ID    Type            Description
242  -------- ----------- --------------- -------------------------
243   woot     8103         hook_update_n   Another good update.
244   woot     a            post-update     Successful post-update.
245   woot     render       post-update     Renders some content.
246  -------- ----------- --------------- -------------------------
247
248  // Do you wish to run the specified pending updates?: yes.
249 LOG;
250         $this->assertOutputEquals(preg_replace('#  *#', ' ', $this->simplifyOutput($expected_output)));
251
252         $expected_error_output = <<<LOG
253  [notice] Update started: woot_update_8103
254  [notice] This is the update message from woot_update_8103
255  [ok] Update completed: woot_update_8103
256  [notice] Update started: woot_post_update_a
257  [notice] This is the update message from woot_post_update_a
258  [ok] Update completed: woot_post_update_a
259  [notice] Update started: woot_post_update_render
260  [ok] Update completed: woot_post_update_render
261  [success] Finished performing updates.
262 LOG;
263
264         $this->assertErrorOutputEquals(preg_replace('#  *#', ' ', $this->simplifyOutput($expected_error_output)));
265     }
266
267     public function tearDown()
268     {
269         $this->recursiveDelete($this->pathPostUpdate, true);
270         parent::tearDown();
271     }
272
273     /**
274      * Forces a post-update hook to run again on the next database update.
275      *
276      * @param string $hook
277      *   The name of the hook that needs to be run again.
278      * @param array $options
279      *   An associative array containing options for the `sql:query` command.
280      */
281     protected function forcePostUpdate($hook, array $options)
282     {
283         $this->drush('sql:query', ["SELECT value FROM key_value WHERE collection = 'post_update' AND name = 'existing_updates'"], $options);
284         $functions = unserialize($this->getOutput());
285         unset($functions[array_search($hook, $functions)]);
286         $functions = serialize($functions);
287         $this->drush('sql:query', ["UPDATE key_value SET value = '$functions' WHERE collection = 'post_update' AND name = 'existing_updates'"], $options);
288     }
289 }