Skip to content
Snippets Groups Projects
Commit 5a0110c4 authored by Yash Rode's avatar Yash Rode Committed by Ted Bowman
Browse files

Issue #3298889 by yash.rode, tedbow: Make...

Issue #3298889 by yash.rode, tedbow: Make \Drupal\package_manager_test_api\ApiController more easily extendable
parent 451a7c34
No related branches found
No related tags found
No related merge requests found
Showing
with 125 additions and 147 deletions
......@@ -4,3 +4,4 @@ type: module
package: Testing
dependencies:
- automatic_updates:automatic_updates_extensions
- automatic_updates:package_manager_test_api
......@@ -4,3 +4,9 @@ automatic_updates_extensions_test_api:
_controller: 'Drupal\automatic_updates_extensions_test_api\ApiController::run'
requirements:
_access: 'TRUE'
automatic_updates_extensions_test_api.finish:
path: '/automatic-updates-extensions-test-api/finish/{id}'
defaults:
_controller: 'Drupal\automatic_updates_extensions_test_api\ApiController::finish'
requirements:
_access: 'TRUE'
......@@ -2,45 +2,19 @@
namespace Drupal\automatic_updates_extensions_test_api;
use Drupal\automatic_updates_extensions\ExtensionUpdater;
use Drupal\Core\Controller\ControllerBase;
use Drupal\package_manager\PathLocator;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Drupal\package_manager_test_api\ApiController as PackageManagerApiController;
/**
* Provides API endpoints to interact with a staging area in functional tests.
*/
class ApiController extends ControllerBase {
/**
* The extension updater.
*
* @var \Drupal\automatic_updates_extensions\ExtensionUpdater
*/
private $extensionUpdater;
class ApiController extends PackageManagerApiController {
/**
* The path locator service.
*
* @var \Drupal\package_manager\PathLocator
*/
private $pathLocator;
/**
* Constructs an ApiController object.
*
* @param \Drupal\automatic_updates_extensions\ExtensionUpdater $extension_updater
* The updater.
* @param \Drupal\package_manager\PathLocator $path_locator
* The path locator service.
* {@inheritdoc}
*/
public function __construct(ExtensionUpdater $extension_updater, PathLocator $path_locator) {
$this->extensionUpdater = $extension_updater;
$this->pathLocator = $path_locator;
}
protected $finishedRoute = 'automatic_updates_extensions_test_api.finish';
/**
* {@inheritdoc}
......@@ -53,36 +27,13 @@ class ApiController extends ControllerBase {
}
/**
* Runs a complete stage life cycle.
*
* Creates a staging area, requires packages into it, applies changes to the
* active directory, and destroys the stage.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request. The runtime and dev dependencies are expected to be in
* either the query string or request body, under the 'runtime' and 'dev'
* keys, respectively. There may also be a 'files_to_return' key, which
* contains an array of file paths, relative to the project root, whose
* contents should be returned in the response.
*
* @return \Symfony\Component\HttpFoundation\JsonResponse
* A JSON response containing an associative array of the contents of the
* files listed in the 'files_to_return' request key. The array will be
* keyed by path, relative to the project root.
* {@inheritdoc}
*/
public function run(Request $request): JsonResponse {
$this->extensionUpdater->begin($request->get('projects', []));
$this->extensionUpdater->stage();
$this->extensionUpdater->apply();
$this->extensionUpdater->postApply();
$this->extensionUpdater->destroy();
$dir = $this->pathLocator->getProjectRoot();
$file_contents = [];
foreach ($request->get('files_to_return', []) as $path) {
$file_contents[$path] = file_get_contents($dir . '/' . $path);
}
return new JsonResponse($file_contents);
protected function createAndApplyStage(Request $request): string {
$id = $this->stage->begin($request->get('projects', []));
$this->stage->stage();
$this->stage->apply();
return $id;
}
}
......@@ -16,12 +16,19 @@ use Symfony\Component\HttpFoundation\Request;
*/
class ApiController extends ControllerBase {
/**
* The route to redirect to after the stage has been applied.
*
* @var string
*/
protected $finishedRoute = 'package_manager_test_api.finish';
/**
* The stage.
*
* @var \Drupal\package_manager\Stage
*/
private $stage;
protected $stage;
/**
* The path locator service.
......@@ -83,14 +90,8 @@ class ApiController extends ControllerBase {
* @see ::finish()
*/
public function run(Request $request): RedirectResponse {
$id = $this->stage->create();
$this->stage->require(
$request->get('runtime', []),
$request->get('dev', [])
);
$this->stage->apply();
$redirect_url = Url::fromRoute('package_manager_test_api.finish')
$id = $this->createAndApplyStage($request);
$redirect_url = Url::fromRoute($this->finishedRoute)
->setRouteParameter('id', $id)
->setOption('query', [
'files_to_return' => $request->get('files_to_return', []),
......@@ -128,4 +129,29 @@ class ApiController extends ControllerBase {
return new JsonResponse($file_contents);
}
/**
* Creates a stage, requires packages into it, and applies the changes.
*
* @param \Symfony\Component\HttpFoundation\Request $request
* The request. The runtime and dev dependencies are expected to be in
* either the query string or request body, under the 'runtime' and 'dev'
* keys, respectively. There may also be a 'files_to_return' key, which
* contains an array of file paths, relative to the project root, whose
* contents should be returned in the response.
*
* @return string
* Unique ID for the stage, which can be used to claim the stage before
* performing other operations on it. Calling code should store this ID for
* as long as the stage needs to exist.
*/
protected function createAndApplyStage(Request $request) : string {
$id = $this->stage->create();
$this->stage->require(
$request->get('runtime', []),
$request->get('dev', [])
);
$this->stage->apply();
return $id;
}
}
......@@ -164,7 +164,7 @@ END;
* @param array $xml_map
* The update XML map, as used by update_test.settings.
*
* @see \Drupal\automatic_updates_test\TestController::metadata()
* @see \Drupal\package_manager_test_release_history\TestController::metadata()
*/
protected function setReleaseMetadata(array $xml_map): void {
foreach ($xml_map as $metadata_file) {
......
automatic_updates_test.update:
path: '/automatic-update-test/update/{to_version}'
defaults:
_controller: '\Drupal\automatic_updates_test\TestController::update'
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
<?php
namespace Drupal\automatic_updates_test;
use Drupal\automatic_updates\Exception\UpdateException;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Render\HtmlResponse;
use Symfony\Component\HttpFoundation\Response;
class TestController extends ControllerBase {
/**
* Performs an in-place update to a given version of Drupal core.
*
* This executes the update immediately, in one request, without using the
* batch system or cron as wrappers.
*
* @param string $to_version
* The version of core to update to.
*
* @return \Symfony\Component\HttpFoundation\Response
* The response object.
*/
public function update(string $to_version): Response {
/** @var \Drupal\automatic_updates\Updater $updater */
$updater = \Drupal::service('automatic_updates.updater');
try {
$updater->begin(['drupal' => $to_version]);
$updater->stage();
$updater->apply();
$updater->postApply();
$updater->destroy();
// The code base has been updated, but as far as the PHP runtime is
// concerned, \Drupal::VERSION refers to the old version, until the next
// request. So check if the updated version is in Drupal.php and return
// a clear indication of whether it's there or not.
$drupal_php = file_get_contents(\Drupal::root() . '/core/lib/Drupal.php');
if (str_contains($drupal_php, "const VERSION = '$to_version';")) {
$content = "$to_version found in Drupal.php";
}
else {
$content = "$to_version not found in Drupal.php";
}
$status = 200;
}
catch (UpdateException $e) {
$messages = [];
foreach ($e->getResults() as $result) {
if ($summary = $result->getSummary()) {
$messages[] = $summary;
}
$messages = array_merge($messages, $result->getMessages());
}
$content = implode('<br />', $messages);
$status = 500;
}
return new HtmlResponse($content, $status);
}
}
name: 'Automatic Updates Test API'
description: 'Provides API endpoints for doing stage operations in functional tests.'
type: module
package: Testing
dependencies:
- automatic_updates:package_manager_test_api
automatic_updates_test_api.update:
path: '/automatic-updates-test-api'
defaults:
_controller: 'Drupal\automatic_updates_test_api\ApiController::run'
requirements:
_access: 'TRUE'
options:
_maintenance_access: TRUE
automatic_updates_test_api.finish:
path: '/automatic-updates-test-api/finish/{id}'
defaults:
_controller: 'Drupal\automatic_updates_test_api\ApiController::finish'
requirements:
_access: 'TRUE'
<?php
namespace Drupal\automatic_updates_test_api;
use Drupal\package_manager_test_api\ApiController as PackageManagerApiController;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
class ApiController extends PackageManagerApiController {
/**
* {@inheritdoc}
*/
protected $finishedRoute = 'automatic_updates_test_api.finish';
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('automatic_updates.updater'),
$container->get('package_manager.path_locator')
);
}
/**
* {@inheritdoc}
*/
protected function createAndApplyStage(Request $request): string {
$id = $this->stage->begin($request->get('projects', []));
$this->stage->stage();
$this->stage->apply();
return $id;
}
}
......@@ -63,20 +63,28 @@ class CoreUpdateTest extends UpdateTestBase {
*/
public function testApi(): void {
$this->createTestProject('RecommendedProject');
$mink = $this->getMink();
$assert_session = $mink->assertSession();
$query = http_build_query([
'projects' => [
'drupal' => '9.8.1',
],
'files_to_return' => [
'web/core/lib/Drupal.php',
],
]);
// Ensure that the update is prevented if the web root and/or vendor
// directories are not writable.
$this->assertReadOnlyFileSystemError('/automatic-update-test/update/9.8.1');
$this->assertReadOnlyFileSystemError("/automatic-updates-test-api?$query");
$mink = $this->getMink();
$session = $mink->getSession();
$session->reload();
$this->assertSame('9.8.1 found in Drupal.php', trim($session->getPage()->getContent()));
$mink->assertSession()->statusCodeEquals(200);
$file_contents = $session->getPage()->getContent();
$file_contents = json_decode($file_contents, TRUE, 512, JSON_THROW_ON_ERROR);
$this->assertStringContainsString("const VERSION = '9.8.1';", $file_contents['web/core/lib/Drupal.php']);
// Even though the response is what we expect, assert the status code as
// well, to be extra-certain that there was no kind of server-side error.
$assert_session->statusCodeEquals(200);
$this->assertUpdateSuccessful('9.8.1');
}
......
......@@ -19,7 +19,7 @@ abstract class UpdateTestBase extends TemplateProjectTestBase {
// Install Automatic Updates, and other modules needed for testing.
$this->installModules([
'automatic_updates',
'automatic_updates_test',
'automatic_updates_test_api',
'automatic_updates_test_cron',
]);
}
......
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