From 51393541016052b51d146a985e3c3d1fb8bf25cd Mon Sep 17 00:00:00 2001
From: catch <catch@35733.no-reply.drupal.org>
Date: Fri, 7 Feb 2020 16:14:17 +0000
Subject: [PATCH] Issue #3086644 by Mile23, alexpott, Mixologic,
 greg.1.anderson, jibran: LegacyProject composer templates wrongly reference
 8.x + fix test coverage

---
 composer/Template/LegacyProject/composer.json |  10 +-
 .../Template/RecommendedProject/composer.json |   8 +-
 core/drupalci.yml                             |   4 -
 .../Template/ComposerProjectTemplatesTest.php | 212 ++++++++++++++++++
 .../test_composer_project_templates.sh        |  48 ----
 5 files changed, 221 insertions(+), 61 deletions(-)
 create mode 100644 core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
 delete mode 100755 core/tests/scripts/test_composer_project_templates.sh

diff --git a/composer/Template/LegacyProject/composer.json b/composer/Template/LegacyProject/composer.json
index 995e12bbd060..c7f532dfe61c 100644
--- a/composer/Template/LegacyProject/composer.json
+++ b/composer/Template/LegacyProject/composer.json
@@ -16,13 +16,13 @@
     ],
     "require": {
         "composer/installers": "^1.2",
-        "drupal/core-composer-scaffold": "^8.8",
-        "drupal/core-project-message": "^8.8",
-        "drupal/core-recommended": "^8.8",
-        "drupal/core-vendor-hardening": "^8.8"
+        "drupal/core-composer-scaffold": "^9",
+        "drupal/core-project-message": "^9",
+        "drupal/core-recommended": "^9",
+        "drupal/core-vendor-hardening": "^9"
     },
     "require-dev": {
-        "drupal/core-dev": "^8.8"
+        "drupal/core-dev": "^9"
     },
     "conflict": {
         "drupal/drupal": "*"
diff --git a/composer/Template/RecommendedProject/composer.json b/composer/Template/RecommendedProject/composer.json
index f6d2635af98c..56aa5cdb2ce4 100644
--- a/composer/Template/RecommendedProject/composer.json
+++ b/composer/Template/RecommendedProject/composer.json
@@ -16,12 +16,12 @@
     ],
     "require": {
         "composer/installers": "^1.2",
-        "drupal/core-composer-scaffold": "^8.8",
-        "drupal/core-project-message": "^8.8",
-        "drupal/core-recommended": "^8.8"
+        "drupal/core-composer-scaffold": "^9",
+        "drupal/core-project-message": "^9",
+        "drupal/core-recommended": "^9"
     },
     "require-dev": {
-        "drupal/core-dev": "^8.8"
+        "drupal/core-dev": "^9"
     },
     "conflict": {
         "drupal/drupal": "*"
diff --git a/core/drupalci.yml b/core/drupalci.yml
index 9ce9b2b1c074..8cc5f1d4883e 100644
--- a/core/drupalci.yml
+++ b/core/drupalci.yml
@@ -55,7 +55,3 @@ build:
       # Run nightwatch testing.
       # @see https://www.drupal.org/project/drupal/issues/2869825
       nightwatchjs:
-#      container_command.drupal_project_templates:
-#        commands:
-#          - "sudo -u www-data ${SOURCE_DIR}/core/tests/scripts/test_composer_project_templates.sh"
-#        halt-on-fail: true
diff --git a/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
new file mode 100644
index 000000000000..e9bf83011683
--- /dev/null
+++ b/core/tests/Drupal/BuildTests/Composer/Template/ComposerProjectTemplatesTest.php
@@ -0,0 +1,212 @@
+<?php
+
+namespace Drupal\BuildTests\Composer\Template;
+
+use Composer\Json\JsonFile;
+use Drupal\BuildTests\Framework\BuildTestBase;
+use Drupal\Composer\Composer;
+use Symfony\Component\Finder\Finder;
+
+/**
+ * Demonstrate that Composer project templates are buildable as patched.
+ *
+ * We have to use the packages.json fixture so that Composer will use the
+ * in-codebase version of the project template.
+ *
+ * We also have to add path repositories to the in-codebase project template or
+ * else Composer will try to use packagist to resolve dependencies we'd prefer
+ * it to find locally.
+ *
+ * This is because Composer only uses the packages.json file to resolve the
+ * project template and not any other dependencies.
+ *
+ * @group #slow
+ * @group Template
+ *
+ * @requires externalCommand composer
+ */
+class ComposerProjectTemplatesTest extends BuildTestBase {
+
+  /**
+   * Get Composer items that we want to be path repos, from within a directory.
+   *
+   * @param string $workspace_directory
+   *   The full path to the workspace directory.
+   * @param string $subdir
+   *   The subdirectory to search under composer/.
+   *
+   * @return string[]
+   *   Array of paths, indexed by package name.
+   */
+  public function getPathReposForType($workspace_directory, $subdir) {
+    // Find the Composer items that we want to be path repos.
+    $path_repos = Finder::create()
+      ->files()
+      ->name('composer.json')
+      ->in($workspace_directory . '/composer/' . $subdir);
+
+    $data = [];
+    /* @var $path_repo \SplFileInfo */
+    foreach ($path_repos as $path_repo) {
+      $json_file = new JsonFile($path_repo->getPathname());
+      $json = $json_file->read();
+      $data[$json['name']] = $path_repo->getPath();
+    }
+    return $data;
+  }
+
+  public function provideTemplateCreateProject() {
+    return [
+      'recommended-project' => [
+        'drupal/recommended-project',
+        'composer/Template/RecommendedProject',
+        '/web',
+      ],
+      'legacy-project' => [
+        'drupal/legacy-project',
+        'composer/Template/LegacyProject',
+        '',
+      ],
+    ];
+  }
+
+  /**
+   * Make sure we've accounted for all the templates.
+   */
+  public function testVerifyTemplateTestProviderIsAccurate() {
+    $root = $this->getDrupalRoot();
+    $data = $this->provideTemplateCreateProject($root);
+
+    // Find all the templates.
+    $template_files = Finder::create()
+      ->files()
+      ->name('composer.json')
+      ->in($root . '/composer/Template');
+
+    $this->assertEquals(count($template_files), count($data));
+
+    // We could have the same number of templates but different names.
+    $template_data = [];
+    foreach ($data as $data_name => $data_value) {
+      $template_data[$data_value[0]] = $data_name;
+    }
+    /* @var $file \SplFileInfo */
+    foreach ($template_files as $file) {
+      $json_file = new JsonFile($file->getPathname());
+      $json = $json_file->read();
+      $this->assertArrayHasKey('name', $json);
+      // Assert that the template name is in the project created
+      // from the template.
+      $this->assertArrayHasKey($json['name'], $template_data);
+    }
+  }
+
+  /**
+   * @dataProvider provideTemplateCreateProject
+   */
+  public function testTemplateCreateProject($project, $package_dir, $docroot_dir) {
+    $this->copyCodebase();
+
+    // Get the Drupal core version branch. For instance, this should be
+    // 8.9.x-dev for the 8.9.x branch.
+    $core_version = Composer::drupalVersionBranch();
+
+    // Set up the template to use our path repos. Inclusion of metapackages is
+    // reported differently, so we load up a separate set for them.
+    $metapackage_path_repos = $this->getPathReposForType($this->getWorkspaceDirectory(), 'Metapackage');
+    $path_repos = array_merge($metapackage_path_repos, $this->getPathReposForType($this->getWorkspaceDirectory(), 'Plugin'));
+    // Always add drupal/core as a path repo.
+    $path_repos['drupal/core'] = $this->getWorkspaceDirectory() . '/core';
+    foreach ($path_repos as $name => $path) {
+      $this->executeCommand('composer config repositories.' . $name . ' path ' . $path, $package_dir);
+      $this->assertCommandSuccessful();
+    }
+
+    $repository_path = $this->getWorkspaceDirectory() . '/test_repository/packages.json';
+    $this->makeTestPackage($repository_path, $core_version);
+
+    $autoloader = $this->getWorkspaceDirectory() . '/testproject' . $docroot_dir . '/autoload.php';
+    $this->assertFileNotExists($autoloader);
+
+    $this->executeCommand("COMPOSER_CORE_VERSION=$core_version composer create-project $project testproject $core_version -s dev -vv --repository $repository_path");
+    $this->assertCommandSuccessful();
+
+    // Ensure we used the project from our codebase.
+    $this->assertErrorOutputContains("Installing $project ($core_version): Symlinking from $package_dir");
+    // Ensure that we used drupal/core from our codebase. This probably means
+    // that drupal/core-recommended was added successfully by the project.
+    $this->assertErrorOutputContains("Installing drupal/core ($core_version): Symlinking from");
+    // Verify that there is an autoloader.
+    $this->assertFileExists($autoloader);
+
+    // In order to verify that Composer used the path repos for our project, we
+    // have to get the requirements from the project composer.json so we can
+    // reconcile our expectations.
+    $template_json_file = $this->getWorkspaceDirectory() . '/' . $package_dir . '/composer.json';
+    $this->assertFileExists($template_json_file);
+    $json_file = new JsonFile($template_json_file);
+    $template_json = $json_file->read();
+    // Get the require and require-dev information, and ensure that our
+    // requirements are not erroneously empty.
+    $this->assertNotEmpty(
+      $require = array_merge($template_json['require'] ?? [], $template_json['require-dev'] ?? [])
+    );
+    // Verify that path repo packages were installed.
+    $path_repos = array_keys($path_repos);
+    foreach (array_keys($require) as $package_name) {
+      if (in_array($package_name, $path_repos)) {
+        // Metapackages do not report that they were installed as symlinks, but
+        // we still must check that their installed version matches
+        // COMPOSER_CORE_VERSION.
+        if (array_key_exists($package_name, $metapackage_path_repos)) {
+          $this->assertErrorOutputContains("Installing $package_name ($core_version)");
+        }
+        else {
+          $this->assertErrorOutputContains("Installing $package_name ($core_version): Symlinking from");
+        }
+      }
+    }
+  }
+
+  /**
+   * Creates a test package that points to the templates.
+   *
+   * @param string $repository_path
+   *   The path where to create the test package.
+   * @param string $version
+   *   The version under test.
+   */
+  protected function makeTestPackage($repository_path, $version) {
+    $json = <<<JSON
+{
+  "packages": {
+    "drupal/recommended-project": {
+      "$version": {
+        "name": "drupal/recommended-project",
+        "dist": {
+          "type": "path",
+          "url": "composer/Template/RecommendedProject"
+        },
+        "type": "project",
+        "version": "$version"
+      }
+    },
+    "drupal/legacy-project": {
+      "$version": {
+        "name": "drupal/legacy-project",
+        "dist": {
+          "type": "path",
+          "url": "composer/Template/LegacyProject"
+        },
+        "type": "project",
+        "version": "$version"
+      }
+    }
+  }
+}
+JSON;
+    mkdir(dirname($repository_path));
+    file_put_contents($repository_path, $json);
+  }
+
+}
diff --git a/core/tests/scripts/test_composer_project_templates.sh b/core/tests/scripts/test_composer_project_templates.sh
deleted file mode 100755
index 620f6ad29dd3..000000000000
--- a/core/tests/scripts/test_composer_project_templates.sh
+++ /dev/null
@@ -1,48 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-IFS=$'\n\t'
-
-# @todo: convert to a build test after #2984031 is in.
-
-#/ Usage:       ./drupal_project_templates.sh
-#/ Description: Container command to check default composer templates.
-#/ Options:
-#/   --help: Display this help message
-usage() { grep '^#/' "$0" | cut -c4- ; exit 0 ; }
-expr "$*" : ".*--help" > /dev/null && usage
-
-info()    { echo "[INFO]    $*" ; }
-fatal()   { echo "[FATAL]   $*" ; exit 1 ; }
-
-assertScaffold() {
-  if [ -f $1/autoload.php ]
-  then
-    info "autoload.php file found."
-  else
-    fatal "No autoload.php file found."
-  fi
-  if [ -f $1/core/authorize.php ]
-  then
-    info "authorize.php file found."
-  else
-    fatal "No authorize.php file found."
-  fi
-}
-
-info "Starting script"
-
-SOURCE_DIR=$(realpath $(dirname $0))/../../..
-
-info "Installing recommended project composer template"
-composer --working-dir="${SOURCE_DIR}/composer/Template/RecommendedProject" config repositories.scaffold path '../../Plugin/Scaffold'
-composer --working-dir="${SOURCE_DIR}/composer/Template/RecommendedProject" install --no-suggest --no-progress --no-interaction
-info "Recommended project composer template installed successfully."
-assertScaffold "${SOURCE_DIR}/composer/Template/RecommendedProject/web"
-
-info "Installing legacy project composer template"
-composer --working-dir="${SOURCE_DIR}/composer/Template/LegacyProject" config repositories.scaffold path '../../Plugin/Scaffold'
-composer --working-dir="${SOURCE_DIR}/composer/Template/LegacyProject" install --no-suggest --no-progress --no-interaction
-info "Legacy project composer template installed successfully."
-assertScaffold "${SOURCE_DIR}/composer/Template/LegacyProject"
-
-info "Script complete!"
-- 
GitLab