Skip to content
Snippets Groups Projects
Commit fb30485b authored by Ted Bowman's avatar Ted Bowman Committed by Adam G-H
Browse files

Issue #3250136 by phenaproxima, tedbow: Allow package_manager_bypass to record...

Issue #3250136 by phenaproxima, tedbow: Allow package_manager_bypass to record method invocations for functional tests
parent 5d1aa914
No related branches found
No related tags found
1 merge request!122Issue #3250136: Allow package_manager_bypass to record calls for functional test
......@@ -8,12 +8,13 @@ use PhpTuf\ComposerStager\Domain\Output\ProcessOutputCallbackInterface;
/**
* Defines an update beginner which doesn't do anything.
*/
class Beginner implements BeginnerInterface {
class Beginner extends InvocationRecorderBase implements BeginnerInterface {
/**
* {@inheritdoc}
*/
public function begin(string $activeDir, string $stagingDir, ?array $exclusions = [], ?ProcessOutputCallbackInterface $callback = NULL, ?int $timeout = 120): void {
$this->saveInvocationArguments($activeDir, $stagingDir, $exclusions);
}
}
......@@ -8,7 +8,7 @@ use PhpTuf\ComposerStager\Domain\Output\ProcessOutputCallbackInterface;
/**
* Defines an update committer which doesn't do any actual committing.
*/
class Committer implements CommitterInterface {
class Committer extends InvocationRecorderBase implements CommitterInterface {
/**
* The decorated committer service.
......@@ -31,6 +31,7 @@ class Committer implements CommitterInterface {
* {@inheritdoc}
*/
public function commit(string $stagingDir, string $activeDir, ?array $exclusions = [], ?ProcessOutputCallbackInterface $callback = NULL, ?int $timeout = 120): void {
$this->saveInvocationArguments($activeDir, $stagingDir, $exclusions);
}
/**
......
<?php
namespace Drupal\package_manager_bypass;
/**
* Records information about method invocations.
*
* This can be used by functional tests to ensure that the bypassed Composer
* Stager services were called as expected. Kernel and unit tests should use
* regular mocks instead.
*/
abstract class InvocationRecorderBase {
/**
* Returns the arguments from every invocation of the main class method.
*
* @return array[]
* The arguments from every invocation of the main class method.
*/
public function getInvocationArguments(): array {
return \Drupal::state()->get(static::class, []);
}
/**
* Records the arguments from an invocation of the main class method.
*
* @param mixed ...$arguments
* The arguments that the main class method was called with.
*/
protected function saveInvocationArguments(...$arguments) {
$invocations = $this->getInvocationArguments();
$invocations[] = $arguments;
\Drupal::state()->set(static::class, $invocations);
}
}
......@@ -8,12 +8,13 @@ use PhpTuf\ComposerStager\Domain\StagerInterface;
/**
* Defines an update stager which doesn't actually do anything.
*/
class Stager implements StagerInterface {
class Stager extends InvocationRecorderBase implements StagerInterface {
/**
* {@inheritdoc}
*/
public function stage(array $composerCommand, string $stagingDir, ?ProcessOutputCallbackInterface $callback = NULL, ?int $timeout = 120): void {
$this->saveInvocationArguments($composerCommand, $stagingDir);
}
}
......@@ -131,6 +131,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
$release_notes = $assert_session->elementExists('named', ['link', 'Release notes'], $cells[2]);
$this->assertSame('Release notes for Drupal', $release_notes->getAttribute('title'));
$assert_session->buttonExists('Update');
$this->assertUpdateStagedTimes(0);
}
/**
......@@ -184,8 +185,10 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
$assert_session->pageTextNotContains(static::$warningsExplanation);
$page->pressButton('Update');
$this->checkForMetaRefresh();
// If a validator flags an error, but doesn't throw, the update should still
// be halted.
$this->assertUpdateStagedTimes(0);
$assert_session->pageTextContainsOnce('An error has occurred.');
$page->clickLink('the error page');
$assert_session->pageTextContainsOnce((string) $expected_results[0]->getMessages()[0]);
......@@ -199,6 +202,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
TestChecker1::setTestResult($expected_results, PreCreateEvent::class);
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(0);
$assert_session->pageTextContainsOnce('An error has occurred.');
$page->clickLink('the error page');
// Since there's only one message, we shouldn't see the summary.
......@@ -240,6 +244,7 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
$this->drupalGet('/admin/modules/automatic-update');
$page->pressButton('Update');
$this->checkForMetaRefresh();
$this->assertUpdateStagedTimes(1);
// Confirm we are on the confirmation page.
$assert_session->addressEquals('/admin/automatic-update-ready');
......@@ -260,7 +265,28 @@ class UpdaterFormTest extends AutomaticUpdatesFunctionalTestBase {
// Confirm we are on the confirmation page.
$assert_session->addressEquals('/admin/automatic-update-ready');
$this->assertUpdateStagedTimes(2);
$assert_session->buttonExists('Continue');
}
/**
* Asserts the number of times an update was staged.
*
* @param int $attempted_times
* The expected number of times an update was staged.
*/
private function assertUpdateStagedTimes(int $attempted_times): void {
/** @var \Drupal\package_manager_bypass\InvocationRecorderBase $beginner */
$beginner = $this->container->get('package_manager.beginner');
$this->assertCount($attempted_times, $beginner->getInvocationArguments());
/** @var \Drupal\package_manager_bypass\InvocationRecorderBase $stager */
$stager = $this->container->get('package_manager.stager');
$this->assertCount($attempted_times, $stager->getInvocationArguments());
/** @var \Drupal\package_manager_bypass\InvocationRecorderBase $committer */
$committer = $this->container->get('package_manager.committer');
$this->assertEmpty($committer->getInvocationArguments());
}
}
......@@ -4,8 +4,6 @@ namespace Drupal\Tests\automatic_updates\Kernel;
use Drupal\package_manager\PathLocator;
use Drupal\Tests\user\Traits\UserCreationTrait;
use PhpTuf\ComposerStager\Domain\StagerInterface;
use Prophecy\Argument;
/**
* @coversDefaultClass \Drupal\automatic_updates\Updater
......@@ -71,27 +69,32 @@ class UpdaterTest extends AutomaticUpdatesKernelTestBase {
// When we call Updater::stage(), the stored project versions should be
// read from state and passed to Composer Stager's Stager service, in the
// form of a Composer command. We set up a mock here to ensure that those
// calls are made as expected.
$stager = $this->prophesize(StagerInterface::class);
// form of a Composer command. This is done using package_manager_bypass's
// invocation recorder, rather than a regular mock, in order to test that
// the invocation recorder itself works.
// The production dependencies should be updated first...
$command = [
$expected_require_arguments = [
'require',
'drupal/core-recommended:9.8.1',
'--update-with-all-dependencies',
];
$stager->stage($command, Argument::cetera())->shouldBeCalled();
// ...followed by the dev dependencies.
$command = [
$expected_require_dev_arguments = [
'require',
'drupal/core-dev:9.8.1',
'--update-with-all-dependencies',
'--dev',
];
$stager->stage($command, Argument::cetera())->shouldBeCalled();
$this->container->set('package_manager.stager', $stager->reveal());
$this->container->get('automatic_updates.updater')->stage();
/** @var \Drupal\package_manager_bypass\InvocationRecorderBase $stager */
$stager = $this->container->get('package_manager.stager');
[
$actual_require_arguments,
$actual_require_dev_arguments,
] = $stager->getInvocationArguments();
$this->assertSame($expected_require_arguments, $actual_require_arguments[0]);
$this->assertSame($expected_require_dev_arguments, $actual_require_dev_arguments[0]);
}
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment