diff --git a/.vortex/.ahoy.yml b/.vortex/.ahoy.yml index 28a8354d3..8ccc0fd30 100644 --- a/.vortex/.ahoy.yml +++ b/.vortex/.ahoy.yml @@ -4,7 +4,7 @@ ahoyapi: v2 commands: install: - name: Install test dependencies. + usage: Install test dependencies. cmd: | [ -d ./tests/vendor ] && rm -rf ./tests/vendor composer --working-dir tests install @@ -18,19 +18,19 @@ commands: yarn --cwd=docs install --frozen-lockfile docs: - name: Start documentation server. + usage: Start documentation server. cmd: | [ ! -d ./docs/node_modules ] && yarn --cwd=docs install --frozen-lockfile yarn --cwd=docs run start docs-serve: - name: Serve built documentation. + usage: Serve built documentation. cmd: | [ ! -d ./docs/node_modules ] && yarn --cwd=docs install --frozen-lockfile yarn --cwd=docs run start build-docs: - name: Build documentation. + usage: Build documentation. cmd: | [ ! -d ./docs/node_modules ] && yarn --cwd=docs install --frozen-lockfile yarn --cwd=docs run build @@ -48,13 +48,13 @@ commands: php -S "localhost:${port}" -t ./docs_combined/build ./docs/.utils/serve-router.php build-installer: - name: Build documentation. + usage: Build documentation. cmd: | [ ! -d ./installer/vendor ] && composer --working-dir installer install composer --working-dir installer build lint: - name: Lint Vortex project. + usage: Lint Vortex project. cmd: | ahoy lint-installer ahoy lint-tests @@ -65,7 +65,7 @@ commands: ahoy lint-ci lint-fix: - name: Fix linting issues in Vortex project. + usage: Fix linting issues in Vortex project. cmd: | ahoy lint-installer-fix ahoy lint-tests-fix @@ -106,13 +106,13 @@ commands: lint-docs: cmd: | yarn --cwd=docs run lint - yarn --cwd=./docs run spellcheck + yarn --cwd=docs run spellcheck lint-docs-fix: cmd: yarn --cwd=docs run lint-fix test: - name: Test Vortex project. + usage: Test Vortex project. cmd: | ahoy test-common ahoy test-docs @@ -127,17 +127,17 @@ commands: test-docs: cmd: | - [ ! -d ./docs/node_modules ] && yarn --cwd=./docs install --frozen-lockfile - yarn --cwd=./docs run test - yarn --cwd=./docs run spellcheck + [ ! -d ./docs/node_modules ] && yarn --cwd=docs install --frozen-lockfile + yarn --cwd=docs run test + yarn --cwd=docs run spellcheck # If there are changes to the snapshots - this command will re-run twice reporting error the first time. update-snapshots: aliases: [us] cmd: | export XDEBUG_MODE=off - composer --working-dir=tests update-snapshots - composer --working-dir=installer update-snapshots -- --jobs=8 + composer --working-dir tests update-snapshots + composer --working-dir installer update-snapshots -- --jobs=8 update-snapshots-install: aliases: [usi] diff --git a/.vortex/installer/src/Command/BuildCommand.php b/.vortex/installer/src/Command/BuildCommand.php index 3bcc3a1b1..c4a5f5887 100644 --- a/.vortex/installer/src/Command/BuildCommand.php +++ b/.vortex/installer/src/Command/BuildCommand.php @@ -201,7 +201,6 @@ protected function showFailureSummary(): void { Tui::line(''); - // Show last 10 lines of output for context. $runner_output = $this->processRunner->getOutput(as_array: TRUE); if (!is_array($runner_output)) { diff --git a/.vortex/installer/src/Command/DestinationAwareTrait.php b/.vortex/installer/src/Command/DestinationAwareTrait.php index b082c53b3..99ff140a9 100644 --- a/.vortex/installer/src/Command/DestinationAwareTrait.php +++ b/.vortex/installer/src/Command/DestinationAwareTrait.php @@ -16,12 +16,7 @@ trait DestinationAwareTrait { * Add the destination option to the command. */ protected function addDestinationOption(): void { - $this->addOption( - 'destination', - 'd', - InputOption::VALUE_REQUIRED, - 'Target directory for the operation. Defaults to current directory.' - ); + $this->addOption('destination', 'd', InputOption::VALUE_REQUIRED, 'Target directory for the operation. Defaults to current directory.'); } /** diff --git a/.vortex/installer/src/Command/InstallCommand.php b/.vortex/installer/src/Command/InstallCommand.php index 6e4994141..0f67813d1 100644 --- a/.vortex/installer/src/Command/InstallCommand.php +++ b/.vortex/installer/src/Command/InstallCommand.php @@ -218,7 +218,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int action: function (): string { $release_prefix = Version::releasePrefix($this->getApplication()->getVersion()); // The staging directory can be pointed at a reused location, and the - // download unpacks into it rather than replacing it, so anything a + // download unpacks into it rather than replacing it. Anything a // previous run left behind would be treated as shipped by this one. $this->fileManager->resetStaging(); $version = $this->getRepositoryDownloader()->download($this->artifact, $this->config->get(Config::TMP), $release_prefix); @@ -304,8 +304,8 @@ function (string $dir, string $ref): void { $this->presenter->footerBuildSkipped(); } - // Cleanup should take place only in case of the successful installation. - // Otherwise, the user should be able to re-run the installer. + // Cleanup runs only on successful installation; after a failure the user + // can re-run the installer. register_shutdown_function([$this, 'cleanup']); return Command::SUCCESS; @@ -448,7 +448,7 @@ public function cleanup(): void { * Get the repository downloader. * * Provides a default RepositoryDownloader instance or returns the injected - * one. This allows tests to inject mocks via setRepositoryDownloader(). + * one. * * @return \DrevOps\VortexInstaller\Downloader\RepositoryDownloader * The repository downloader. @@ -471,7 +471,6 @@ public function setRepositoryDownloader(RepositoryDownloader $repository_downloa * Get the file downloader. * * Provides a default Downloader instance or returns the injected one. - * This allows tests to inject mocks via setFileDownloader(). * * @return \DrevOps\VortexInstaller\Downloader\Downloader * The file downloader. diff --git a/.vortex/installer/src/Downloader/Artifact.php b/.vortex/installer/src/Downloader/Artifact.php index ab239b4be..ab5edae91 100644 --- a/.vortex/installer/src/Downloader/Artifact.php +++ b/.vortex/installer/src/Downloader/Artifact.php @@ -122,7 +122,6 @@ public function isLocal(): bool { * Check if this artifact uses default repository and reference. */ public function isDefault(): bool { - // Check if using default repository (with or without .git). $default_repo_without_git = self::normalizeRepoUrl(RepositoryDownloader::DEFAULT_REPO); $is_default_repo = ($this->repo === RepositoryDownloader::DEFAULT_REPO || $this->repo === $default_repo_without_git); @@ -168,7 +167,6 @@ protected static function parseUri(string $src): array { $src = $matches[1] . '#' . $matches[2]; } - // Try GitHub-specific patterns first. $github_pattern = self::detectGitHubUrlPattern($src); if ($github_pattern !== NULL) { [$repo, $ref] = $github_pattern; @@ -180,7 +178,6 @@ protected static function parseUri(string $src): array { return [$repo, $ref]; } - // Fall back to #ref parsing (standard git reference syntax). if (str_starts_with($src, 'https://') || str_starts_with($src, 'http://')) { if (!preg_match('~^(https?://[^/]+/[^/]+/[^#]+)(?:#(.+))?$~', $src, $matches)) { throw new \RuntimeException(sprintf('Invalid remote repository format: "%s". Use # to specify a reference (e.g., repo.git#tag).', $src)); diff --git a/.vortex/installer/src/Downloader/Downloader.php b/.vortex/installer/src/Downloader/Downloader.php index d0ac9ccbe..0f369e491 100644 --- a/.vortex/installer/src/Downloader/Downloader.php +++ b/.vortex/installer/src/Downloader/Downloader.php @@ -22,8 +22,8 @@ class Downloader { * Constructs a new Downloader instance. * * @param \GuzzleHttp\ClientInterface|null $httpClient - * Optional HTTP client for testing. If not provided, a default Guzzle - * client will be created. + * Optional HTTP client. If not provided, a default Guzzle client will be + * created. */ public function __construct( protected ?ClientInterface $httpClient = new Client(self::CLIENT_OPTIONS), diff --git a/.vortex/installer/src/Downloader/RepositoryDownloader.php b/.vortex/installer/src/Downloader/RepositoryDownloader.php index dfc9a727c..2a1e20333 100644 --- a/.vortex/installer/src/Downloader/RepositoryDownloader.php +++ b/.vortex/installer/src/Downloader/RepositoryDownloader.php @@ -31,11 +31,11 @@ class RepositoryDownloader implements RepositoryDownloaderInterface { * Optional HTTP client for API calls (e.g., discovering releases). * If not provided, a default Guzzle client will be created. * @param \DrevOps\VortexInstaller\Downloader\ArchiverInterface|null $archiver - * Optional Archiver instance for testing. If not provided, a default - * Archiver will be created. + * Optional Archiver instance. If not provided, a default Archiver will be + * created. * @param \DrevOps\VortexInstaller\Utils\Git|null $git - * Optional Git instance for testing. If not provided, will be created - * when needed for local repository operations. + * Optional Git instance. If not provided, will be created when needed for + * local repository operations. * @param \DrevOps\VortexInstaller\Downloader\Downloader|null $fileDownloader * Optional Downloader instance for downloading archive files. * If not provided, a default Downloader will be created. @@ -316,8 +316,6 @@ protected function validateRemoteRefExists(string $repo_url, string $ref): void $options = ['headers' => self::requestHeaders($archive_url), 'http_errors' => FALSE]; try { - // Use HEAD request to check if the archive URL exists without - // downloading. $response = $this->httpClient->request('HEAD', $archive_url, $options); $status_code = $response->getStatusCode(); diff --git a/.vortex/installer/src/Logger/FileLogger.php b/.vortex/installer/src/Logger/FileLogger.php index 792cd1ff4..bf44a5ff9 100644 --- a/.vortex/installer/src/Logger/FileLogger.php +++ b/.vortex/installer/src/Logger/FileLogger.php @@ -145,14 +145,12 @@ public function getDir(): string { protected function buildFilename(string $command, array $args = []): string { $parts = [$command]; - // Only include positional arguments, not options (starting with -). foreach ($args as $arg) { if (!str_starts_with($arg, '-')) { $parts[] = $arg; } } - // Sanitize for use in filename. $name = implode('-', $parts); $name = (string) preg_replace('/[^a-zA-Z0-9\-_]/', '-', $name); $name = (string) preg_replace('/-+/', '-', $name); diff --git a/.vortex/installer/src/Logger/LoggerAwareTrait.php b/.vortex/installer/src/Logger/LoggerAwareTrait.php index 93529652d..8f8909e98 100644 --- a/.vortex/installer/src/Logger/LoggerAwareTrait.php +++ b/.vortex/installer/src/Logger/LoggerAwareTrait.php @@ -17,8 +17,6 @@ trait LoggerAwareTrait { /** * Get the logger. * - * Factory method that returns existing logger or creates new one. - * * @return \DrevOps\VortexInstaller\Logger\FileLoggerInterface * The logger instance. */ @@ -29,8 +27,6 @@ public function getLogger(): FileLoggerInterface { /** * Set the logger. * - * Allows dependency injection for testing. - * * @param \DrevOps\VortexInstaller\Logger\FileLoggerInterface $logger * The logger instance. */ diff --git a/.vortex/installer/src/Prompts/Handlers/CustomModules.php b/.vortex/installer/src/Prompts/Handlers/CustomModules.php index 4ecae50f8..b23df7d98 100644 --- a/.vortex/installer/src/Prompts/Handlers/CustomModules.php +++ b/.vortex/installer/src/Prompts/Handlers/CustomModules.php @@ -113,20 +113,20 @@ public function discover(): null|string|bool|array { * {@inheritdoc} */ public function process(): void { - $selected = $this->getResponseAsArray(); + $v = $this->getResponseAsArray(); $t = $this->tmpDir; $w = $this->webroot; // The search module cannot function without Solr, so remove it from the // selection when the Solr service was not selected. - if (in_array(self::SEARCH, $selected) && isset($this->responses[Services::id()])) { + if (in_array(self::SEARCH, $v) && isset($this->responses[Services::id()])) { $services = $this->responses[Services::id()]; if (is_array($services) && !in_array(Services::SOLR, $services)) { - $selected = array_values(array_diff($selected, [self::SEARCH])); + $v = array_values(array_diff($v, [self::SEARCH])); } } - if (!in_array(self::BASE, $selected)) { + if (!in_array(self::BASE, $v)) { File::removeTokenAsync('CUSTOM_MODULE_BASE'); $locations = [ @@ -144,7 +144,7 @@ public function process(): void { } } - if (!in_array(self::DEMO, $selected)) { + if (!in_array(self::DEMO, $v)) { File::removeTokenAsync('CUSTOM_MODULE_DEMO'); $locations = [ @@ -164,7 +164,7 @@ public function process(): void { self::removeDemoBehatFeatures($t); } - if (!in_array(self::SEARCH, $selected)) { + if (!in_array(self::SEARCH, $v)) { File::removeTokenAsync('CUSTOM_MODULE_SEARCH'); $locations = [ @@ -185,7 +185,7 @@ public function process(): void { // The 'page' content model is shared: the demo module attaches behavior to // the content type and the search tests index content of that type. It is // only removed once neither of them remains. - if (!in_array(self::DEMO, $selected) && !in_array(self::SEARCH, $selected)) { + if (!in_array(self::DEMO, $v) && !in_array(self::SEARCH, $v)) { File::removeTokenAsync('CONTENT_MODEL'); File::remove($t . '/recipes/page'); } diff --git a/.vortex/installer/src/Prompts/Handlers/DeployTypes.php b/.vortex/installer/src/Prompts/Handlers/DeployTypes.php index 6c694311d..bcb6e60ff 100644 --- a/.vortex/installer/src/Prompts/Handlers/DeployTypes.php +++ b/.vortex/installer/src/Prompts/Handlers/DeployTypes.php @@ -79,6 +79,7 @@ public function discover(): null|string|bool|array { if (!empty($types)) { $types = Converter::fromList($types); sort($types); + return $types; } @@ -89,13 +90,13 @@ public function discover(): null|string|bool|array { * {@inheritdoc} */ public function process(): void { - $types = $this->getResponseAsArray(); + $v = $this->getResponseAsArray(); $t = $this->tmpDir; - if (!empty($types)) { - Env::writeValueDotenv('VORTEX_DEPLOY_TYPES', Converter::toList($types), $t . '/.env'); + if (!empty($v)) { + Env::writeValueDotenv('VORTEX_DEPLOY_TYPES', Converter::toList($v), $t . '/.env'); - if (!in_array(self::ARTIFACT, $types)) { + if (!in_array(self::ARTIFACT, $v)) { File::remove($t . '/.gitignore.deployment'); File::remove($t . '/.gitignore.artifact'); } diff --git a/.vortex/installer/src/Prompts/Handlers/Dotenv.php b/.vortex/installer/src/Prompts/Handlers/Dotenv.php index ec94e618a..4fad17b53 100644 --- a/.vortex/installer/src/Prompts/Handlers/Dotenv.php +++ b/.vortex/installer/src/Prompts/Handlers/Dotenv.php @@ -16,7 +16,6 @@ public function label(): string { } public function discover(): null|string|bool|array { - return NULL; } @@ -29,7 +28,6 @@ public function process(): void { Env::writeValueDotenv($name, $value, $t . '/.env'); } } - } } diff --git a/.vortex/installer/src/Prompts/Handlers/HostingProvider.php b/.vortex/installer/src/Prompts/Handlers/HostingProvider.php index 2a54abbd7..928258f2a 100644 --- a/.vortex/installer/src/Prompts/Handlers/HostingProvider.php +++ b/.vortex/installer/src/Prompts/Handlers/HostingProvider.php @@ -55,7 +55,7 @@ public function options(array $responses): ?array { * {@inheritdoc} */ public function default(array $responses): null|string|bool|array { - return 'none'; + return self::NONE; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Internal.php b/.vortex/installer/src/Prompts/Handlers/Internal.php index aba89c6b0..c95b5d266 100644 --- a/.vortex/installer/src/Prompts/Handlers/Internal.php +++ b/.vortex/installer/src/Prompts/Handlers/Internal.php @@ -126,7 +126,6 @@ public function process(): void { } } }); - } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Modules.php b/.vortex/installer/src/Prompts/Handlers/Modules.php index 01b1fd785..3cf545b4c 100644 --- a/.vortex/installer/src/Prompts/Handlers/Modules.php +++ b/.vortex/installer/src/Prompts/Handlers/Modules.php @@ -80,7 +80,7 @@ public function discover(): null|string|bool|array { * {@inheritdoc} */ public function process(): void { - $selected_modules = $this->getResponseAsArray(); + $v = $this->getResponseAsArray(); $all_modules = self::getAvailableModules(); $t = $this->tmpDir; @@ -89,7 +89,7 @@ public function process(): void { $removed_packages = []; foreach (array_keys($all_modules) as $module_name) { - if (!in_array($module_name, $selected_modules)) { + if (!in_array($module_name, $v)) { $removed_packages[] = 'drupal/' . $module_name; File::remove($t . '/' . $w . '/sites/default/includes/modules/settings.' . $module_name . '.php'); @@ -126,19 +126,19 @@ public function process(): void { } // The only scenario in the demo pages feature asserts Testmode filtering, - // so the feature does not survive without the module. It is not named after + // so the feature has no purpose without the module. It is not named after // the module, so the removal above does not cover it. - if (!in_array('testmode', $selected_modules)) { + if (!in_array('testmode', $v)) { File::remove($t . '/tests/behat/features/pages.feature'); } // Without any of the modules it drives, the script has no operations to // perform, so it is removed. - if (count(array_intersect(self::DEV_MODULES, $selected_modules)) === 0) { + if (count(array_intersect(self::DEV_MODULES, $v)) === 0) { File::remove($t . '/scripts/provision-10-enable-dev-modules.sh'); } - if (count($selected_modules) === 0) { + if (count($v) === 0) { File::removeTokenAsync('MODULE'); } } diff --git a/.vortex/installer/src/Prompts/Handlers/NotificationChannels.php b/.vortex/installer/src/Prompts/Handlers/NotificationChannels.php index 24cbbc2e3..6c8ea494a 100644 --- a/.vortex/installer/src/Prompts/Handlers/NotificationChannels.php +++ b/.vortex/installer/src/Prompts/Handlers/NotificationChannels.php @@ -10,17 +10,17 @@ class NotificationChannels extends AbstractHandler { - public const EMAIL = 'email'; + const EMAIL = 'email'; - public const GITHUB = 'github'; + const GITHUB = 'github'; - public const JIRA = 'jira'; + const JIRA = 'jira'; - public const NEWRELIC = 'newrelic'; + const NEWRELIC = 'newrelic'; - public const SLACK = 'slack'; + const SLACK = 'slack'; - public const WEBHOOK = 'webhook'; + const WEBHOOK = 'webhook'; /** * {@inheritdoc} @@ -66,6 +66,7 @@ public function discover(): null|string|bool|array { if (!empty($channels)) { $channels = Converter::fromList($channels); sort($channels); + return $channels; } diff --git a/.vortex/installer/src/Prompts/Handlers/Theme.php b/.vortex/installer/src/Prompts/Handlers/Theme.php index cf6a3fa9d..106bee8ae 100644 --- a/.vortex/installer/src/Prompts/Handlers/Theme.php +++ b/.vortex/installer/src/Prompts/Handlers/Theme.php @@ -147,15 +147,7 @@ public function process(): void { $file_dst = self::findThemeFile($this->destinationDir, $w, $v); - if ( - $this->isInstalled() - && - ( - empty($file_dst) - || - !self::isVortexTheme(dirname($file_dst)) - ) - ) { + if ($this->isInstalled() && (empty($file_dst) || !self::isVortexTheme(dirname($file_dst)))) { $file_tmpl = self::findThemeFile($t, $w); if (!empty($file_tmpl) && is_readable($file_tmpl)) { File::remove(dirname($file_tmpl)); diff --git a/.vortex/installer/src/Prompts/Handlers/Timezone.php b/.vortex/installer/src/Prompts/Handlers/Timezone.php index d82d3bb81..07db93ad3 100644 --- a/.vortex/installer/src/Prompts/Handlers/Timezone.php +++ b/.vortex/installer/src/Prompts/Handlers/Timezone.php @@ -642,14 +642,13 @@ public function default(array $responses): null|string|bool|array { * {@inheritdoc} */ public function discover(): null|string|bool|array { - $value = NULL; - $from_env = Env::getFromDotenv('TZ', $this->destinationDir); + if ($from_env) { return $from_env; } - return $value; + return NULL; } /** diff --git a/.vortex/installer/src/Prompts/Handlers/Tools.php b/.vortex/installer/src/Prompts/Handlers/Tools.php index d910f21dc..76a2e379e 100644 --- a/.vortex/installer/src/Prompts/Handlers/Tools.php +++ b/.vortex/installer/src/Prompts/Handlers/Tools.php @@ -106,12 +106,12 @@ public function discover(): null|string|bool|array { * {@inheritdoc} */ public function process(): void { - $selected_tools = $this->getResponseAsArray(); + $v = $this->getResponseAsArray(); $tools = self::getToolDefinitions('tools'); $groups = self::getToolDefinitions('groups'); - $missing_tools = array_diff_key($tools, array_flip($selected_tools)); + $missing_tools = array_diff_key($tools, array_flip($v)); foreach (array_keys($missing_tools) as $name) { $this->processTool($name); @@ -124,7 +124,7 @@ public function process(): void { // Remove fei: command and its call when all FE tools and custom // theme are absent, as there are no front-end dependencies to install. $fe_all_group = $groups['frontend_all'] ?? NULL; - if ($fe_all_group && isset($fe_all_group['tools']) && !array_intersect($fe_all_group['tools'], $selected_tools)) { + if ($fe_all_group && isset($fe_all_group['tools']) && !array_intersect($fe_all_group['tools'], $v)) { $theme = $this->responses[Theme::id()] ?? NULL; if (in_array($theme, [Theme::OLIVERO, Theme::CLARO, Theme::STARK])) { File::replaceContentInFile($this->tmpDir . '/.ahoy.yml', Replacement::create('ahoy_fei', function (string $content): string { @@ -173,9 +173,9 @@ protected function processTool(string $name): void { protected function processGroup(string $name): void { $config = self::getToolDefinitions('groups')[$name]; - $selected_tools = $this->getResponseAsArray(); + $v = $this->getResponseAsArray(); - if (!isset($config['tools']) || array_intersect($config['tools'], $selected_tools)) { + if (!isset($config['tools']) || array_intersect($config['tools'], $v)) { return; } @@ -389,7 +389,7 @@ public static function getToolDefinitions(string $filter = 'all'): array { $pj->addSubNode('scripts', 'lint-fix', 'npm run lint-css-fix'); }, // A project created before the move to flat config still carries the - // legacy files, which linger unread once the tool is deselected. + // legacy files, and nothing reads them once the tool is deselected. 'files' => ['eslint.config.mjs', '.eslintrc.json', '.eslintignore', '.prettierrc.json', '.prettierignore'], ], diff --git a/.vortex/installer/src/Prompts/Handlers/VisualRegression.php b/.vortex/installer/src/Prompts/Handlers/VisualRegression.php index 6a6a3c7d3..5c14bfffd 100644 --- a/.vortex/installer/src/Prompts/Handlers/VisualRegression.php +++ b/.vortex/installer/src/Prompts/Handlers/VisualRegression.php @@ -75,10 +75,9 @@ public function process(): void { } // The 'diffy' channel in the notify router is gated by - // VORTEX_NOTIFY_CHANNELS so it is inactive unless explicitly - // enabled. The token markers around it in notify are intentionally - // left in place even when VR is off - the channel is shipped as - // part of the vortex-tooling Composer package, not the consumer + // VORTEX_NOTIFY_CHANNELS, so it is inactive unless explicitly enabled. + // Its token markers in notify stay even when VR is off: the channel + // ships in the vortex-tooling Composer package, not the consumer // template, and the marker comments document the block boundary. } diff --git a/.vortex/installer/src/Prompts/InstallerPresenter.php b/.vortex/installer/src/Prompts/InstallerPresenter.php index 39b84a43f..721146a9c 100644 --- a/.vortex/installer/src/Prompts/InstallerPresenter.php +++ b/.vortex/installer/src/Prompts/InstallerPresenter.php @@ -69,8 +69,8 @@ public function header(Artifact $artifact, string $version): void { $logo = Tui::cyan($logo); // Depending on how the installer is run, the version is either the actual - // version or the placeholder (the PHAR packager replaces the placeholder - // with the actual version). + // version or the placeholder. The PHAR packager replaces the placeholder + // with the actual version. if (str_contains($version, 'vortex-installer-version')) { $version = str_replace('@vortex-installer-version@', 'development', $version); } diff --git a/.vortex/installer/src/Prompts/PromptManager.php b/.vortex/installer/src/Prompts/PromptManager.php index 8e3774bfa..7a2056af6 100644 --- a/.vortex/installer/src/Prompts/PromptManager.php +++ b/.vortex/installer/src/Prompts/PromptManager.php @@ -391,8 +391,8 @@ public function runProcessors(): void { * * The answers come from discovery against the destination rather than from * the choices this run collected, so the render reproduces the project's - * current configuration even where this run changes it. That is what makes - * the result comparable to the project's own files. + * current configuration even where this run changes it. This keeps the + * result comparable to the project's own files. * * @param string $dir * Directory holding an unprocessed template download. diff --git a/.vortex/installer/src/Runner/AbstractRunner.php b/.vortex/installer/src/Runner/AbstractRunner.php index 8c7986630..d432b6921 100644 --- a/.vortex/installer/src/Runner/AbstractRunner.php +++ b/.vortex/installer/src/Runner/AbstractRunner.php @@ -255,7 +255,7 @@ protected function parseCommand(string $command): array { if ($current !== '' || $has_content) { if (!$end_of_options_found && $current === '--') { $end_of_options_found = TRUE; - // Add the -- marker to the parts array so it reaches the command. + // Keep the -- marker in the parts so the command receives it. $parts[] = $current; $current = ''; $has_content = FALSE; @@ -335,10 +335,7 @@ protected function quoteArgument(string $argument): string { return "''"; } - // Check if argument needs quoting (contains spaces, quotes, or shell - // special chars). if (preg_match('/[\s"\'\\\\$`!*?#~<>|;&(){}[\]]/', $argument)) { - // Use single quotes and escape any single quotes within. $escaped = str_replace("'", "'\\''", $argument); return "'" . $escaped . "'"; } diff --git a/.vortex/installer/src/Runner/CommandRunner.php b/.vortex/installer/src/Runner/CommandRunner.php index 7befadc31..c0fc98895 100644 --- a/.vortex/installer/src/Runner/CommandRunner.php +++ b/.vortex/installer/src/Runner/CommandRunner.php @@ -36,7 +36,6 @@ public function run(string $command, array $args = [], array $inputs = [], array $input_args = array_merge($args, $inputs); $this->command = $this->buildCommandString($command, $args, $inputs); - // Validate command existence and prepare input (also validated). $symfony_command = $this->application->find($command); $input = new ArrayInput($input_args); diff --git a/.vortex/installer/src/Runner/CommandRunnerAwareTrait.php b/.vortex/installer/src/Runner/CommandRunnerAwareTrait.php index ae78cff74..5de494015 100644 --- a/.vortex/installer/src/Runner/CommandRunnerAwareTrait.php +++ b/.vortex/installer/src/Runner/CommandRunnerAwareTrait.php @@ -17,7 +17,6 @@ trait CommandRunnerAwareTrait { /** * Get the command runner. * - * Factory method that returns existing runner or creates new one. * Requires getApplication() method from Symfony Command class. * * @return \DrevOps\VortexInstaller\Runner\CommandRunner @@ -31,8 +30,6 @@ public function getCommandRunner(): CommandRunner { /** * Set the command runner. * - * Allows dependency injection for testing. - * * @param \DrevOps\VortexInstaller\Runner\CommandRunner $runner * The command runner instance. */ diff --git a/.vortex/installer/src/Runner/ExecutableFinderAwareTrait.php b/.vortex/installer/src/Runner/ExecutableFinderAwareTrait.php index 75044d53c..937c17437 100644 --- a/.vortex/installer/src/Runner/ExecutableFinderAwareTrait.php +++ b/.vortex/installer/src/Runner/ExecutableFinderAwareTrait.php @@ -19,21 +19,16 @@ trait ExecutableFinderAwareTrait { /** * Get the executable finder. * - * Factory method that returns existing finder or creates new one. - * * @return \Symfony\Component\Process\ExecutableFinder * The executable finder instance. */ public function getExecutableFinder(): ExecutableFinder { - $this->executableFinder ??= new ExecutableFinder(); - return $this->executableFinder; + return $this->executableFinder ??= new ExecutableFinder(); } /** * Set the executable finder. * - * Allows dependency injection for testing. - * * @param \Symfony\Component\Process\ExecutableFinder $finder * The executable finder instance. */ diff --git a/.vortex/installer/src/Runner/ProcessRunnerAwareTrait.php b/.vortex/installer/src/Runner/ProcessRunnerAwareTrait.php index 9987059ea..2e6c9ddeb 100644 --- a/.vortex/installer/src/Runner/ProcessRunnerAwareTrait.php +++ b/.vortex/installer/src/Runner/ProcessRunnerAwareTrait.php @@ -17,8 +17,6 @@ trait ProcessRunnerAwareTrait { /** * Get the process runner. * - * Factory method that returns existing runner or creates new one. - * * @return \DrevOps\VortexInstaller\Runner\ProcessRunner * The process runner instance. */ @@ -29,8 +27,6 @@ public function getProcessRunner(): ProcessRunner { /** * Set the process runner. * - * Allows dependency injection for testing. - * * @param \DrevOps\VortexInstaller\Runner\ProcessRunner $runner * The process runner instance. */ diff --git a/.vortex/installer/src/Runner/RunnerInterface.php b/.vortex/installer/src/Runner/RunnerInterface.php index 139695eb0..ef9dc4fa5 100644 --- a/.vortex/installer/src/Runner/RunnerInterface.php +++ b/.vortex/installer/src/Runner/RunnerInterface.php @@ -13,13 +13,13 @@ interface RunnerInterface extends LoggerAwareInterface { // @see https://tldp.org/LDP/abs/html/exitcodes.html - public const EXIT_SUCCESS = 0; + const EXIT_SUCCESS = 0; - public const EXIT_FAILURE = 1; + const EXIT_FAILURE = 1; - public const EXIT_INVALID = 2; + const EXIT_INVALID = 2; - public const EXIT_COMMAND_NOT_FOUND = 127; + const EXIT_COMMAND_NOT_FOUND = 127; /** * Run a command. diff --git a/.vortex/installer/src/Schema/SchemaValidator.php b/.vortex/installer/src/Schema/SchemaValidator.php index 37893fadc..086850de3 100644 --- a/.vortex/installer/src/Schema/SchemaValidator.php +++ b/.vortex/installer/src/Schema/SchemaValidator.php @@ -42,10 +42,7 @@ public function validate(array $config): array { $known_ids = array_keys($this->handlers); foreach (array_keys($normalized) as $key) { if (!in_array($key, $known_ids, TRUE)) { - $errors[] = [ - 'prompt' => $key, - 'message' => sprintf('Unknown prompt "%s".', $key), - ]; + $errors[] = ['prompt' => $key, 'message' => sprintf('Unknown prompt "%s".', $key)]; } } diff --git a/.vortex/installer/src/Task/TaskOutput.php b/.vortex/installer/src/Task/TaskOutput.php index 791f1e3e8..932cc671b 100644 --- a/.vortex/installer/src/Task/TaskOutput.php +++ b/.vortex/installer/src/Task/TaskOutput.php @@ -13,9 +13,7 @@ */ class TaskOutput implements OutputInterface { - public function __construct( - protected OutputInterface $wrapped, - ) { + public function __construct(protected OutputInterface $wrapped) { } /** diff --git a/.vortex/installer/src/Utils/Config.php b/.vortex/installer/src/Utils/Config.php index 101fb99b7..03a0aa464 100644 --- a/.vortex/installer/src/Utils/Config.php +++ b/.vortex/installer/src/Utils/Config.php @@ -100,9 +100,6 @@ public function getDestination(): ?string { return $this->get(self::DESTINATION); } - /** - * Shorthand to get the value of whether install should be quiet. - */ public function isQuiet(): bool { return (bool) $this->get(self::QUIET, FALSE); } diff --git a/.vortex/installer/src/Utils/FileManager.php b/.vortex/installer/src/Utils/FileManager.php index b3c3bcdad..89d92022f 100644 --- a/.vortex/installer/src/Utils/FileManager.php +++ b/.vortex/installer/src/Utils/FileManager.php @@ -80,15 +80,15 @@ public function snapshotTemplate(): void { * Record what the version the project currently runs installed. * * A path the template has stopped shipping altogether is absent from the - * incoming download, so the selection diff alone cannot see it. Rendering - * the project's own version restores it as a candidate, which is what makes - * a file dropped between releases removable rather than permanent. + * incoming download, so the selection diff alone cannot identify it. + * Rendering the version the project runs restores such a path as a + * candidate, so a file dropped between releases is removable rather than + * permanent. * - * The download is rendered rather than hashed as it arrives, resolving the - * token replacements and directory renames that leave the template's own - * files matching nothing in the project. Rendering it as the destination - * has it installed, rather than as this run would install it, is what keeps - * a path this run drops recognisable as template-owned. + * The download is rendered before hashing: token replacements and directory + * renames leave the raw template files matching nothing in the project. It + * is rendered as the destination has it installed, not as this run would + * install it, so a path this run drops stays recognisable as template-owned. * * Failure is not fatal: the recorded reference may no longer resolve, in * which case only the selection diff applies. @@ -214,7 +214,6 @@ public function copyFiles(): void { File::copy($src, $destination); } - // Special case for .env.local as it may exist. if (!file_exists($destination . '/.env.local') && file_exists($destination . '/.env.local.example')) { File::copy($destination . '/.env.local.example', $destination . '/.env.local'); } @@ -232,7 +231,7 @@ public function copyFiles(): void { * as an active feature. A path is only removed when the project's copy still * matches what the template put there: a project that edited the file owns * it, and an edit that cannot be ruled out is treated as one. Only projects - * already running Vortex are pruned at all. + * already running Vortex are pruned. * * @param array $paths * Template-relative paths absent from the staged copy. @@ -286,8 +285,8 @@ protected function removeExcludedPaths(array $paths, array $expected): void { * * The copy overlays the staged content without regard for what the project * put there, so a change the project made to a shipped file is lost. The - * content of all three sides is only available before the overlay, which is - * where the registry has to be built. + * content of all three sides is only available before the overlay, so the + * registry is built first. * * @param string $src * The staged template directory. @@ -437,7 +436,6 @@ public function prepareDemo(Downloader $downloader): array|string { return sprintf('%s is set. Skipping demo database fetch.', Config::IS_DEMO_DB_FETCH_SKIP); } - // Reload variables from destination's .env. Env::putFromDotenv($this->config->getDestination() . '/.env'); $url = Env::get('VORTEX_FETCH_DB_URL'); diff --git a/.vortex/installer/src/Utils/NpmLock.php b/.vortex/installer/src/Utils/NpmLock.php index f023b3df7..15bfd6ac8 100644 --- a/.vortex/installer/src/Utils/NpmLock.php +++ b/.vortex/installer/src/Utils/NpmLock.php @@ -10,7 +10,7 @@ class NpmLock { /** - * Name of the lock file sitting next to a manifest. + * Name of the lock file next to a manifest. */ const FILE = 'package-lock.json'; @@ -25,7 +25,7 @@ class NpmLock { ]; /** - * Bring the lock file next to a manifest back in line with it. + * Reconcile the lock file with the manifest next to it. * * The lock's root entry is reconciled with the manifest's dependency blocks * and every package no longer reachable from a root is dropped. diff --git a/.vortex/installer/src/Utils/OptionsResolver.php b/.vortex/installer/src/Utils/OptionsResolver.php index b18dee8c9..093701459 100644 --- a/.vortex/installer/src/Utils/OptionsResolver.php +++ b/.vortex/installer/src/Utils/OptionsResolver.php @@ -105,11 +105,11 @@ public static function resolve(array $options): array { $config->set(Config::IS_VORTEX_PROJECT, File::contains($config->getDestination() . '/README.md', '/badge\/Vortex-/')); - // Flag to proceed with installation. If FALSE - the installation will only - // print resolved values and will not proceed. + // Flag to proceed with installation. If FALSE, the installation only + // prints the resolved values and does not proceed. $config->set(Config::PROCEED, TRUE); - // Internal flag to enforce DEMO mode. If not set, the demo mode will be + // Internal flag to enforce demo mode. If not set, demo mode is // discovered automatically. if (Env::get(Config::IS_DEMO) !== NULL) { $config->set(Config::IS_DEMO, (bool) Env::get(Config::IS_DEMO)); diff --git a/.vortex/installer/src/Utils/Strings.php b/.vortex/installer/src/Utils/Strings.php index ba2bfa89c..fe1497ad1 100644 --- a/.vortex/installer/src/Utils/Strings.php +++ b/.vortex/installer/src/Utils/Strings.php @@ -7,7 +7,7 @@ class Strings { public static function isAsciiStart(string $string): bool { - return preg_match('/^[\x00-\x7F]/', $string) === 1; + return (bool) preg_match('/^[\x00-\x7F]/', $string); } public static function strlenPlain(string $text): int { @@ -92,7 +92,6 @@ protected static function processDocblock(array $matches): string { $comment_content = $matches[2]; $following_newline = $matches[3] ?? ''; - // Single-line docblocks - return unchanged. if (!str_contains((string) $comment_content, "\n")) { return $full_match; } diff --git a/.vortex/installer/src/Utils/UpdateRegistry.php b/.vortex/installer/src/Utils/UpdateRegistry.php index 442fed144..517b387b5 100644 --- a/.vortex/installer/src/Utils/UpdateRegistry.php +++ b/.vortex/installer/src/Utils/UpdateRegistry.php @@ -167,9 +167,9 @@ protected function renderDiff(string $from, string $to, string $from_label, stri $differ = new Differ(new UnifiedDiffOutputBuilder($header)); $diff = rtrim($differ->diff($from, $to)); - // The template ships Markdown that itself contains fences, and a diff - // renders an unchanged line with a single leading space, which Markdown - // still reads as a closing fence. + // The template ships Markdown that itself contains fences. A diff renders + // an unchanged line with a single leading space, which Markdown still + // reads as a closing fence. preg_match_all('/`{3,}/', $diff, $matches); $fence = str_repeat('`', $matches[0] === [] ? 3 : max(3, max(array_map(strlen(...), $matches[0])) + 1)); diff --git a/.vortex/installer/src/Utils/Validator.php b/.vortex/installer/src/Utils/Validator.php index 11958ca65..0ba0d1585 100644 --- a/.vortex/installer/src/Utils/Validator.php +++ b/.vortex/installer/src/Utils/Validator.php @@ -65,7 +65,6 @@ public static function isGitCommitShaShort(string $value): bool { * @see https://git-scm.com/docs/git-check-ref-format */ public static function isGitRef(string $value): bool { - // Reserved keywords have special meaning. if (in_array($value, ['stable', 'HEAD'], TRUE)) { return TRUE; } diff --git a/.vortex/installer/src/Utils/Yaml.php b/.vortex/installer/src/Utils/Yaml.php index 6811ab82d..3d69acf12 100644 --- a/.vortex/installer/src/Utils/Yaml.php +++ b/.vortex/installer/src/Utils/Yaml.php @@ -40,14 +40,12 @@ public static function collapseEmptyLinesInLiteralBlock(string $content): string ]; } - // Track current literal block state to avoid repeated lookups. $current_block_indent = -1; $current_block_start = -1; for ($i = 0; $i < $line_count; $i++) { $data = $line_data[$i]; - // Update block state when encountering literal block starts. if ($data['is_literal_start'] >= 0) { $current_block_indent = $data['is_literal_start']; $current_block_start = $i; @@ -59,7 +57,6 @@ public static function collapseEmptyLinesInLiteralBlock(string $content): string } if ($data['is_empty']) { - // If not in a literal block, keep the line. if ($current_block_indent < 0) { $result_lines[] = $data['line']; continue; @@ -79,7 +76,6 @@ public static function collapseEmptyLinesInLiteralBlock(string $content): string } } - // Keep line if block ends, skip if within block. if ($block_ends) { $result_lines[] = $data['line']; } diff --git a/.vortex/installer/tests/Functional/Command/BuildCommandTest.php b/.vortex/installer/tests/Functional/Command/BuildCommandTest.php index 17757ccd0..2938f5b46 100644 --- a/.vortex/installer/tests/Functional/Command/BuildCommandTest.php +++ b/.vortex/installer/tests/Functional/Command/BuildCommandTest.php @@ -221,7 +221,6 @@ public static function dataProviderBuildCommand(): \Iterator { TuiOutput::BUILD_EXPORT_DATABASE, ]), ), - ]; // ----------------------------------------------------------------------- // Profile flag scenarios. diff --git a/.vortex/installer/tests/Functional/Command/InstallCommandTest.php b/.vortex/installer/tests/Functional/Command/InstallCommandTest.php index 05be078aa..39b968f43 100644 --- a/.vortex/installer/tests/Functional/Command/InstallCommandTest.php +++ b/.vortex/installer/tests/Functional/Command/InstallCommandTest.php @@ -4,11 +4,11 @@ namespace DrevOps\VortexInstaller\Tests\Functional\Command; -use DrevOps\VortexInstaller\Logger\FileLoggerInterface; use DrevOps\VortexInstaller\Command\BuildCommand; use DrevOps\VortexInstaller\Command\CheckRequirementsCommand; use DrevOps\VortexInstaller\Command\InstallCommand; use DrevOps\VortexInstaller\Downloader\RepositoryDownloader; +use DrevOps\VortexInstaller\Logger\FileLoggerInterface; use DrevOps\VortexInstaller\Prompts\InstallerPresenter; use DrevOps\VortexInstaller\Runner\ProcessRunner; use DrevOps\VortexInstaller\Runner\RunnerInterface; diff --git a/.vortex/installer/tests/Functional/Command/SchemaValidateCommandTest.php b/.vortex/installer/tests/Functional/Command/SchemaValidateCommandTest.php index 6de2bd721..3f4694de8 100644 --- a/.vortex/installer/tests/Functional/Command/SchemaValidateCommandTest.php +++ b/.vortex/installer/tests/Functional/Command/SchemaValidateCommandTest.php @@ -10,8 +10,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use DrevOps\VortexInstaller\Prompts\Handlers\Migration; use DrevOps\VortexInstaller\Prompts\Handlers\Name; -use DrevOps\VortexInstaller\Schema\SchemaValidator; use DrevOps\VortexInstaller\Schema\SchemaGenerator; +use DrevOps\VortexInstaller\Schema\SchemaValidator; use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; diff --git a/.vortex/installer/tests/Functional/FunctionalTestCase.php b/.vortex/installer/tests/Functional/FunctionalTestCase.php index da013988c..dd086cd3a 100644 --- a/.vortex/installer/tests/Functional/FunctionalTestCase.php +++ b/.vortex/installer/tests/Functional/FunctionalTestCase.php @@ -4,6 +4,7 @@ namespace DrevOps\VortexInstaller\Tests\Functional; +use AlexSkrypnyk\File\Replacer\Replacement; use AlexSkrypnyk\PhpunitHelpers\Traits\ApplicationTrait; use AlexSkrypnyk\PhpunitHelpers\Traits\TuiTrait as UpstreamTuiTrait; use DrevOps\VortexInstaller\Command\InstallCommand; @@ -11,7 +12,6 @@ use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\Env; -use AlexSkrypnyk\File\Replacer\Replacement; use DrevOps\VortexInstaller\Utils\File; use DrevOps\VortexInstaller\Utils\Strings; @@ -37,7 +37,7 @@ public static function setUpBeforeClass(): void { * {@inheritdoc} */ protected function tearDown(): void { - static::tuiTearDown(); + static::tuiTeardown(); if (empty(static::$fixtures)) { throw new \RuntimeException('Fixtures directory is not set.'); diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/AiCodeInstructionsHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/AiCodeInstructionsHandlerProcessTest.php index 91c7a578a..ff1ca3dc0 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/AiCodeInstructionsHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/AiCodeInstructionsHandlerProcessTest.php @@ -12,7 +12,7 @@ class AiCodeInstructionsHandlerProcessTest extends AbstractHandlerProcessTestCas public static function dataProviderHandlerProcess(): \Iterator { yield 'ai_instructions_enabled' => [ - static::cw(fn($test): true => $test->prompts[AiCodeInstructions::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[AiCodeInstructions::id()] = TRUE), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertFileExists(static::$sut . '/AGENTS.md'); $test->assertFileExists(static::$sut . '/CLAUDE.md'); @@ -20,7 +20,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'ai_instructions_disabled' => [ - static::cw(fn($test): false => $test->prompts[AiCodeInstructions::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[AiCodeInstructions::id()] = FALSE), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertFileDoesNotExist(static::$sut . '/AGENTS.md'); $test->assertFileDoesNotExist(static::$sut . '/CLAUDE.md'); diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/BaselineHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/BaselineHandlerProcessTest.php index 7c78f0014..df6ac69a1 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/BaselineHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/BaselineHandlerProcessTest.php @@ -4,6 +4,8 @@ namespace DrevOps\VortexInstaller\Tests\Functional\Prompts\Handlers; +use DrevOps\VortexInstaller\Command\InstallCommand; +use DrevOps\VortexInstaller\Downloader\RepositoryDownloader; use DrevOps\VortexInstaller\Prompts\Handlers\AssignAuthorPr; use DrevOps\VortexInstaller\Prompts\Handlers\CiProvider; use DrevOps\VortexInstaller\Prompts\Handlers\CodeProvider; @@ -29,7 +31,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Webroot; use DrevOps\VortexInstaller\Prompts\PromptManager; use DrevOps\VortexInstaller\Utils\Config; -use DrevOps\VortexInstaller\Downloader\RepositoryDownloader; use DrevOps\VortexInstaller\Utils\File; use DrevOps\VortexInstaller\Utils\Git; use DrevOps\VortexInstaller\Utils\Tui; @@ -85,7 +86,7 @@ public static function dataProviderHandlerProcess(): \Iterator { // Test overriding array value. Services::id() => [Services::SOLR, Services::CLAMAV], ])); - $test->installOptions['prompts'] = $prompts_file; + $test->installOptions[InstallCommand::OPTION_PROMPTS] = $prompts_file; }), NULL, ['Welcome to the Vortex non-interactive installer'], @@ -98,7 +99,7 @@ public static function dataProviderHandlerProcess(): \Iterator { // Test overriding array value. Services::id() => [Services::SOLR, Services::REDIS], ]); - $test->installOptions['prompts'] = $prompts_string; + $test->installOptions[InstallCommand::OPTION_PROMPTS] = $prompts_string; }), NULL, ['Welcome to the Vortex non-interactive installer'], diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/CiProviderHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/CiProviderHandlerProcessTest.php index 932b10161..8f421792b 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/CiProviderHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/CiProviderHandlerProcessTest.php @@ -13,7 +13,7 @@ class CiProviderHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'ciprovider_gha' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; $test->prompts[AiCodeInstructions::id()] = TRUE; }), @@ -23,7 +23,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'ciprovider_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; $test->prompts[AiCodeInstructions::id()] = TRUE; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/CodeCoverageProviderHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/CodeCoverageProviderHandlerProcessTest.php index 39b9e46f7..6a47b612f 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/CodeCoverageProviderHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/CodeCoverageProviderHandlerProcessTest.php @@ -13,10 +13,10 @@ class CodeCoverageProviderHandlerProcessTest extends AbstractHandlerProcessTestC public static function dataProviderHandlerProcess(): \Iterator { yield 'code_coverage_provider_codecov' => [ - static::cw(fn($test): string => $test->prompts[CodeCoverageProvider::id()] = CodeCoverageProvider::CODECOV), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[CodeCoverageProvider::id()] = CodeCoverageProvider::CODECOV), ]; yield 'code_coverage_provider_codecov_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[CodeCoverageProvider::id()] = CodeCoverageProvider::CODECOV; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/CodeProviderHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/CodeProviderHandlerProcessTest.php index a4965319e..3d37ec383 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/CodeProviderHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/CodeProviderHandlerProcessTest.php @@ -12,14 +12,14 @@ class CodeProviderHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'code_provider_github' => [ - static::cw(fn($test): string => $test->prompts[CodeProvider::id()] = CodeProvider::GITHUB), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[CodeProvider::id()] = CodeProvider::GITHUB), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertFileDoesNotExist(static::$sut . '/.github/PULL_REQUEST_TEMPLATE.dist.md'); $test->assertFileContainsString(static::$sut . '/.github/PULL_REQUEST_TEMPLATE.md', 'Checklist before requesting a review'); }), ]; yield 'code_provider_other' => [ - static::cw(fn($test): string => $test->prompts[CodeProvider::id()] = CodeProvider::OTHER), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[CodeProvider::id()] = CodeProvider::OTHER), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertDirectoryDoesNotExist(static::$sut . '/.github'); }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/CustomModulesHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/CustomModulesHandlerProcessTest.php index aee4fe972..1f57e072d 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/CustomModulesHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/CustomModulesHandlerProcessTest.php @@ -7,7 +7,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\AiCodeInstructions; use DrevOps\VortexInstaller\Prompts\Handlers\CustomModules; use DrevOps\VortexInstaller\Prompts\Handlers\Services; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(CustomModules::class)] @@ -15,14 +14,14 @@ class CustomModulesHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'custom_modules_no_base' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[CustomModules::id()] = [CustomModules::SEARCH, CustomModules::DEMO]; $test->prompts[AiCodeInstructions::id()] = TRUE; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('_base')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('_base')), ]; yield 'custom_modules_no_demo' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[CustomModules::id()] = [CustomModules::BASE, CustomModules::SEARCH]; $test->prompts[AiCodeInstructions::id()] = TRUE; }), @@ -33,7 +32,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'custom_modules_no_search' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[CustomModules::id()] = [CustomModules::BASE, CustomModules::DEMO]; $test->prompts[AiCodeInstructions::id()] = TRUE; }), @@ -43,7 +42,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'custom_modules_none' => [ - static::cw(fn($test): array => $test->prompts[CustomModules::id()] = []), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[CustomModules::id()] = []), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertSutNotContains('_base'); $test->assertSutNotContains('_demo'); @@ -52,7 +51,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'custom_modules_search_without_solr' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { // Search module selected but Solr service deselected - safety net // should force-remove search module. $test->prompts[CustomModules::id()] = [CustomModules::BASE, CustomModules::SEARCH, CustomModules::DEMO]; diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/DatabaseFetchSourceHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/DatabaseFetchSourceHandlerProcessTest.php index a37980a61..9ca89d124 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/DatabaseFetchSourceHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/DatabaseFetchSourceHandlerProcessTest.php @@ -15,26 +15,26 @@ class DatabaseFetchSourceHandlerProcessTest extends AbstractHandlerProcessTestCa public static function dataProviderHandlerProcess(): \Iterator { yield 'db_fetch_source_url' => [ - static::cw(fn($test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::URL), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::URL), ]; yield 'db_fetch_source_ftp' => [ - static::cw(fn($test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::FTP), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::FTP), ]; yield 'db_fetch_source_acquia' => [ - static::cw(fn($test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::ACQUIA), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::ACQUIA), ]; yield 'db_fetch_source_lagoon' => [ - static::cw(fn($test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::LAGOON), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::LAGOON), ]; yield 'db_fetch_source_container_registry' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::CONTAINER_REGISTRY; $test->prompts[DatabaseImage::id()] = 'the_empire/star_wars:latest'; $test->prompts[AiCodeInstructions::id()] = TRUE; }), ]; yield 'db_fetch_source_s3' => [ - static::cw(fn($test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::S3), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DatabaseFetchSource::id()] = DatabaseFetchSource::S3), ]; } diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/DependencyUpdatesProviderHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/DependencyUpdatesProviderHandlerProcessTest.php index 82d744ba3..7cf6e7c24 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/DependencyUpdatesProviderHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/DependencyUpdatesProviderHandlerProcessTest.php @@ -13,19 +13,19 @@ class DependencyUpdatesProviderHandlerProcessTest extends AbstractHandlerProcess public static function dataProviderHandlerProcess(): \Iterator { yield 'deps_updates_provider_ci_gha' => [ - static::cw(fn($test): string => $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::RENOVATEBOT_CI), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::RENOVATEBOT_CI), ]; yield 'deps_updates_provider_ci_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::RENOVATEBOT_CI; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), ]; yield 'deps_updates_provider_app' => [ - static::cw(fn($test): string => $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::RENOVATEBOT_APP), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::RENOVATEBOT_APP), ]; yield 'deps_updates_provider_none' => [ - static::cw(fn($test): string => $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::NONE), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[DependencyUpdatesProvider::id()] = DependencyUpdatesProvider::NONE), ]; } diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/DeployTypeHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/DeployTypesHandlerProcessTest.php similarity index 53% rename from .vortex/installer/tests/Functional/Prompts/Handlers/DeployTypeHandlerProcessTest.php rename to .vortex/installer/tests/Functional/Prompts/Handlers/DeployTypesHandlerProcessTest.php index e771d938d..113db0329 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/DeployTypeHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/DeployTypesHandlerProcessTest.php @@ -9,32 +9,32 @@ use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(DeployTypes::class)] -class DeployTypeHandlerProcessTest extends AbstractHandlerProcessTestCase { +class DeployTypesHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'deploy_types_artifact' => [ - static::cw(fn($test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::ARTIFACT]), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::ARTIFACT]), ]; yield 'deploy_types_lagoon' => [ - static::cw(fn($test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::LAGOON]), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::LAGOON]), ]; yield 'deploy_types_webhook' => [ - static::cw(fn($test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::WEBHOOK]), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::WEBHOOK]), ]; yield 'deploy_types_all_gha' => [ - static::cw(fn($test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::WEBHOOK, DeployTypes::LAGOON, DeployTypes::ARTIFACT]), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[DeployTypes::id()] = [DeployTypes::WEBHOOK, DeployTypes::LAGOON, DeployTypes::ARTIFACT]), ]; yield 'deploy_types_all_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[DeployTypes::id()] = [DeployTypes::WEBHOOK, DeployTypes::LAGOON, DeployTypes::ARTIFACT]; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), ]; yield 'deploy_types_none_gha' => [ - static::cw(fn($test): array => $test->prompts[DeployTypes::id()] = []), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[DeployTypes::id()] = []), ]; yield 'deploy_types_none_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[DeployTypes::id()] = []; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/DocsHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/DocsHandlerProcessTest.php index c7e0635ec..1c22f5aae 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/DocsHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/DocsHandlerProcessTest.php @@ -12,10 +12,10 @@ class DocsHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'preserve_docs_project_enabled' => [ - static::cw(fn($test): true => $test->prompts[PreserveDocsProject::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[PreserveDocsProject::id()] = TRUE), ]; yield 'preserve_docs_project_disabled' => [ - static::cw(fn($test): false => $test->prompts[PreserveDocsProject::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[PreserveDocsProject::id()] = FALSE), ]; } diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/FrontendBuildHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/FrontendBuildHandlerProcessTest.php index 46ba071f7..987fbdda9 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/FrontendBuildHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/FrontendBuildHandlerProcessTest.php @@ -12,14 +12,14 @@ class FrontendBuildHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'frontend_build_container' => [ - static::cw(fn($test): true => $test->prompts[FrontendBuild::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[FrontendBuild::id()] = TRUE), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertFileContainsString(static::$sut . '/.env', 'VORTEX_FRONTEND_BUILD_SKIP=0'); }), ]; yield 'frontend_build_skip' => [ - static::cw(fn($test): false => $test->prompts[FrontendBuild::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[FrontendBuild::id()] = FALSE), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertFileContainsString(static::$sut . '/.env', 'VORTEX_FRONTEND_BUILD_SKIP=1'); }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/GitleaksHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/GitleaksHandlerProcessTest.php index d0c2bcd44..6c3918628 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/GitleaksHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/GitleaksHandlerProcessTest.php @@ -12,10 +12,10 @@ class GitleaksHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'gitleaks_enabled' => [ - static::cw(fn($test): true => $test->prompts[Gitleaks::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[Gitleaks::id()] = TRUE), ]; yield 'gitleaks_disabled' => [ - static::cw(fn($test): false => $test->prompts[Gitleaks::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[Gitleaks::id()] = FALSE), ]; } diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProjectNameHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProjectNameHandlerProcessTest.php index 5f5423ad0..8bfaae3c8 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProjectNameHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProjectNameHandlerProcessTest.php @@ -4,8 +4,8 @@ namespace DrevOps\VortexInstaller\Tests\Functional\Prompts\Handlers; -use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use DrevOps\VortexInstaller\Prompts\Handlers\HostingProjectName; +use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(HostingProjectName::class)] @@ -13,7 +13,7 @@ class HostingProjectNameHandlerProcessTest extends AbstractHandlerProcessTestCas public static function dataProviderHandlerProcess(): \Iterator { yield 'hosting_project_name___acquia' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[HostingProvider::id()] = HostingProvider::ACQUIA; $test->prompts[HostingProjectName::id()] = 'my_custom_acquia-project'; }), @@ -24,7 +24,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'hosting_project_name___lagoon' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[HostingProvider::id()] = HostingProvider::LAGOON; $test->prompts[HostingProjectName::id()] = 'my_custom_lagoon-project'; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProviderHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProviderHandlerProcessTest.php index 647909f09..f39970153 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProviderHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/HostingProviderHandlerProcessTest.php @@ -6,7 +6,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\AiCodeInstructions; use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(HostingProvider::class)] @@ -14,20 +13,20 @@ class HostingProviderHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'hosting_acquia' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[HostingProvider::id()] = HostingProvider::ACQUIA; $test->prompts[AiCodeInstructions::id()] = TRUE; }), // Cannot assert for the full absence of 'lagoon' since we use Lagoon // images for local and CI even with Acquia. - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('lagoon_')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('lagoon_')), ]; yield 'hosting_lagoon' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[HostingProvider::id()] = HostingProvider::LAGOON; $test->prompts[AiCodeInstructions::id()] = TRUE; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('acquia')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('acquia')), ]; } diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationFetchSourceHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationFetchSourceHandlerProcessTest.php index dd86e3629..f82f9a70e 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationFetchSourceHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationFetchSourceHandlerProcessTest.php @@ -14,7 +14,7 @@ class MigrationFetchSourceHandlerProcessTest extends AbstractHandlerProcessTestC public static function dataProviderHandlerProcess(): \Iterator { yield 'migration_fetch_source_url' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[MigrationFetchSource::id()] = MigrationFetchSource::URL; }), @@ -26,7 +26,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_fetch_source_ftp' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[MigrationFetchSource::id()] = MigrationFetchSource::FTP; }), @@ -38,7 +38,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_fetch_source_acquia' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[MigrationFetchSource::id()] = MigrationFetchSource::ACQUIA; }), @@ -50,7 +50,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_fetch_source_lagoon' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[MigrationFetchSource::id()] = MigrationFetchSource::LAGOON; }), @@ -62,7 +62,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_fetch_source_s3' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[MigrationFetchSource::id()] = MigrationFetchSource::S3; }), @@ -74,7 +74,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_fetch_source_container_registry' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[MigrationFetchSource::id()] = MigrationFetchSource::CONTAINER_REGISTRY; $test->prompts[MigrationImage::id()] = 'the_empire/star_wars-migration:latest'; diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationHandlerProcessTest.php index dd1345d6d..a9e13b7c8 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/MigrationHandlerProcessTest.php @@ -14,7 +14,7 @@ class MigrationHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'migration_enabled' => [ - static::cw(fn($test): true => $test->prompts[Migration::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[Migration::id()] = TRUE), static::cw(function (AbstractHandlerProcessTestCase $test): void { // Files and directories created by the handler. $test->assertFileExists(static::$sut . '/web/sites/default/settings.migration.php'); @@ -35,7 +35,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_enabled_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), @@ -49,7 +49,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_disabled' => [ - static::cw(fn($test): false => $test->prompts[Migration::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[Migration::id()] = FALSE), static::cw(function (AbstractHandlerProcessTestCase $test): void { // Files and directories removed by the handler. $test->assertFileDoesNotExist(static::$sut . '/web/sites/default/settings.migration.php'); @@ -70,7 +70,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_disabled_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = FALSE; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), @@ -84,7 +84,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_enabled_lagoon' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = TRUE; $test->prompts[HostingProvider::id()] = HostingProvider::LAGOON; }), @@ -98,7 +98,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'migration_disabled_lagoon' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Migration::id()] = FALSE; $test->prompts[HostingProvider::id()] = HostingProvider::LAGOON; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/ModulesHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/ModulesHandlerProcessTest.php index 757cdbea1..772855bbd 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/ModulesHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/ModulesHandlerProcessTest.php @@ -5,7 +5,6 @@ namespace DrevOps\VortexInstaller\Tests\Functional\Prompts\Handlers; use DrevOps\VortexInstaller\Prompts\Handlers\Modules; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(Modules::class)] @@ -13,49 +12,49 @@ class ModulesHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'modules_no_coffee' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('coffee'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('coffee')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('coffee')), ]; yield 'modules_no_config_split' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('config_split'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('config_split')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('config_split')), ]; yield 'modules_no_config_update' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('config_update'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('config_update')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('config_update')), ]; yield 'modules_no_devel' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('devel'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/devel')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('drupal/devel')), ]; yield 'modules_no_drupal_helpers' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('drupal_helpers'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/drupal_helpers')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('drupal/drupal_helpers')), ]; yield 'modules_no_environment_indicator' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('environment_indicator'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('environment_indicator')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('environment_indicator')), ]; yield 'modules_no_fast_404' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('fast_404'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('fast_404')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('fast_404')), ]; yield 'modules_no_generated_content' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('generated_content'); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -67,28 +66,28 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_no_navigation_extra_tools' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('navigation_extra_tools'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/navigation_extra_tools')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('drupal/navigation_extra_tools')), ]; yield 'modules_no_pathauto' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('pathauto'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/pathauto')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('drupal/pathauto')), ]; yield 'modules_no_redirect' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('redirect'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'drupal/redirect', 'RedirectTrait', ])), ]; yield 'modules_no_reroute_email' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('reroute_email'); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -100,37 +99,37 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_no_robotstxt' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('robotstxt'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('robotstxt')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('robotstxt')), ]; yield 'modules_no_sdc_devel' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('sdc_devel'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/sdc_devel')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('drupal/sdc_devel')), ]; yield 'modules_no_seckit' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('seckit'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('seckit')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('seckit')), ]; yield 'modules_no_shield' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('shield'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('shield')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('shield')), ]; yield 'modules_no_stage_file_proxy' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('stage_file_proxy'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('stage_file_proxy')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('stage_file_proxy')), ]; yield 'modules_no_testmode' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('testmode'); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -145,23 +144,23 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_no_xmlsitemap' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept('xmlsitemap'); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('drupal/xmlsitemap')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('drupal/xmlsitemap')), ]; yield 'modules_no_seckit_shield_stage_file_proxy' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept(['seckit', 'shield', 'stage_file_proxy']); }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'seckit', 'shield', 'stage_file_proxy', ])), ]; yield 'modules_no_devel_sdc_devel' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept(['devel', 'sdc_devel']); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -173,7 +172,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_no_devel_sdc_devel_generated_content' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept(['devel', 'sdc_devel', 'generated_content']); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -185,7 +184,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_no_devel_sdc_devel_generated_content_testmode' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept(['devel', 'sdc_devel', 'generated_content', 'testmode']); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -197,7 +196,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_no_devel_sdc_devel_generated_content_testmode_reroute_email' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Modules::id()] = static::getModulesExcept(['devel', 'sdc_devel', 'generated_content', 'testmode', 'reroute_email']); }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -206,7 +205,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'modules_none' => [ - static::cw(fn($test): array => $test->prompts[Modules::id()] = []), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[Modules::id()] = []), static::cw(function (AbstractHandlerProcessTestCase $test): void { foreach (array_keys(Modules::getAvailableModules()) as $module) { // Cannot assert by the module name alone, as some module names diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/NamesHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/NamesHandlerProcessTest.php index 24e0c7fac..bb859aae7 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/NamesHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/NamesHandlerProcessTest.php @@ -19,7 +19,7 @@ class NamesHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'names' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Name::id()] = 'New hope'; $test->prompts[MachineName::id()] = 'the_new_hope'; $test->prompts[Org::id()] = 'Jedi Order'; diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/NotificationChannelsHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/NotificationChannelsHandlerProcessTest.php index 5793702d3..d46af110c 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/NotificationChannelsHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/NotificationChannelsHandlerProcessTest.php @@ -12,7 +12,7 @@ class NotificationChannelsHandlerProcessTest extends AbstractHandlerProcessTestC public static function dataProviderHandlerProcess(): \Iterator { yield 'notification_channels_all' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [ NotificationChannels::EMAIL, NotificationChannels::GITHUB, @@ -30,7 +30,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_email_only' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [NotificationChannels::EMAIL]; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -41,7 +41,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_github_only' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [NotificationChannels::GITHUB]; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -52,7 +52,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_jira_only' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [NotificationChannels::JIRA]; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -63,7 +63,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_newrelic_only' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [NotificationChannels::NEWRELIC]; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -74,7 +74,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_slack_only' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [NotificationChannels::SLACK]; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -85,7 +85,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_webhook_only' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[NotificationChannels::id()] = [NotificationChannels::WEBHOOK]; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -96,7 +96,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'notification_channels_none' => [ - static::cw(fn($test): array => $test->prompts[NotificationChannels::id()] = []), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[NotificationChannels::id()] = []), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertSutNotContains('VORTEX_NOTIFY_EMAIL_FROM'); $test->assertSutNotContains('VORTEX_NOTIFY_EMAIL_RECIPIENTS'); diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/ProfileHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/ProfileHandlerProcessTest.php index ae6712ac5..9fe172a81 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/ProfileHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/ProfileHandlerProcessTest.php @@ -13,10 +13,10 @@ class ProfileHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'profile_minimal' => [ - static::cw(fn($test): string => $test->prompts[Profile::id()] = Profile::MINIMAL), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Profile::id()] = Profile::MINIMAL), ]; yield 'profile_the_empire' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Profile::id()] = Profile::CUSTOM; $test->prompts[ProfileCustom::id()] = 'the_empire'; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/ProvisionTypeHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/ProvisionTypeHandlerProcessTest.php index f9900e864..ae7210045 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/ProvisionTypeHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/ProvisionTypeHandlerProcessTest.php @@ -14,17 +14,17 @@ class ProvisionTypeHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'provision_database' => [ - static::cw(fn($test): string => $test->prompts[ProvisionType::id()] = ProvisionType::DATABASE), + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[ProvisionType::id()] = ProvisionType::DATABASE), ]; yield 'provision_database_lagoon' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[ProvisionType::id()] = ProvisionType::DATABASE; $test->prompts[HostingProvider::id()] = HostingProvider::LAGOON; $test->prompts[AiCodeInstructions::id()] = TRUE; }), ]; yield 'provision_profile' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[ProvisionType::id()] = ProvisionType::PROFILE; $test->prompts[AiCodeInstructions::id()] = TRUE; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/PullRequestHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/PullRequestHandlerProcessTest.php index 23f488052..1814dce69 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/PullRequestHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/PullRequestHandlerProcessTest.php @@ -14,16 +14,16 @@ class PullRequestHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'assign_author_pr_enabled' => [ - static::cw(fn($test): true => $test->prompts[AssignAuthorPr::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[AssignAuthorPr::id()] = TRUE), ]; yield 'assign_author_pr_disabled' => [ - static::cw(fn($test): false => $test->prompts[AssignAuthorPr::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[AssignAuthorPr::id()] = FALSE), ]; yield 'label_merge_conflicts_pr_enabled' => [ - static::cw(fn($test): true => $test->prompts[LabelMergeConflictsPr::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[LabelMergeConflictsPr::id()] = TRUE), ]; yield 'label_merge_conflicts_pr_disabled' => [ - static::cw(fn($test): false => $test->prompts[LabelMergeConflictsPr::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[LabelMergeConflictsPr::id()] = FALSE), ]; } diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/ServicesHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/ServicesHandlerProcessTest.php index 5b4653656..5abc4a3a4 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/ServicesHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/ServicesHandlerProcessTest.php @@ -6,7 +6,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\AiCodeInstructions; use DrevOps\VortexInstaller\Prompts\Handlers\Services; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(Services::class)] @@ -14,7 +13,7 @@ class ServicesHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'services_no_clamav' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Services::id()] = [Services::SOLR, Services::REDIS]; $test->prompts[AiCodeInstructions::id()] = TRUE; }), @@ -24,14 +23,14 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'services_no_redis' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Services::id()] = [Services::CLAMAV, Services::SOLR]; $test->prompts[AiCodeInstructions::id()] = TRUE; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains('redis')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains('redis')), ]; yield 'services_no_solr' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Services::id()] = [Services::CLAMAV, Services::REDIS]; $test->prompts[AiCodeInstructions::id()] = TRUE; }), @@ -41,7 +40,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'services_none' => [ - static::cw(fn($test): array => $test->prompts[Services::id()] = []), + static::cw(fn(AbstractHandlerProcessTestCase $test): array => $test->prompts[Services::id()] = []), static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->assertSutNotContains('clamav'); $test->assertSutNotContains('solr'); diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/StarterHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/StarterHandlerProcessTest.php index a4adbd563..9eefb9bdd 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/StarterHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/StarterHandlerProcessTest.php @@ -5,7 +5,6 @@ namespace DrevOps\VortexInstaller\Tests\Functional\Prompts\Handlers; use DrevOps\VortexInstaller\Prompts\Handlers\Starter; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(Starter::class)] @@ -13,24 +12,24 @@ class StarterHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'starter_demo_db' => [ - static::cw(fn($test): string => $test->prompts[Starter::id()] = Starter::LOAD_DATABASE_DEMO), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Starter::id()] = Starter::LOAD_DATABASE_DEMO), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'drupal/cms', 'wikimedia/composer-merge-plugin', 'vendor/drupal/cms/composer.json', ])), ]; yield 'starter_drupal_profile' => [ - static::cw(fn($test): string => $test->prompts[Starter::id()] = Starter::INSTALL_PROFILE_CORE), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Starter::id()] = Starter::INSTALL_PROFILE_CORE), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'drupal/cms', 'wikimedia/composer-merge-plugin', 'vendor/drupal/cms/composer.json', ])), ]; yield 'starter_drupal_cms_profile' => [ - static::cw(fn($test): string => $test->prompts[Starter::id()] = Starter::INSTALL_PROFILE_DRUPALCMS), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Starter::id()] = Starter::INSTALL_PROFILE_DRUPALCMS), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutContains([ 'drupal/cms', 'wikimedia/composer-merge-plugin', 'vendor/drupal/cms/composer.json', diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/ThemeHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/ThemeHandlerProcessTest.php index 6ca816490..1668c2695 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/ThemeHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/ThemeHandlerProcessTest.php @@ -6,7 +6,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Theme; use DrevOps\VortexInstaller\Prompts\Handlers\ThemeCustom; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use DrevOps\VortexInstaller\Utils\File; use PHPUnit\Framework\Attributes\CoversClass; @@ -15,8 +14,8 @@ class ThemeHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'theme_olivero' => [ - static::cw(fn($test): string => $test->prompts[Theme::id()] = Theme::OLIVERO), - static::cw(fn(FunctionalTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'themes/custom', [ + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Theme::id()] = Theme::OLIVERO), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'themes/custom', [ '.gitignore', 'scripts/vortex', 'composer.json', @@ -25,8 +24,8 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'theme_claro' => [ - static::cw(fn($test): string => $test->prompts[Theme::id()] = Theme::CLARO), - static::cw(fn(FunctionalTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'themes/custom', [ + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Theme::id()] = Theme::CLARO), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'themes/custom', [ '.gitignore', 'scripts/vortex', 'composer.json', @@ -35,8 +34,8 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'theme_stark' => [ - static::cw(fn($test): string => $test->prompts[Theme::id()] = Theme::STARK), - static::cw(fn(FunctionalTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'themes/custom', [ + static::cw(fn(AbstractHandlerProcessTestCase $test): string => $test->prompts[Theme::id()] = Theme::STARK), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'themes/custom', [ '.gitignore', 'scripts/vortex', 'composer.json', @@ -45,11 +44,11 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'theme_custom' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Theme::id()] = Theme::CUSTOM; $test->prompts[ThemeCustom::id()] = 'light_saber'; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'your_site_theme')), + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertDirectoryNotContainsString(static::$sut, 'your_site_theme')), ]; yield 'theme_custom_non_vortex' => [ static::cw(function (AbstractHandlerProcessTestCase $test): void { diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/TimezoneHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/TimezoneHandlerProcessTest.php index 64a0a95fe..2f6c82761 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/TimezoneHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/TimezoneHandlerProcessTest.php @@ -13,7 +13,7 @@ class TimezoneHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'timezone_gha' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Timezone::id()] = 'America/New_York'; $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), @@ -37,7 +37,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'timezone_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Timezone::id()] = 'America/New_York'; $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/ToolsHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/ToolsHandlerProcessTest.php index 08b80401c..e9351cfb9 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/ToolsHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/ToolsHandlerProcessTest.php @@ -7,7 +7,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\CiProvider; use DrevOps\VortexInstaller\Prompts\Handlers\Theme; use DrevOps\VortexInstaller\Prompts\Handlers\Tools; -use DrevOps\VortexInstaller\Tests\Functional\FunctionalTestCase; use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(Tools::class)] @@ -15,7 +14,7 @@ class ToolsHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'tools_none' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[Tools::id()] = []; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -58,12 +57,12 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_phpcs' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPCS])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpcs', 'phpcbf', 'dealerdirect/phpcodesniffer-composer-installer', @@ -72,12 +71,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_no_phpcs_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPCS])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpcs', 'phpcbf', 'dealerdirect/phpcodesniffer-composer-installer', @@ -86,75 +85,75 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_no_phpstan' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPSTAN])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpstan', 'phpstan/phpstan', 'mglaman/phpstan-drupal', ])), ]; yield 'tools_no_phpstan_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPSTAN])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpstan', 'phpstan/phpstan', 'mglaman/phpstan-drupal', ])), ]; yield 'tools_no_rector' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::RECTOR])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'rector', 'rector/rector', ])), ]; yield 'tools_no_rector_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::RECTOR])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'rector', 'rector/rector', ])), ]; yield 'tools_no_twig' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::TWIG_CS_FIXER])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'twig-cs-fixer', 'vincentlanglet/twig-cs-fixer', ])), ]; yield 'tools_no_twig_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::TWIG_CS_FIXER])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'twig-cs-fixer', 'vincentlanglet/twig-cs-fixer', ])), ]; yield 'tools_no_dclint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::DCLINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -167,7 +166,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_dclint_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::DCLINT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -180,7 +179,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_hadolint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::HADOLINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -197,7 +196,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_hadolint_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::HADOLINT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -212,7 +211,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_docker_linters' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::DCLINT, Tools::HADOLINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -227,7 +226,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_docker_linters_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::DCLINT, Tools::HADOLINT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -242,7 +241,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_eslint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -279,7 +278,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_eslint_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -304,7 +303,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_stylelint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::STYLELINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -332,7 +331,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_eslint_no_stylelint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT, Tools::STYLELINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -369,7 +368,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_stylelint_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::STYLELINT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -385,12 +384,12 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_phpunit' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPUNIT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpunit', 'ahoy test-unit', 'ahoy test-kernel', @@ -398,12 +397,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_no_phpunit_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPUNIT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpunit', 'ahoy test-unit', 'ahoy test-kernel', @@ -411,12 +410,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_no_behat' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::BEHAT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'behat', 'behat/behat', 'drupal/drupal-extension', @@ -428,12 +427,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_no_behat_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::BEHAT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'behat', 'behat/behat', 'drupal/drupal-extension', @@ -444,12 +443,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_groups_no_be_lint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPCS, Tools::PHPSTAN, Tools::RECTOR])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpcs', 'phpcbf', 'dealerdirect/phpcodesniffer-composer-installer', @@ -463,12 +462,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_groups_no_be_lint_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPCS, Tools::PHPSTAN, Tools::RECTOR])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpcs', 'phpcbf', 'dealerdirect/phpcodesniffer-composer-installer', @@ -482,7 +481,7 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_no_jest' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::JEST])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -501,7 +500,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_jest_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::JEST])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -516,7 +515,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_groups_no_fe_lint' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT, Tools::STYLELINT, Tools::JEST])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -532,7 +531,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_groups_no_fe_lint_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT, Tools::STYLELINT, Tools::JEST])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -548,12 +547,12 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_groups_no_be_tests' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPUNIT, Tools::BEHAT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpunit', 'ahoy test-unit', 'ahoy test-kernel', @@ -569,12 +568,12 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_groups_no_be_tests_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::PHPUNIT, Tools::BEHAT])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; }), - static::cw(fn(FunctionalTestCase $test) => $test->assertSutNotContains([ + static::cw(fn(AbstractHandlerProcessTestCase $test) => $test->assertSutNotContains([ 'phpunit', 'ahoy test-unit', 'ahoy test-kernel', @@ -590,7 +589,7 @@ public static function dataProviderHandlerProcess(): \Iterator { ])), ]; yield 'tools_groups_no_fe_lint_no_theme' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT, Tools::STYLELINT, Tools::JEST])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -613,7 +612,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_groups_no_fe_lint_no_theme_circleci' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT, Tools::STYLELINT, Tools::JEST])); $test->prompts[CiProvider::id()] = CiProvider::CIRCLECI; @@ -636,7 +635,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_stylelint_no_theme' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::STYLELINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; @@ -654,7 +653,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'tools_no_eslint_no_theme' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $tools = array_keys(Tools::getToolDefinitions('tools')); $test->prompts[Tools::id()] = array_values(array_diff($tools, [Tools::ESLINT])); $test->prompts[CiProvider::id()] = CiProvider::GITHUB_ACTIONS; diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/VersionSchemeHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/VersionSchemeHandlerProcessTest.php index 432a803dc..b5dcd90c9 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/VersionSchemeHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/VersionSchemeHandlerProcessTest.php @@ -12,7 +12,7 @@ class VersionSchemeHandlerProcessTest extends AbstractHandlerProcessTestCase { public static function dataProviderHandlerProcess(): \Iterator { yield 'version_scheme_calver' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[VersionScheme::id()] = VersionScheme::CALVER; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -23,7 +23,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'version_scheme_semver' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[VersionScheme::id()] = VersionScheme::SEMVER; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { @@ -34,7 +34,7 @@ public static function dataProviderHandlerProcess(): \Iterator { }), ]; yield 'version_scheme_other' => [ - static::cw(function ($test): void { + static::cw(function (AbstractHandlerProcessTestCase $test): void { $test->prompts[VersionScheme::id()] = VersionScheme::OTHER; }), static::cw(function (AbstractHandlerProcessTestCase $test): void { diff --git a/.vortex/installer/tests/Functional/Prompts/Handlers/VisualRegressionHandlerProcessTest.php b/.vortex/installer/tests/Functional/Prompts/Handlers/VisualRegressionHandlerProcessTest.php index 4fc6c32e2..f7c91cbc1 100644 --- a/.vortex/installer/tests/Functional/Prompts/Handlers/VisualRegressionHandlerProcessTest.php +++ b/.vortex/installer/tests/Functional/Prompts/Handlers/VisualRegressionHandlerProcessTest.php @@ -12,10 +12,10 @@ class VisualRegressionHandlerProcessTest extends AbstractHandlerProcessTestCase public static function dataProviderHandlerProcess(): \Iterator { yield 'visual_regression_enabled' => [ - static::cw(fn($test): true => $test->prompts[VisualRegression::id()] = TRUE), + static::cw(fn(AbstractHandlerProcessTestCase $test): true => $test->prompts[VisualRegression::id()] = TRUE), ]; yield 'visual_regression_disabled' => [ - static::cw(fn($test): false => $test->prompts[VisualRegression::id()] = FALSE), + static::cw(fn(AbstractHandlerProcessTestCase $test): false => $test->prompts[VisualRegression::id()] = FALSE), ]; } diff --git a/.vortex/installer/tests/Helpers/TuiOutput.php b/.vortex/installer/tests/Helpers/TuiOutput.php index 70ec47dc1..333eca75c 100644 --- a/.vortex/installer/tests/Helpers/TuiOutput.php +++ b/.vortex/installer/tests/Helpers/TuiOutput.php @@ -1,5 +1,7 @@ > * Test data. */ @@ -69,8 +67,6 @@ public function testValidateValidArchive(string $creator): void { } /** - * Data provider for testValidateValidArchive(). - * * @return \Iterator> * Test data. */ @@ -98,8 +94,6 @@ public function testValidateInvalid(?string $path, ?string $content, string $exp } /** - * Data provider for testValidateInvalid(). - * * @return \Iterator> * Test data. */ @@ -138,8 +132,6 @@ public function testExtract(string $creator, bool $strip, string $expected_path) } /** - * Data provider for testExtract(). - * * @return \Iterator> * Test data. */ @@ -185,8 +177,6 @@ public function testExtractErrors(?string $extension, ?string $content, bool $st } /** - * Data provider for testExtractErrors(). - * * @return \Iterator> * Test data. */ diff --git a/.vortex/installer/tests/Unit/Downloader/ArtifactTest.php b/.vortex/installer/tests/Unit/Downloader/ArtifactTest.php index 56a1ee4a7..774763bb2 100644 --- a/.vortex/installer/tests/Unit/Downloader/ArtifactTest.php +++ b/.vortex/installer/tests/Unit/Downloader/ArtifactTest.php @@ -10,9 +10,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -/** - * Tests for Artifact class. - */ #[CoversClass(Artifact::class)] class ArtifactTest extends TestCase { @@ -32,11 +29,7 @@ public function testFromUri(?string $uri, string $expected_repo, string $expecte } } - /** - * Data provider for testFromUri(). - */ public static function dataProviderFromUri(): \Iterator { - // Default URI cases. yield 'null uri defaults to default repo and stable ref' => [ NULL, RepositoryDownloader::DEFAULT_REPO, @@ -47,7 +40,6 @@ public static function dataProviderFromUri(): \Iterator { RepositoryDownloader::DEFAULT_REPO, RepositoryDownloader::REF_STABLE, ]; - // GitHub HTTPS patterns. yield 'https url with #ref' => [ 'https://github.com/drevops/vortex.git#1.0.0', 'https://github.com/drevops/vortex.git', @@ -73,7 +65,6 @@ public static function dataProviderFromUri(): \Iterator { 'https://github.com/drevops/vortex', 'abc123def', ]; - // Git SSH patterns. yield 'git@ scp-style with #ref' => [ 'git@github.com:drevops/vortex#stable', 'git@github.com:drevops/vortex', @@ -84,7 +75,6 @@ public static function dataProviderFromUri(): \Iterator { 'git@github.com:drevops/vortex', 'HEAD', ]; - // SSH and Git protocol URLs. yield 'ssh:// url with #ref' => [ 'ssh://git@github.com/drevops/vortex#develop', 'ssh://git@github.com/drevops/vortex', @@ -115,7 +105,6 @@ public static function dataProviderFromUri(): \Iterator { 'http://github.com/drevops/vortex', 'HEAD', ]; - // Local path patterns. yield 'local path with #ref' => [ '/path/to/repo#develop', '/path/to/repo', @@ -141,7 +130,6 @@ public static function dataProviderFromUri(): \Iterator { '/path/to/repo', 'HEAD', ]; - // Invalid ref format. yield 'invalid ref with space' => [ 'https://github.com/drevops/vortex.git#invalid ref', '', @@ -163,7 +151,6 @@ public static function dataProviderFromUri(): \Iterator { \RuntimeException::class, 'Invalid git reference: "feature//name"', ]; - // Invalid URI formats. yield 'invalid https format - missing path structure' => [ 'https://github.com', '', @@ -236,9 +223,6 @@ public function testCreate(string $repo, string $ref, ?string $expected_exceptio } } - /** - * Data provider for testCreate(). - */ public static function dataProviderCreate(): \Iterator { yield 'valid remote repo and ref' => [ 'https://github.com/drevops/vortex.git', @@ -268,9 +252,6 @@ public function testIsRemote(string $repo, bool $expected): void { $this->assertEquals($expected, $artifact->isRemote()); } - /** - * Data provider for testIsRemote(). - */ public static function dataProviderIsRemote(): \Iterator { yield 'https url' => ['https://github.com/drevops/vortex.git', TRUE]; yield 'http url' => ['http://github.com/drevops/vortex.git', TRUE]; @@ -288,9 +269,6 @@ public function testIsLocal(string $repo, bool $expected): void { $this->assertEquals($expected, $artifact->isLocal()); } - /** - * Data provider for testIsLocal(). - */ public static function dataProviderIsLocal(): \Iterator { yield 'https url' => ['https://github.com/drevops/vortex.git', FALSE]; yield 'http url' => ['http://github.com/drevops/vortex.git', FALSE]; @@ -308,9 +286,6 @@ public function testIsDefault(string $repo, string $ref, bool $expected): void { $this->assertEquals($expected, $artifact->isDefault()); } - /** - * Data provider for testIsDefault(). - */ public static function dataProviderIsDefault(): \Iterator { yield 'default repo with stable ref' => [ RepositoryDownloader::DEFAULT_REPO, @@ -350,9 +325,6 @@ public function testGetRepoUrl(string $repo, string $expected_url): void { $this->assertEquals($expected_url, $artifact->getRepoUrl()); } - /** - * Data provider for testGetRepoUrl(). - */ public static function dataProviderGetRepoUrl(): \Iterator { yield 'https url with .git' => [ 'https://github.com/drevops/vortex.git', @@ -378,9 +350,6 @@ public function testIsStable(string $repo, string $ref, bool $expected): void { $this->assertEquals($expected, $artifact->isStable()); } - /** - * Data provider for testIsStable(). - */ public static function dataProviderIsStable(): \Iterator { yield 'stable ref' => ['https://github.com/drevops/vortex.git', 'stable', TRUE]; yield 'HEAD ref' => ['https://github.com/drevops/vortex.git', 'HEAD', FALSE]; @@ -394,9 +363,6 @@ public function testIsDevelopment(string $repo, string $ref, bool $expected): vo $this->assertEquals($expected, $artifact->isDevelopment()); } - /** - * Data provider for testIsDevelopment(). - */ public static function dataProviderIsDevelopment(): \Iterator { yield 'HEAD ref' => ['https://github.com/drevops/vortex.git', 'HEAD', TRUE]; yield 'stable ref' => ['https://github.com/drevops/vortex.git', 'stable', FALSE]; diff --git a/.vortex/installer/tests/Unit/Downloader/DownloaderTest.php b/.vortex/installer/tests/Unit/Downloader/DownloaderTest.php index b576564b1..ec3aba7b7 100644 --- a/.vortex/installer/tests/Unit/Downloader/DownloaderTest.php +++ b/.vortex/installer/tests/Unit/Downloader/DownloaderTest.php @@ -33,7 +33,6 @@ public function testDownloadSuccess(): void { $downloader = new Downloader($mock_http_client); $downloader->download('https://example.com/file.sql', $destination); - // If we got here without exception, the download was successful. $this->addToAssertionCount(1); } @@ -75,7 +74,6 @@ public function testDownloadFollowsRedirects(): void { } public function testDownloadWithDefaultClient(): void { - // Test that the class can be instantiated without providing an HTTP client. $downloader = new Downloader(); $this->assertInstanceOf(Downloader::class, $downloader); } diff --git a/.vortex/installer/tests/Unit/Downloader/RepositoryDownloaderTest.php b/.vortex/installer/tests/Unit/Downloader/RepositoryDownloaderTest.php index 71270fa9f..9aae68f9e 100644 --- a/.vortex/installer/tests/Unit/Downloader/RepositoryDownloaderTest.php +++ b/.vortex/installer/tests/Unit/Downloader/RepositoryDownloaderTest.php @@ -5,8 +5,8 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Downloader; use AlexSkrypnyk\File\File; -use DrevOps\VortexInstaller\Downloader\Artifact; use DrevOps\VortexInstaller\Downloader\ArchiverInterface; +use DrevOps\VortexInstaller\Downloader\Artifact; use DrevOps\VortexInstaller\Downloader\Downloader; use DrevOps\VortexInstaller\Downloader\RepositoryDownloader; use DrevOps\VortexInstaller\Runner\ProcessRunner; @@ -127,7 +127,6 @@ public function testDiscoverLatestReleaseRemote(string $repo, mixed $release_dat $mock_body->method('getContents')->willReturn($release_json); $mock_response->method('getStatusCode')->willReturn(200); - // Only the API call uses httpClient now. $mock_http_client->method('request')->willReturn($mock_response); } } @@ -157,8 +156,6 @@ public function testDiscoverLatestReleaseRemote(string $repo, mixed $release_dat } /** - * Data provider for testDiscoverLatestReleaseRemote(). - * * @return \Iterator> * Test data. */ @@ -352,8 +349,6 @@ public function testDownloadWithNullDestination(string $repo, string $expected_m } /** - * Data provider for testDownloadWithNullDestination(). - * * @return \Iterator> * Test data. */ @@ -374,7 +369,8 @@ public function testDownloadFromLocal(string $ref, string $expected_version): vo $destination = self::$tmp . '/dest_' . uniqid(); File::mkdir($destination); - // Handle the special case where we need to get the actual commit hash. + // The 'COMMIT_HASH' sentinel resolves to the repository's actual commit + // hash at run time. if ($ref === 'COMMIT_HASH') { $output = self::gitRunner($temp_repo_dir)->run('git rev-parse HEAD', output: new NullOutput())->getOutput(); $this->assertIsString($output, 'Failed to get commit hash from git repository'); @@ -393,8 +389,6 @@ public function testDownloadFromLocal(string $ref, string $expected_version): vo } /** - * Data provider for testDownloadFromLocal(). - * * @return \Iterator> * Test data. */ @@ -445,7 +439,6 @@ public function testDiscoverLatestReleaseRemoteWithGithubToken(): void { return $mock_response; }); $mock_archiver = $this->createMock(ArchiverInterface::class); - // File downloader should receive the token in headers. $mock_file_downloader = $this->createMock(Downloader::class); $mock_file_downloader->expects($this->once())->method('download')->willReturnCallback(function ($url, $dest, array $headers): void { $this->assertArrayHasKey('Authorization', $headers); @@ -463,7 +456,6 @@ public function testDownloadArchiveWithGithubToken(): void { static::envSet('GITHUB_TOKEN', 'test_token_67890'); $mock_http_client = $this->createMock(ClientInterface::class); $mock_archiver = $this->createMock(ArchiverInterface::class); - // File downloader should receive the token in headers. $mock_file_downloader = $this->createMock(Downloader::class); $mock_file_downloader->expects($this->once())->method('download')->willReturnCallback(function ($url, $dest, array $headers): void { $this->assertArrayHasKey('Authorization', $headers); @@ -477,74 +469,6 @@ public function testDownloadArchiveWithGithubToken(): void { $this->assertEquals('develop', $version); } - protected function createMockHttpClient(int $status_code = 200, string $body_content = 'mock content'): ClientInterface { - $mock_client = $this->createMock(ClientInterface::class); - $mock_response = $this->createMock(ResponseInterface::class); - $mock_body = $this->createMock(StreamInterface::class); - $mock_response->method('getBody')->willReturn($mock_body); - $mock_body->method('getContents')->willReturn($body_content); - $mock_response->method('getStatusCode')->willReturn($status_code); - $mock_client->method('request')->willReturn($mock_response); - return $mock_client; - } - - protected function createMockArchiver(): MockObject { - return $this->createMock(ArchiverInterface::class); - } - - protected function createGitRepo(bool $with_composer_json = TRUE): string { - $temp_repo_dir = self::$tmp . '/test_git_repo_' . uniqid(); - File::mkdir($temp_repo_dir); - - $runner = self::gitRunner($temp_repo_dir); - $runner->run('git init', output: new NullOutput()); - $runner->run('git', args: ['config', 'user.email', 'test@example.com'], output: new NullOutput()); - $runner->run('git', args: ['config', 'user.name', 'Test User'], output: new NullOutput()); - - File::dump($temp_repo_dir . '/test.txt', 'test content'); - $runner->run('git add .', output: new NullOutput()); - $runner->run('git', args: ['commit', '-m', 'Initial commit'], output: new NullOutput()); - - if ($with_composer_json) { - File::dump($temp_repo_dir . '/composer.json', '{}'); - $runner->run('git add composer.json', output: new NullOutput()); - $runner->run('git', args: ['commit', '-m', 'Add composer.json'], output: new NullOutput()); - } - - return $temp_repo_dir; - } - - /** - * Create a runner that operates on a repository without writing a log. - */ - protected static function gitRunner(string $repo_dir): ProcessRunner { - $runner = new ProcessRunner(); - $runner->getLogger()->disable(); - - return $runner->setCwd($repo_dir); - } - - protected function removeGitRepo(string $repo_dir): void { - File::remove($repo_dir); - } - - protected function createMockArchiverWithExtract(): MockObject { - $mock_archiver = $this->createMockArchiver(); - $mock_archiver->expects($this->once())->method('validate'); - $mock_archiver->expects($this->once())->method('extract')->willReturnCallback(function ($archive, string $dest): void { - File::dump($dest . '/composer.json', '{}'); - }); - return $mock_archiver; - } - - /** - * @return \PHPUnit\Framework\MockObject\MockObject&\DrevOps\VortexInstaller\Downloader\Downloader - * Mock file downloader. - */ - protected function createMockFileDownloader(): MockObject { - return $this->createMock(Downloader::class); - } - public function testValidateRemoteRepositoryExistsWithNotFoundError(): void { $mock_http_client = $this->createMock(ClientInterface::class); $mock_response = $this->createMock(ResponseInterface::class); @@ -668,4 +592,69 @@ public function testValidateLocalArtifactWithCustomRef(): void { $this->removeGitRepo($temp_repo_dir); } + protected function createMockHttpClient(int $status_code = 200, string $body_content = 'mock content'): ClientInterface { + $mock_client = $this->createMock(ClientInterface::class); + $mock_response = $this->createMock(ResponseInterface::class); + $mock_body = $this->createMock(StreamInterface::class); + $mock_response->method('getBody')->willReturn($mock_body); + $mock_body->method('getContents')->willReturn($body_content); + $mock_response->method('getStatusCode')->willReturn($status_code); + $mock_client->method('request')->willReturn($mock_response); + return $mock_client; + } + + protected function createMockArchiver(): MockObject { + return $this->createMock(ArchiverInterface::class); + } + + protected function createGitRepo(bool $with_composer_json = TRUE): string { + $temp_repo_dir = self::$tmp . '/test_git_repo_' . uniqid(); + File::mkdir($temp_repo_dir); + + $runner = self::gitRunner($temp_repo_dir); + $runner->run('git init', output: new NullOutput()); + $runner->run('git', args: ['config', 'user.email', 'test@example.com'], output: new NullOutput()); + $runner->run('git', args: ['config', 'user.name', 'Test User'], output: new NullOutput()); + + File::dump($temp_repo_dir . '/test.txt', 'test content'); + $runner->run('git add .', output: new NullOutput()); + $runner->run('git', args: ['commit', '-m', 'Initial commit'], output: new NullOutput()); + + if ($with_composer_json) { + File::dump($temp_repo_dir . '/composer.json', '{}'); + $runner->run('git add composer.json', output: new NullOutput()); + $runner->run('git', args: ['commit', '-m', 'Add composer.json'], output: new NullOutput()); + } + + return $temp_repo_dir; + } + + protected static function gitRunner(string $repo_dir): ProcessRunner { + $runner = new ProcessRunner(); + $runner->getLogger()->disable(); + + return $runner->setCwd($repo_dir); + } + + protected function removeGitRepo(string $repo_dir): void { + File::remove($repo_dir); + } + + protected function createMockArchiverWithExtract(): MockObject { + $mock_archiver = $this->createMockArchiver(); + $mock_archiver->expects($this->once())->method('validate'); + $mock_archiver->expects($this->once())->method('extract')->willReturnCallback(function ($archive, string $dest): void { + File::dump($dest . '/composer.json', '{}'); + }); + return $mock_archiver; + } + + /** + * @return \PHPUnit\Framework\MockObject\MockObject&\DrevOps\VortexInstaller\Downloader\Downloader + * Mock file downloader. + */ + protected function createMockFileDownloader(): MockObject { + return $this->createMock(Downloader::class); + } + } diff --git a/.vortex/installer/tests/Unit/Logger/FileLoggerTest.php b/.vortex/installer/tests/Unit/Logger/FileLoggerTest.php index c5373faab..fe8466653 100644 --- a/.vortex/installer/tests/Unit/Logger/FileLoggerTest.php +++ b/.vortex/installer/tests/Unit/Logger/FileLoggerTest.php @@ -10,15 +10,9 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; -/** - * Tests for FileLogger class. - */ #[CoversClass(FileLogger::class)] class FileLoggerTest extends UnitTestCase { - /** - * Test enable and disable methods. - */ #[DataProvider('dataProviderEnableDisable')] public function testEnableDisable(bool $initial_state, bool $after_enable, bool $after_disable): void { $logger = new FileLogger(); @@ -38,9 +32,6 @@ public function testEnableDisable(bool $initial_state, bool $after_enable, bool $this->assertInstanceOf(FileLogger::class, $result, 'disable() should return self for method chaining'); } - /** - * Data provider for enable/disable tests. - */ public static function dataProviderEnableDisable(): \Iterator { yield 'initially enabled' => [ 'initial_state' => TRUE, @@ -54,31 +45,22 @@ public static function dataProviderEnableDisable(): \Iterator { ]; } - /** - * Test setDir and getDir methods. - */ #[DataProvider('dataProviderDirectoryManagement')] public function testDirectoryManagement(string $dir, bool $test_default): void { $logger = new FileLogger(); - // Test default directory uses getcwd(). if ($test_default) { $this->assertEquals(getcwd(), $logger->getDir()); } else { - // Test setDir sets custom directory. $result = $logger->setDir($dir); $this->assertEquals($dir, $logger->getDir()); $this->assertInstanceOf(FileLogger::class, $result, 'setDir() should return self for method chaining'); - // Test getDir returns the set directory. $this->assertEquals($dir, $logger->getDir()); } } - /** - * Data provider for directory paths. - */ public static function dataProviderDirectoryManagement(): \Iterator { yield 'default directory (cwd)' => [ 'dir' => '', @@ -94,9 +76,6 @@ public static function dataProviderDirectoryManagement(): \Iterator { ]; } - /** - * Test open method with enabled logging. - */ #[DataProvider('dataProviderOpen')] public function testOpen(string $command, array $args, bool $enabled, ?string $expected_pattern, ?string $expected_exception, ?string $expected_message): void { if ($expected_exception !== NULL) { @@ -127,11 +106,9 @@ public function testOpen(string $command, array $args, bool $enabled, ?string $e $this->assertMatchesRegularExpression($expected_pattern, $path, 'Log file path should match expected pattern'); } - // Verify log directory was created. $log_dir = dirname($path); $this->assertDirectoryExists($log_dir, 'Log directory should be created'); - // Verify log file was created. $this->assertFileExists($path, 'Log file should be created'); $logger->close(); @@ -139,9 +116,6 @@ public function testOpen(string $command, array $args, bool $enabled, ?string $e } } - /** - * Data provider for open scenarios. - */ public static function dataProviderOpen(): \Iterator { yield 'simple command, enabled' => [ 'command' => 'test-command', @@ -185,9 +159,6 @@ public static function dataProviderOpen(): \Iterator { ]; } - /** - * Test write method. - */ #[DataProvider('dataProviderWrite')] public function testWrite(string $content, bool $is_open, int $expected_writes): void { $logger = new FileLogger(); @@ -199,7 +170,6 @@ public function testWrite(string $content, bool $is_open, int $expected_writes): $this->assertNotNull($path); } - // Write content multiple times. for ($i = 0; $i < $expected_writes; $i++) { $logger->write($content); } @@ -208,7 +178,6 @@ public function testWrite(string $content, bool $is_open, int $expected_writes): $logger->close(); $path = $logger->getPath(); - // Verify content was written. $written_content = file_get_contents((string) $path); $expected_content = str_repeat($content, $expected_writes); $this->assertEquals($expected_content, $written_content, 'Written content should match expected content'); @@ -216,16 +185,13 @@ public function testWrite(string $content, bool $is_open, int $expected_writes): File::remove((string) $path); } else { - // When logger is not open, write() should be a no-op. - // We can't directly verify this, but we ensure no errors occur. + // write() on an unopened logger has no observable effect, so the test + // only asserts that no error was thrown. // @phpstan-ignore-next-line $this->assertTrue(TRUE, 'write() should not throw error when logger is not open'); } } - /** - * Data provider for write content. - */ public static function dataProviderWrite(): \Iterator { yield 'single write, logger open' => [ 'content' => 'Test log entry', @@ -254,33 +220,27 @@ public static function dataProviderWrite(): \Iterator { ]; } - /** - * Test close method. - */ public function testClose(): void { $logger = new FileLogger(); $logger->setDir(self::$tmp); - // Test close when no file is open (should be no-op). $logger->close(); // @phpstan-ignore-next-line $this->assertTrue(TRUE, 'close() should not throw error when no file is open'); - // Test close after opening. $logger->open('test-command'); $path = $logger->getPath(); $this->assertNotNull($path); $logger->close(); - // Verify file is closed by attempting to write (should be no-op). + // The file remains after close(); a subsequent write() must leave it + // empty. $logger->write('should not be written'); - // File should still exist but content should not be written after close. $content = file_get_contents($path); $this->assertEquals('', $content, 'No content should be written after close()'); - // Test multiple close calls (idempotent). $logger->close(); $logger->close(); // @phpstan-ignore-next-line @@ -289,17 +249,12 @@ public function testClose(): void { File::remove($path); } - /** - * Test getPath method. - */ public function testGetPath(): void { $logger = new FileLogger(); $logger->setDir(self::$tmp); - // Test getPath before open() is called. $this->assertNull($logger->getPath(), 'getPath() should return NULL before open() is called'); - // Test getPath after open(). $logger->open('test-command'); $path = $logger->getPath(); // @phpstan-ignore-next-line @@ -309,7 +264,6 @@ public function testGetPath(): void { $logger->close(); File::remove((string) $path); - // Test getPath when logging is disabled before open. $logger2 = new FileLogger(); $logger2->setDir(self::$tmp); $logger2->disable(); @@ -318,9 +272,6 @@ public function testGetPath(): void { $this->assertNull($logger2->getPath(), 'getPath() should return NULL when logging is disabled'); } - /** - * Test buildFilename method. - */ #[DataProvider('dataProviderBuildFilename')] public function testBuildFilename(string $command, array $args, string $expected): void { $logger = new FileLogger(); @@ -341,9 +292,6 @@ public function testBuildFilename(string $command, array $args, string $expected } } - /** - * Data provider for filename building. - */ public static function dataProviderBuildFilename(): \Iterator { yield 'command only' => [ 'command' => 'test-command', diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerDiscoveryTestCase.php b/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerDiscoveryTestCase.php index e850016bb..29503a0d4 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerDiscoveryTestCase.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerDiscoveryTestCase.php @@ -19,9 +19,9 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Domain; use DrevOps\VortexInstaller\Prompts\Handlers\FrontendBuild; use DrevOps\VortexInstaller\Prompts\Handlers\Gitleaks; +use DrevOps\VortexInstaller\Prompts\Handlers\HostingProjectName; use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use DrevOps\VortexInstaller\Prompts\Handlers\LabelMergeConflictsPr; -use DrevOps\VortexInstaller\Prompts\Handlers\HostingProjectName; use DrevOps\VortexInstaller\Prompts\Handlers\MachineName; use DrevOps\VortexInstaller\Prompts\Handlers\Migration; use DrevOps\VortexInstaller\Prompts\Handlers\MigrationFetchSource; diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerTypeTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerTypeTest.php index 3820c8e35..7f96ec123 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerTypeTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/AbstractHandlerTypeTest.php @@ -12,7 +12,6 @@ use DrevOps\VortexInstaller\Prompts\Handlers\CodeProvider; use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseFetchSource; use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseImage; -use DrevOps\VortexInstaller\Prompts\Handlers\MigrationImage; use DrevOps\VortexInstaller\Prompts\Handlers\DependencyUpdatesProvider; use DrevOps\VortexInstaller\Prompts\Handlers\DeployTypes; use DrevOps\VortexInstaller\Prompts\Handlers\Domain; @@ -24,6 +23,7 @@ use DrevOps\VortexInstaller\Prompts\Handlers\MachineName; use DrevOps\VortexInstaller\Prompts\Handlers\Migration; use DrevOps\VortexInstaller\Prompts\Handlers\MigrationFetchSource; +use DrevOps\VortexInstaller\Prompts\Handlers\MigrationImage; use DrevOps\VortexInstaller\Prompts\Handlers\ModulePrefix; use DrevOps\VortexInstaller\Prompts\Handlers\Modules; use DrevOps\VortexInstaller\Prompts\Handlers\Name; diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/BaselineHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/BaselineHandlerDiscoveryTest.php index f5fbbfbf8..1500ff361 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/BaselineHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/BaselineHandlerDiscoveryTest.php @@ -35,7 +35,7 @@ public static function dataProviderRunPrompts(): \Iterator { yield 'installed project' => [ [], $expected_installed, - function (BaselineHandlerDiscoveryTest $test, Config $config): void { + function (AbstractHandlerDiscoveryTestCase $test, Config $config): void { $test->stubComposerJsonValue('type', 'drupal-project'); $test->stubComposerJsonValue('name', 'myproject_org/myproject'); $test->stubVortexProject($config); @@ -44,7 +44,7 @@ function (BaselineHandlerDiscoveryTest $test, Config $config): void { yield 'installed project - minimal' => [ [], $expected_installed, - function (BaselineHandlerDiscoveryTest $test, Config $config): void { + function (AbstractHandlerDiscoveryTestCase $test, Config $config): void { $test->stubComposerJsonValue('name', 'myproject_org/myproject'); $test->stubVortexProject($config); }, diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/CiProviderHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/CiProviderHandlerDiscoveryTest.php index 795fed29b..d84e2bb12 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/CiProviderHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/CiProviderHandlerDiscoveryTest.php @@ -7,8 +7,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\CiProvider; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(CiProvider::class)] class CiProviderHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/CodeCoverageProviderHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/CodeCoverageProviderHandlerDiscoveryTest.php index 088c1abfd..23ebc1cab 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/CodeCoverageProviderHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/CodeCoverageProviderHandlerDiscoveryTest.php @@ -8,8 +8,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\CodeCoverageProvider; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(CodeCoverageProvider::class)] class CodeCoverageProviderHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseFetchSourceHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseFetchSourceHandlerDiscoveryTest.php index ace901e3b..bcc78955b 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseFetchSourceHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseFetchSourceHandlerDiscoveryTest.php @@ -5,8 +5,8 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Prompts\Handlers; use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseFetchSource; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(DatabaseFetchSource::class)] class DatabaseFetchSourceHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseImageHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseImageHandlerDiscoveryTest.php index df5192c07..06a772b02 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseImageHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/DatabaseImageHandlerDiscoveryTest.php @@ -7,8 +7,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseFetchSource; use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseImage; use DrevOps\VortexInstaller\Utils\Config; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(DatabaseImage::class)] class DatabaseImageHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/DependencyUpdatesProviderHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/DependencyUpdatesProviderHandlerDiscoveryTest.php index 6eecea658..86c48f2b6 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/DependencyUpdatesProviderHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/DependencyUpdatesProviderHandlerDiscoveryTest.php @@ -8,8 +8,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\DependencyUpdatesProvider; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(DependencyUpdatesProvider::class)] class DependencyUpdatesProviderHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/DeployTypesHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/DeployTypesHandlerDiscoveryTest.php index e6d3b342f..347e824e5 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/DeployTypesHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/DeployTypesHandlerDiscoveryTest.php @@ -7,8 +7,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\DeployTypes; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\Converter; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(DeployTypes::class)] class DeployTypesHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/DocsHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/DocsHandlerDiscoveryTest.php index 05efa15aa..86e77a660 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/DocsHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/DocsHandlerDiscoveryTest.php @@ -7,8 +7,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\PreserveDocsProject; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(PreserveDocsProject::class)] class DocsHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/FrontendBuildHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/FrontendBuildHandlerDiscoveryTest.php index 6ba3dd85b..8594cd34b 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/FrontendBuildHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/FrontendBuildHandlerDiscoveryTest.php @@ -26,12 +26,10 @@ public static function dataProviderRunPrompts(): \Iterator { [FrontendBuild::id() => Key::ENTER], [FrontendBuild::id() => TRUE] + $expected_defaults, ]; - yield 'frontend build - not shown for core theme' => [ [Theme::id() => Key::DOWN . Key::ENTER], [Theme::id() => Theme::OLIVERO] + $expected_defaults_core, ]; - yield 'frontend build - discovery - build in container' => [ [], [Theme::id() => 'discovered_project', FrontendBuild::id() => TRUE] + $expected_installed, @@ -41,7 +39,6 @@ function (AbstractHandlerDiscoveryTestCase $test, Config $config): void { $test->stubDotenvValue('VORTEX_FRONTEND_BUILD_SKIP', '0'); }, ]; - yield 'frontend build - discovery - skip' => [ [], [Theme::id() => 'discovered_project', FrontendBuild::id() => FALSE] + $expected_installed, @@ -51,7 +48,6 @@ function (AbstractHandlerDiscoveryTestCase $test, Config $config): void { $test->stubDotenvValue('VORTEX_FRONTEND_BUILD_SKIP', '1'); }, ]; - yield 'frontend build - discovery - default when absent' => [ [], [Theme::id() => 'discovered_project', FrontendBuild::id() => TRUE] + $expected_installed, diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProjectNameHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProjectNameHandlerDiscoveryTest.php index ea1efc416..59a9fbe6d 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProjectNameHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProjectNameHandlerDiscoveryTest.php @@ -6,8 +6,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseFetchSource; use DrevOps\VortexInstaller\Prompts\Handlers\DeployTypes; -use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use DrevOps\VortexInstaller\Prompts\Handlers\HostingProjectName; +use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use DrevOps\VortexInstaller\Prompts\Handlers\Webroot; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProviderHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProviderHandlerDiscoveryTest.php index 3280bdc5d..fc8558a38 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProviderHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/HostingProviderHandlerDiscoveryTest.php @@ -11,8 +11,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Webroot; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(HostingProvider::class)] class HostingProviderHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationFetchSourceHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationFetchSourceHandlerDiscoveryTest.php index 5e0226c97..fa6800840 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationFetchSourceHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationFetchSourceHandlerDiscoveryTest.php @@ -6,8 +6,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\Migration; use DrevOps\VortexInstaller\Prompts\Handlers\MigrationFetchSource; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(MigrationFetchSource::class)] class MigrationFetchSourceHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationHandlerDiscoveryTest.php index 66808a802..5eebc65d8 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/MigrationHandlerDiscoveryTest.php @@ -10,8 +10,8 @@ use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; use DrevOps\VortexInstaller\Utils\Yaml; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(Migration::class)] class MigrationHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/NamesHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/NamesHandlerDiscoveryTest.php index 1329beae8..821e26627 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/NamesHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/NamesHandlerDiscoveryTest.php @@ -140,7 +140,7 @@ function (AbstractHandlerDiscoveryTestCase $test): void { [OrgMachineName::id() => 'prompted_org'], [OrgMachineName::id() => 'prompted_org'] + $expected_defaults, ]; - yield 'org machine name - invalid ' => [ + yield 'org machine name - invalid' => [ [OrgMachineName::id() => 'a word'], 'Please enter a valid organization machine name: only lowercase letters, numbers, and underscores are allowed.', ]; diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/PullRequestHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/PullRequestHandlerDiscoveryTest.php index 7e61396f7..cfa0cb806 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/PullRequestHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/PullRequestHandlerDiscoveryTest.php @@ -8,8 +8,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\LabelMergeConflictsPr; use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(AssignAuthorPr::class)] #[CoversClass(LabelMergeConflictsPr::class)] diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/ServicesHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/ServicesHandlerDiscoveryTest.php index 6badcd08b..9daed2c03 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/ServicesHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/ServicesHandlerDiscoveryTest.php @@ -8,8 +8,8 @@ use DrevOps\VortexInstaller\Utils\Config; use DrevOps\VortexInstaller\Utils\File; use DrevOps\VortexInstaller\Utils\Yaml; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(Services::class)] class ServicesHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/StarterHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/StarterHandlerDiscoveryTest.php index c61f961e0..3c5f9704d 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/StarterHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/StarterHandlerDiscoveryTest.php @@ -29,7 +29,6 @@ public static function dataProviderRunPrompts(): \Iterator { Starter::id() => Starter::INSTALL_PROFILE_DRUPALCMS, Profile::id() => Starter::INSTALL_PROFILE_DRUPALCMS_PATH, ] + $expected_defaults, - ]; yield 'starter - discovery' => [ [], diff --git a/.vortex/installer/tests/Unit/Prompts/Handlers/VersionSchemeHandlerDiscoveryTest.php b/.vortex/installer/tests/Unit/Prompts/Handlers/VersionSchemeHandlerDiscoveryTest.php index e947bb79b..c6c15617b 100644 --- a/.vortex/installer/tests/Unit/Prompts/Handlers/VersionSchemeHandlerDiscoveryTest.php +++ b/.vortex/installer/tests/Unit/Prompts/Handlers/VersionSchemeHandlerDiscoveryTest.php @@ -6,8 +6,8 @@ use DrevOps\VortexInstaller\Prompts\Handlers\VersionScheme; use DrevOps\VortexInstaller\Utils\Config; -use PHPUnit\Framework\Attributes\CoversClass; use Laravel\Prompts\Key; +use PHPUnit\Framework\Attributes\CoversClass; #[CoversClass(VersionScheme::class)] class VersionSchemeHandlerDiscoveryTest extends AbstractHandlerDiscoveryTestCase { diff --git a/.vortex/installer/tests/Unit/Runner/AbstractRunnerTest.php b/.vortex/installer/tests/Unit/Runner/AbstractRunnerTest.php index dd671acd9..9a685d303 100644 --- a/.vortex/installer/tests/Unit/Runner/AbstractRunnerTest.php +++ b/.vortex/installer/tests/Unit/Runner/AbstractRunnerTest.php @@ -4,24 +4,18 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Runner; -use DrevOps\VortexInstaller\Utils\Tui; use DrevOps\VortexInstaller\Logger\FileLogger; use DrevOps\VortexInstaller\Logger\FileLoggerInterface; use DrevOps\VortexInstaller\Runner\AbstractRunner; use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; +use DrevOps\VortexInstaller\Utils\Tui; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Console\Output\OutputInterface; -/** - * Tests for AbstractRunner class. - */ #[CoversClass(AbstractRunner::class)] class AbstractRunnerTest extends UnitTestCase { - /** - * Test getLogger creates FileLogger instance lazily. - */ public function testGetLoggerCreatesInstanceLazily(): void { $runner = new ConcreteRunner(); @@ -32,9 +26,6 @@ public function testGetLoggerCreatesInstanceLazily(): void { $this->assertSame($logger1, $logger2, 'getLogger() should return the same instance on subsequent calls'); } - /** - * Test getCwd returns current directory by default. - */ public function testGetCwdReturnsCurrentDirectory(): void { $runner = new ConcreteRunner(); @@ -42,9 +33,6 @@ public function testGetCwdReturnsCurrentDirectory(): void { $this->assertEquals(getcwd(), $cwd); } - /** - * Test setCwd sets custom directory. - */ public function testSetCwdSetsCustomDirectory(): void { $runner = new ConcreteRunner(); @@ -53,9 +41,6 @@ public function testSetCwdSetsCustomDirectory(): void { $this->assertInstanceOf(AbstractRunner::class, $result, 'setCwd() should return self for method chaining'); } - /** - * Test setCwd updates logger directory. - */ public function testSetCwdUpdatesLoggerDirectory(): void { $runner = new ConcreteRunner(); $logger = $runner->getLogger(); @@ -65,9 +50,6 @@ public function testSetCwdUpdatesLoggerDirectory(): void { $this->assertEquals(self::$tmp, $logger->getDir()); } - /** - * Test setLogger replaces the lazily created logger. - */ public function testSetLoggerReplacesInstance(): void { $runner = new ConcreteRunner(); $this->assertInstanceOf(FileLogger::class, $runner->getLogger()); @@ -78,13 +60,9 @@ public function testSetLoggerReplacesInstance(): void { $this->assertSame($logger, $runner->getLogger()); } - /** - * Test enableStreaming sets internal flag. - */ public function testEnableStreaming(): void { $runner = new ConcreteRunner(); - // Streaming is enabled by default. $this->assertTrue($runner->shouldStream()); $runner->disableStreaming(); @@ -95,9 +73,6 @@ public function testEnableStreaming(): void { $this->assertInstanceOf(AbstractRunner::class, $result, 'enableStreaming() should return self for method chaining'); } - /** - * Test disableStreaming sets internal flag. - */ public function testDisableStreaming(): void { $runner = new ConcreteRunner(); @@ -108,36 +83,24 @@ public function testDisableStreaming(): void { $this->assertInstanceOf(AbstractRunner::class, $result, 'disableStreaming() should return self for method chaining'); } - /** - * Test getCommand returns NULL initially. - */ public function testGetCommandInitiallyNull(): void { $runner = new ConcreteRunner(); $this->assertNull($runner->getCommand()); } - /** - * Test getExitCode returns 0 initially. - */ public function testGetExitCodeInitiallyZero(): void { $runner = new ConcreteRunner(); $this->assertEquals(0, $runner->getExitCode()); } - /** - * Test getOutput returns empty string initially. - */ public function testGetOutputInitiallyEmpty(): void { $runner = new ConcreteRunner(); $this->assertEquals('', $runner->getOutput()); } - /** - * Test parseCommand with various formats. - */ #[DataProvider('dataProviderParseCommand')] public function testParseCommand(string $command, array $expected, ?string $expected_exception, ?string $expected_message): void { if ($expected_exception !== NULL) { @@ -154,9 +117,6 @@ public function testParseCommand(string $command, array $expected, ?string $expe } } - /** - * Data provider for parseCommand. - */ public static function dataProviderParseCommand(): \Iterator { yield 'simple command' => [ 'command' => 'echo', @@ -346,9 +306,6 @@ public static function dataProviderParseCommand(): \Iterator { ]; } - /** - * Test reset method. - */ public function testReset(): void { $runner = new ConcreteRunner(); @@ -367,9 +324,6 @@ public function testReset(): void { $this->assertEquals(0, $runner->getExitCode()); } - /** - * Test initLogger sets correct directory and opens log. - */ public function testInitLogger(): void { $runner = new ConcreteRunner(); $runner->setCwd(self::$tmp); @@ -388,13 +342,9 @@ public function testInitLogger(): void { $logger->close(); } - /** - * Test resolveOutput with NULL uses default. - */ public function testResolveOutputWithNull(): void { $runner = new ConcreteRunner(); - // Initialize Tui with a mock output first. $mock_output = $this->createMock(OutputInterface::class); Tui::init($mock_output); @@ -404,9 +354,6 @@ public function testResolveOutputWithNull(): void { $this->assertSame($mock_output, $output); } - /** - * Test resolveOutput with provided output. - */ public function testResolveOutputWithProvided(): void { $runner = new ConcreteRunner(); $mock_output = $this->createMock(OutputInterface::class); @@ -416,9 +363,6 @@ public function testResolveOutputWithProvided(): void { $this->assertSame($mock_output, $output); } - /** - * Test getOutput with as_array parameter. - */ #[DataProvider('dataProviderGetOutputVariations')] public function testGetOutputVariations(string $output, bool $as_array, ?int $lines, string|array $expected): void { $runner = new ConcreteRunner(); @@ -429,9 +373,6 @@ public function testGetOutputVariations(string $output, bool $as_array, ?int $li $this->assertEquals($expected, $result); } - /** - * Data provider for getOutput variations. - */ public static function dataProviderGetOutputVariations(): \Iterator { yield 'string output, as_array=false, no limit' => [ 'output' => "Line 1\nLine 2\nLine 3", @@ -471,9 +412,6 @@ public static function dataProviderGetOutputVariations(): \Iterator { ]; } - /** - * Test buildCommandString with various arguments. - */ #[DataProvider('dataProviderBuildCommandString')] public function testBuildCommandString(string $command, array $args, array $opts, string $expected): void { $runner = new ConcreteRunner(); @@ -483,9 +421,6 @@ public function testBuildCommandString(string $command, array $args, array $opts $this->assertEquals($expected, $result); } - /** - * Data provider for buildCommandString. - */ public static function dataProviderBuildCommandString(): \Iterator { yield 'command only' => [ 'command' => 'echo', @@ -525,9 +460,6 @@ public static function dataProviderBuildCommandString(): \Iterator { ]; } - /** - * Test quoteArgument method. - */ #[DataProvider('dataProviderQuoteArgument')] public function testQuoteArgument(string $argument, string $expected): void { $runner = new ConcreteRunner(); @@ -537,9 +469,6 @@ public function testQuoteArgument(string $argument, string $expected): void { $this->assertEquals($expected, $result); } - /** - * Data provider for quoteArgument. - */ public static function dataProviderQuoteArgument(): \Iterator { yield 'simple string (no quoting)' => [ 'argument' => 'hello', @@ -571,9 +500,6 @@ public static function dataProviderQuoteArgument(): \Iterator { ]; } - /** - * Test formatArgs method. - */ #[DataProvider('dataProviderFormatArgs')] public function testFormatArgs(array $args, array $expected): void { $runner = new ConcreteRunner(); @@ -583,9 +509,6 @@ public function testFormatArgs(array $args, array $expected): void { $this->assertEquals($expected, $result); } - /** - * Data provider for formatArgs. - */ public static function dataProviderFormatArgs(): \Iterator { yield 'positional args' => [ 'args' => ['arg1', 'arg2'], @@ -619,93 +542,56 @@ public static function dataProviderFormatArgs(): \Iterator { } -/** - * Concrete runner implementation for testing AbstractRunner. - */ class ConcreteRunner extends AbstractRunner { /** * {@inheritdoc} */ public function run(string $command, array $args = [], array $inputs = [], array $env = [], ?OutputInterface $output = NULL): static { - // Simple implementation for testing. $this->command = $command; return $this; } - /** - * Public wrapper for parseCommand. - */ public function parseCommandPublic(string $command): array { return $this->parseCommand($command); } - /** - * Public wrapper for buildCommandString. - */ public function buildCommandStringPublic(string $command, array $args = [], array $opts = []): string { return $this->buildCommandString($command, $args, $opts); } - /** - * Public wrapper for quoteArgument. - */ public function quoteArgumentPublic(string $argument): string { return $this->quoteArgument($argument); } - /** - * Public wrapper for formatArgs. - */ public function formatArgsPublic(array $args): array { return $this->formatArgs($args); } - /** - * Public wrapper for reset. - */ public function resetPublic(): void { $this->reset(); } - /** - * Public setter for command (for testing). - */ public function setCommand(string $command): void { $this->command = $command; } - /** - * Public setter for output (for testing). - */ public function setOutput(string $output): void { $this->output = $output; } - /** - * Public wrapper for setExitCode. - */ public function setExitCodePublic(int $exit_code): void { $this->setExitCode($exit_code); } - /** - * Public getter for shouldStream (for testing). - */ public function shouldStream(): bool { return $this->shouldStream; } - /** - * Public wrapper for initLogger. - */ public function initLoggerPublic(string $command, array $args = []): FileLoggerInterface { return $this->initLogger($command, $args); } - /** - * Public wrapper for resolveOutput. - */ public function resolveOutputPublic(?OutputInterface $output): OutputInterface { return $this->resolveOutput($output); } diff --git a/.vortex/installer/tests/Unit/Runner/CommandRunnerTest.php b/.vortex/installer/tests/Unit/Runner/CommandRunnerTest.php index 708df9641..90fa9a522 100644 --- a/.vortex/installer/tests/Unit/Runner/CommandRunnerTest.php +++ b/.vortex/installer/tests/Unit/Runner/CommandRunnerTest.php @@ -16,15 +16,9 @@ use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; -/** - * Tests for CommandRunner class. - */ #[CoversClass(CommandRunner::class)] class CommandRunnerTest extends UnitTestCase { - /** - * Test constructor accepts Application instance. - */ public function testConstructor(): void { $application = new Application(); $runner = new CommandRunner($application); @@ -32,9 +26,6 @@ public function testConstructor(): void { $this->assertInstanceOf(CommandRunner::class, $runner); } - /** - * Test run with valid command. - */ public function testRunWithValidCommand(): void { $application = new Application(); $command = new TestCommand('test:command'); @@ -54,9 +45,6 @@ public function testRunWithValidCommand(): void { $this->assertStringContainsString('Test output', is_string($runner_output) ? $runner_output : implode(PHP_EOL, $runner_output)); } - /** - * Test run with streaming enabled/disabled. - */ #[DataProvider('dataProviderRunWithStreaming')] public function testRunWithStreaming(bool $streaming_enabled, bool $should_have_output): void { $application = new Application(); @@ -89,9 +77,6 @@ public function testRunWithStreaming(bool $streaming_enabled, bool $should_have_ $this->assertStringContainsString('Test output', is_string($output) ? $output : implode(PHP_EOL, $output)); } - /** - * Data provider for streaming modes. - */ public static function dataProviderRunWithStreaming(): \Iterator { yield 'streaming enabled' => [ 'streaming_enabled' => TRUE, @@ -103,9 +88,6 @@ public static function dataProviderRunWithStreaming(): \Iterator { ]; } - /** - * Test createCompositeOutput method using reflection. - */ public function testCreateCompositeOutput(): void { $application = new Application(); $runner = new CommandRunner($application); @@ -116,7 +98,6 @@ public function testCreateCompositeOutput(): void { $logger->setDir(self::$tmp); $logger->open('test'); - // Use reflection to access protected method. $reflection = new \ReflectionClass($runner); $method = $reflection->getMethod('createCompositeOutput'); @@ -125,7 +106,6 @@ public function testCreateCompositeOutput(): void { $this->assertInstanceOf(OutputInterface::class, $composite_output); $this->assertInstanceOf(BufferedOutput::class, $buffered_output); - // Test composite output behavior. $composite_output->write('Test message'); $this->assertStringContainsString('Test message', $buffered_output->fetch()); @@ -135,9 +115,6 @@ public function testCreateCompositeOutput(): void { $logger->close(); } - /** - * Test composite output with iterable messages. - */ public function testCompositeOutputWithIterableMessages(): void { $application = new Application(); $runner = new CommandRunner($application); @@ -148,13 +125,11 @@ public function testCompositeOutputWithIterableMessages(): void { $logger->setDir(self::$tmp); $logger->open('test'); - // Use reflection to access protected method. $reflection = new \ReflectionClass($runner); $method = $reflection->getMethod('createCompositeOutput'); [$composite_output, $buffered_output] = $method->invoke($runner, $output, $logger); - // Test with iterable messages. $composite_output->write(['Line 1', 'Line 2']); $content = $buffered_output->fetch(); $this->assertStringContainsString('Line 1', $content); @@ -168,9 +143,6 @@ public function testCompositeOutputWithIterableMessages(): void { $logger->close(); } - /** - * Test run with options. - */ public function testRunWithOptions(): void { $application = new Application(); $command = new TestCommand('test:command'); @@ -182,15 +154,12 @@ public function testRunWithOptions(): void { $output = new BufferedOutput(); Tui::init($output); - // Test without options since test command doesn't define any. + // TestCommand defines no options, so run() is called without any. $runner->run('test:command', []); $this->assertEquals(0, $runner->getExitCode()); } - /** - * Test run captures exit code. - */ public function testRunCapturesExitCode(): void { $application = new Application(); $command = new TestCommandWithExitCode('test:error'); @@ -209,9 +178,6 @@ public function testRunCapturesExitCode(): void { } -/** - * Test command for testing CommandRunner. - */ class TestCommand extends Command { /** @@ -224,9 +190,6 @@ protected function execute(InputInterface $input, OutputInterface $output): int } -/** - * Test command that returns non-zero exit code. - */ class TestCommandWithExitCode extends Command { /** diff --git a/.vortex/installer/tests/Unit/Runner/ProcessRunnerTest.php b/.vortex/installer/tests/Unit/Runner/ProcessRunnerTest.php index 1abb66a8e..2bdd5f2c2 100644 --- a/.vortex/installer/tests/Unit/Runner/ProcessRunnerTest.php +++ b/.vortex/installer/tests/Unit/Runner/ProcessRunnerTest.php @@ -12,15 +12,9 @@ use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Console\Output\BufferedOutput; -/** - * Tests for ProcessRunner class. - */ #[CoversClass(ProcessRunner::class)] class ProcessRunnerTest extends UnitTestCase { - /** - * Test run with simple shell command. - */ #[DataProvider('dataProviderRun')] public function testRun(string $command, array $args, string $expected_output_pattern, int $expected_exit_code, ?string $expected_exception, ?string $expected_message): void { if ($expected_exception !== NULL) { @@ -32,7 +26,6 @@ public function testRun(string $command, array $args, string $expected_output_pa $runner = new ProcessRunner(); $runner->setCwd(self::$tmp); - // Initialize Tui for output. $output = new BufferedOutput(); Tui::init($output); @@ -47,9 +40,6 @@ public function testRun(string $command, array $args, string $expected_output_pa } } - /** - * Data provider for run command tests. - */ public static function dataProviderRun(): \Iterator { yield 'simple echo command' => [ 'command' => 'echo', @@ -93,9 +83,6 @@ public static function dataProviderRun(): \Iterator { ]; } - /** - * Test run with output streaming. - */ #[DataProvider('dataProviderRunWithStreaming')] public function testRunWithStreaming(bool $streaming_enabled, bool $should_have_output_in_stream): void { $runner = new ProcessRunner(); @@ -124,9 +111,6 @@ public function testRunWithStreaming(bool $streaming_enabled, bool $should_have_ $this->assertStringContainsString('test output', is_string($runner_output) ? $runner_output : implode(PHP_EOL, $runner_output)); } - /** - * Data provider for streaming modes. - */ public static function dataProviderRunWithStreaming(): \Iterator { yield 'streaming enabled' => [ 'streaming_enabled' => TRUE, @@ -138,9 +122,6 @@ public static function dataProviderRunWithStreaming(): \Iterator { ]; } - /** - * Test resolveCommand with various command types. - */ #[DataProvider('dataProviderResolveCommand')] public function testResolveCommand(string $command, bool $expect_success, ?string $expected_exception, ?string $expected_message): void { if ($expected_exception !== NULL) { @@ -160,9 +141,6 @@ public function testResolveCommand(string $command, bool $expect_success, ?strin } } - /** - * Data provider for resolveCommand tests. - */ public static function dataProviderResolveCommand(): \Iterator { yield 'simple command (echo)' => [ 'command' => 'echo', @@ -196,9 +174,6 @@ public static function dataProviderResolveCommand(): \Iterator { ]; } - /** - * Test prepareArguments method. - */ #[DataProvider('dataProviderPrepareArguments')] public function testPrepareArguments(array $parsed_args, array $additional_args, array $expected, ?string $expected_exception, ?string $expected_message): void { if ($expected_exception !== NULL) { @@ -216,9 +191,6 @@ public function testPrepareArguments(array $parsed_args, array $additional_args, } } - /** - * Data provider for prepareArguments tests. - */ public static function dataProviderPrepareArguments(): \Iterator { yield 'merge parsed and additional args' => [ 'parsed_args' => ['arg1', 'arg2'], @@ -250,9 +222,6 @@ public static function dataProviderPrepareArguments(): \Iterator { ]; } - /** - * Test validateEnvironmentVars method. - */ #[DataProvider('dataProviderValidateEnvironmentVars')] public function testValidateEnvironmentVars(array $env, ?string $expected_exception, ?string $expected_message): void { if ($expected_exception !== NULL) { @@ -270,9 +239,6 @@ public function testValidateEnvironmentVars(array $env, ?string $expected_except } } - /** - * Data provider for environment variables tests. - */ public static function dataProviderValidateEnvironmentVars(): \Iterator { yield 'valid scalar env vars' => [ 'env' => ['VAR1' => 'value1', 'VAR2' => 'value2'], @@ -291,9 +257,6 @@ public static function dataProviderValidateEnvironmentVars(): \Iterator { ]; } - /** - * Test run with environment variables. - */ public function testRunWithEnvironmentVariables(): void { $runner = new ProcessRunner(); $runner->setCwd(self::$tmp); @@ -301,8 +264,7 @@ public function testRunWithEnvironmentVariables(): void { $output = new BufferedOutput(); Tui::init($output); - // Use printenv command which is more reliable for testing env vars. - // On Windows, we skip this test as printenv may not be available. + // The printenv binary may be absent on Windows. if (PHP_OS_FAMILY === 'Windows') { $this->markTestSkipped('Environment variable test not compatible with Windows.'); } @@ -313,9 +275,6 @@ public function testRunWithEnvironmentVariables(): void { $this->assertStringContainsString('test_value', is_string($output) ? $output : implode(PHP_EOL, $output)); } - /** - * Test run with working directory. - */ public function testRunWithWorkingDirectory(): void { $runner = new ProcessRunner(); $test_dir = self::$tmp . '/test_subdir'; @@ -332,15 +291,11 @@ public function testRunWithWorkingDirectory(): void { $this->assertStringContainsString($test_dir, is_string($output) ? $output : implode(PHP_EOL, $output)); } - /** - * Test resolveCommand with relative path. - */ public function testResolveCommandWithRelativePath(): void { $runner = new TestableProcessRunner(); $test_dir = self::$tmp . '/test_scripts'; File::mkdir($test_dir); - // Create an executable script. $script_path = $test_dir . '/test_script.sh'; File::dump($script_path, "#!/bin/sh\necho 'test'\n"); chmod($script_path, 0755); @@ -353,20 +308,12 @@ public function testResolveCommandWithRelativePath(): void { $this->assertEmpty($parsed); } - /** - * Test prepareArguments with object that can't be cast to scalar. - */ public function testPrepareArgumentsWithNonScalarAfterFormatting(): void { $runner = new TestableProcessRunner(); - // Create a test object that formatArgs will add to the array, - // but which will fail the scalar check. - // However, formatArgs will cast it to string first, so this is hard - // to trigger. - // Let's test with an actual non-scalar after formatArgs processes it. - // Since formatArgs always produces strings, line 126 might be unreachable - // through normal usage. Let's document this. - // For now, just test that normal args work. + // formatArgs() casts every value to a string, so the non-scalar check + // after formatting may be unreachable through normal usage; only normal + // arguments are exercised here. $result = $runner->prepareArgumentsPublic(['test'], ['arg1', 'arg2']); $this->assertEquals(['test', 'arg1', 'arg2'], $result); @@ -374,28 +321,16 @@ public function testPrepareArgumentsWithNonScalarAfterFormatting(): void { } -/** - * Testable ProcessRunner that exposes protected methods. - */ class TestableProcessRunner extends ProcessRunner { - /** - * Public wrapper for resolveCommand. - */ public function resolveCommandPublic(string $command): array { return $this->resolveCommand($command); } - /** - * Public wrapper for prepareArguments. - */ public function prepareArgumentsPublic(array $parsed_args, array $additional_args): array { return $this->prepareArguments($parsed_args, $additional_args); } - /** - * Public wrapper for validateEnvironmentVars. - */ public function validateEnvironmentVarsPublic(array $env): void { $this->validateEnvironmentVars($env); } diff --git a/.vortex/installer/tests/Unit/Schema/SchemaGeneratorTest.php b/.vortex/installer/tests/Unit/Schema/SchemaGeneratorTest.php index 1cd781b06..20d5eec1b 100644 --- a/.vortex/installer/tests/Unit/Schema/SchemaGeneratorTest.php +++ b/.vortex/installer/tests/Unit/Schema/SchemaGeneratorTest.php @@ -8,11 +8,11 @@ use DrevOps\VortexInstaller\Prompts\Handlers\CiProvider; use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseFetchSource; use DrevOps\VortexInstaller\Prompts\Handlers\DatabaseImage; -use DrevOps\VortexInstaller\Prompts\Handlers\MigrationImage; use DrevOps\VortexInstaller\Prompts\Handlers\HostingProjectName; use DrevOps\VortexInstaller\Prompts\Handlers\HostingProvider; use DrevOps\VortexInstaller\Prompts\Handlers\Migration; use DrevOps\VortexInstaller\Prompts\Handlers\MigrationFetchSource; +use DrevOps\VortexInstaller\Prompts\Handlers\MigrationImage; use DrevOps\VortexInstaller\Prompts\Handlers\Name; use DrevOps\VortexInstaller\Prompts\Handlers\ProfileCustom; use DrevOps\VortexInstaller\Prompts\Handlers\ThemeCustom; @@ -39,22 +39,6 @@ class SchemaGeneratorTest extends UnitTestCase { */ protected static ?array $handlers = NULL; - /** - * Get the generated schema (cached). - */ - protected function getSchema(): array { - if (static::$schema === NULL) { - $config = Config::fromString('{}'); - $prompt_manager = new PromptManager($config); - static::$handlers = $prompt_manager->getHandlers(); - - $generator = new SchemaGenerator(static::$handlers); - static::$schema = $generator->generate(); - } - - return static::$schema; - } - public function testGenerateSchema(): void { $schema = $this->getSchema(); @@ -154,6 +138,22 @@ public function testSchemaStaysInSync(): void { $this->assertCount($expected_count, $schema['prompts'], 'Schema prompt count should match handlers minus excluded.'); } + /** + * Get the generated schema (cached). + */ + protected function getSchema(): array { + if (static::$schema === NULL) { + $config = Config::fromString('{}'); + $prompt_manager = new PromptManager($config); + static::$handlers = $prompt_manager->getHandlers(); + + $generator = new SchemaGenerator(static::$handlers); + static::$schema = $generator->generate(); + } + + return static::$schema; + } + /** * Find a prompt in schema by its ID. */ diff --git a/.vortex/installer/tests/Unit/Schema/SchemaValidatorTest.php b/.vortex/installer/tests/Unit/Schema/SchemaValidatorTest.php index e3c103945..6087e1ba4 100644 --- a/.vortex/installer/tests/Unit/Schema/SchemaValidatorTest.php +++ b/.vortex/installer/tests/Unit/Schema/SchemaValidatorTest.php @@ -24,9 +24,6 @@ use DrevOps\VortexInstaller\Utils\Config; use PHPUnit\Framework\Attributes\CoversClass; -/** - * Tests for the SchemaValidator class. - */ #[CoversClass(SchemaValidator::class)] class SchemaValidatorTest extends UnitTestCase { @@ -80,7 +77,6 @@ public function testInvalidSelectValue(): void { $error_prompts = array_column($result['errors'], 'prompt'); $this->assertContains(HostingProvider::id(), $error_prompts); - // Find the specific error for hosting_provider. $hosting_error = NULL; foreach ($result['errors'] as $error) { if ($error['prompt'] === HostingProvider::id()) { @@ -111,7 +107,7 @@ public function testEmptyConfigIsValid(): void { $result = $this->validator->validate($config); - // Empty config is valid — prompts not provided are skipped. + // Prompts not provided are skipped, so an empty config is valid. $this->assertTrue($result['valid']); $this->assertEmpty($result['errors']); $this->assertEmpty($result['resolved']); @@ -126,7 +122,6 @@ public function testDependencyMetValueProvided(): void { $result = $this->validator->validate($config); // DatabaseFetchSource depends on ProvisionType=database. - // Both provided and condition met = OK. $db_errors = array_filter($result['errors'], fn(array $e): bool => $e['prompt'] === DatabaseFetchSource::id()); $this->assertEmpty($db_errors); $this->assertSame(DatabaseFetchSource::URL, $result['resolved'][DatabaseFetchSource::id()] ?? NULL); @@ -141,7 +136,6 @@ public function testDependencyNotMetValueProvided(): void { $result = $this->validator->validate($config); // DatabaseFetchSource depends on ProvisionType=database. - // ProvisionType=profile means condition not met + value provided = warning. $warning_prompts = array_column($result['warnings'], 'prompt'); $this->assertContains(DatabaseFetchSource::id(), $warning_prompts); } @@ -154,7 +148,6 @@ public function testDependencyMetValueMissing(): void { $result = $this->validator->validate($config); // MigrationFetchSource depends on Migration=true. - // Condition met + no value provided + not required = OK (skip). $this->assertTrue($result['valid']); $error_prompts = array_column($result['errors'], 'prompt'); $this->assertNotContains(MigrationFetchSource::id(), $error_prompts); @@ -168,7 +161,6 @@ public function testDependencyNotMetValueMissing(): void { $result = $this->validator->validate($config); // MigrationFetchSource depends on Migration=true. - // Condition not met + no value provided = OK (skip). $error_prompts = array_column($result['errors'], 'prompt'); $this->assertNotContains(MigrationFetchSource::id(), $error_prompts); } @@ -198,7 +190,6 @@ public function testResolvedOnlyContainsProvidedValues(): void { $this->assertTrue($result['valid']); $this->assertSame(HostingProvider::LAGOON, $result['resolved'][HostingProvider::id()]); $this->assertSame('test-project', $result['resolved'][HostingProjectName::id()]); - // Unprovided prompts should not appear in resolved. $this->assertArrayNotHasKey(Migration::id(), $result['resolved']); } diff --git a/.vortex/installer/tests/Unit/SelfTest.php b/.vortex/installer/tests/Unit/SelfTest.php index 1314acc68..b4ba38c22 100644 --- a/.vortex/installer/tests/Unit/SelfTest.php +++ b/.vortex/installer/tests/Unit/SelfTest.php @@ -11,30 +11,23 @@ class SelfTest extends UnitTestCase { public function testEnvCleanup1SetVariables(): void { - // Set environment variables using envSet. static::envSet('VORTEX_TEST_VAR_1', 'value1'); static::envSet('VORTEX_TEST_VAR_2', 'value2'); - // Set environment variables using envSetMultiple. static::envSetMultiple([ 'VORTEX_TEST_VAR_3' => 'value3', 'VORTEX_TEST_VAR_4' => 'value4', ]); - // Verify variables are set during the test. $this->assertSame('value1', getenv('VORTEX_TEST_VAR_1')); $this->assertSame('value2', getenv('VORTEX_TEST_VAR_2')); $this->assertSame('value3', getenv('VORTEX_TEST_VAR_3')); $this->assertSame('value4', getenv('VORTEX_TEST_VAR_4')); - - // Note: tearDown() will be called after this test, which should clean up - // all environment variables via envReset(). } #[Depends('testEnvCleanup1SetVariables')] public function testEnvCleanup2VerifyCleanup(): void { - // Verify that environment variables from the previous test were cleaned up - // by tearDown() calling envReset(). + // tearDown() of the previous test cleared its variables via envReset(). $this->assertFalse(getenv('VORTEX_TEST_VAR_1'), 'VORTEX_TEST_VAR_1 should be cleaned up after previous test'); $this->assertFalse(getenv('VORTEX_TEST_VAR_2'), 'VORTEX_TEST_VAR_2 should be cleaned up after previous test'); $this->assertFalse(getenv('VORTEX_TEST_VAR_3'), 'VORTEX_TEST_VAR_3 should be cleaned up after previous test'); diff --git a/.vortex/installer/tests/Unit/Task/TaskTest.php b/.vortex/installer/tests/Unit/Task/TaskTest.php index 85a601c52..93eb490bd 100644 --- a/.vortex/installer/tests/Unit/Task/TaskTest.php +++ b/.vortex/installer/tests/Unit/Task/TaskTest.php @@ -4,8 +4,8 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Task; -use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; use DrevOps\VortexInstaller\Task\Task; +use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; use DrevOps\VortexInstaller\Utils\Tui; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; diff --git a/.vortex/installer/tests/Unit/UnitTestCase.php b/.vortex/installer/tests/Unit/UnitTestCase.php index 7be61e797..63128ca51 100644 --- a/.vortex/installer/tests/Unit/UnitTestCase.php +++ b/.vortex/installer/tests/Unit/UnitTestCase.php @@ -13,10 +13,6 @@ use DrevOps\VortexInstaller\Utils\Yaml; /** - * Class UnitTestCase. - * - * UnitTestCase fixture class. - * * phpcs:disable Drupal.Commenting.FunctionComment.Missing * phpcs:disable Drupal.Commenting.DocComment.MissingShort */ diff --git a/.vortex/installer/tests/Unit/Utils/ConfigTest.php b/.vortex/installer/tests/Unit/Utils/ConfigTest.php index ebfac9b93..fe041233b 100644 --- a/.vortex/installer/tests/Unit/Utils/ConfigTest.php +++ b/.vortex/installer/tests/Unit/Utils/ConfigTest.php @@ -10,16 +10,12 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; -/** - * Tests for the Config class. - */ #[CoversClass(Config::class)] class ConfigTest extends UnitTestCase { protected function setUp(): void { parent::setUp(); - // Clear any existing environment variables that could interfere with tests. static::envUnsetPrefix('VORTEX_INSTALLER'); } @@ -56,7 +52,6 @@ public function testFromStringValid(string $json, array $expected_values): void $config = Config::fromString($json); if (empty($expected_values)) { - // For empty JSON, just assert that config was created successfully. $this->assertInstanceOf(Config::class, $config); } else { @@ -136,12 +131,9 @@ public static function dataProviderFromStringInvalid(): \Iterator { public function testGetAndSet(string $name, mixed $value, mixed $default, mixed $expected): void { $config = new Config(); - // Test default behavior. $this->assertEquals($default, $config->get($name, $default)); - // Test setting and getting. $result = $config->set($name, $value); - // Test fluent interface. $this->assertSame($config, $result); $this->assertEquals($expected, $config->get($name)); } @@ -162,10 +154,8 @@ public function testSetWithEnvironmentVariable(): void { $env_value = 'env_value'; $set_value = 'set_value'; - // Set environment variable. static::envSet($env_key, $env_value); - // Environment variable should take precedence. $config->set($env_key, $set_value); $this->assertEquals($env_value, $config->get($env_key)); } @@ -176,10 +166,8 @@ public function testSetSkipEnvironment(): void { $env_value = 'env_value'; $set_value = 'set_value'; - // Set environment variable. static::envSet($env_key, $env_value); - // Skip environment check. $config->set($env_key, $set_value, TRUE); $this->assertEquals($set_value, $config->get($env_key)); } @@ -223,15 +211,12 @@ public static function dataProviderIsQuiet(): \Iterator { public function testSetQuiet(): void { $config = new Config(); - // Test default parameter (true). $config->setQuiet(); $this->assertTrue($config->isQuiet()); - // Test explicit false. $config->setQuiet(FALSE); $this->assertFalse($config->isQuiet()); - // Test explicit true. $config->setQuiet(TRUE); $this->assertTrue($config->isQuiet()); } @@ -259,15 +244,12 @@ public static function dataProviderGetNoInteraction(): \Iterator { public function testSetNoInteraction(): void { $config = new Config(); - // Test default parameter (true). $config->setNoInteraction(); $this->assertTrue($config->getNoInteraction()); - // Test explicit false. $config->setNoInteraction(FALSE); $this->assertFalse($config->getNoInteraction()); - // Test explicit true. $config->setNoInteraction(TRUE); $this->assertTrue($config->getNoInteraction()); } @@ -293,7 +275,6 @@ public static function dataProviderIsVortexProject(): \Iterator { } public function testConstants(): void { - // Test that all constants are defined and have expected values. $this->assertEquals('VORTEX_INSTALLER_ROOT_DIR', Config::ROOT); $this->assertEquals('VORTEX_INSTALLER_DST_DIR', Config::DESTINATION); $this->assertEquals('VORTEX_INSTALLER_TMP_DIR', Config::TMP); @@ -311,7 +292,6 @@ public function testConstants(): void { } public function testEnvironmentVariablePrecedenceInConstructor(): void { - // Set environment variables. static::envSetMultiple([ Config::ROOT => '/env/root', Config::DESTINATION => '/env/dst', @@ -320,7 +300,6 @@ public function testEnvironmentVariablePrecedenceInConstructor(): void { $config = new Config('/param/root', '/param/dst', '/param/tmp'); - // Environment variables should take precedence for ROOT and TMP. $this->assertEquals('/env/root', $config->getRoot()); // DESTINATION is set with skip_env=TRUE, so the param value wins. $this->assertEquals('/param/dst', $config->getDestination()); @@ -344,7 +323,6 @@ public function testFluentInterface(): void { public function testDefaultValues(): void { $config = new Config(); - // Test default values for boolean methods. $this->assertFalse($config->isQuiet()); $this->assertFalse($config->getNoInteraction()); $this->assertFalse($config->isVortexProject()); diff --git a/.vortex/installer/tests/Unit/Utils/ConverterTest.php b/.vortex/installer/tests/Unit/Utils/ConverterTest.php index 10b169656..18eef2bec 100644 --- a/.vortex/installer/tests/Unit/Utils/ConverterTest.php +++ b/.vortex/installer/tests/Unit/Utils/ConverterTest.php @@ -5,13 +5,10 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; +use DrevOps\VortexInstaller\Utils\Converter; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; -use DrevOps\VortexInstaller\Utils\Converter; -/** - * Tests for the Converter class. - */ #[CoversClass(Converter::class)] class ConverterTest extends UnitTestCase { @@ -21,50 +18,39 @@ public function testMachineExtended(string $input, string $expected): void { } public static function dataProviderMachineExtended(): \Iterator { - // Basic cases. yield ['hello world', 'hello_world']; yield ['Hello World', 'hello_world']; yield ['HELLO WORLD', 'hello_world']; - // Multiple spaces. yield ['hello world', 'hello__world']; yield ['hello world', 'hello___world']; - // Mixed case with spaces. yield ['My Project Name', 'my_project_name']; yield ['YOUR_SITE_NAME', 'your_site_name']; - // Already underscored. yield ['hello_world', 'hello_world']; yield ['Hello_World', 'hello_world']; // Special characters (should be removed by strict()) yield ['hello@world!', 'helloworld']; yield ['my-project#name$', 'my-projectname']; yield ['test%^&*()project', 'testproject']; - // Numbers. yield ['project 123', 'project_123']; yield ['Project2024 Name', 'project2024_name']; // Unicode characters (should be replaced by strict()) yield ['café münü', 'cafe_munu']; yield ['project 😀 name', 'project__name']; - // Hyphens and underscores mixed. yield ['my-project_name', 'my-project_name']; yield ['test-case_example', 'test-case_example']; - // Empty and edge cases. yield ['', '']; yield [' ', '_']; yield [' ', '__']; yield ['_', '_']; yield ['-', '-']; - // Single word. yield ['project', 'project']; yield ['PROJECT', 'project']; yield ['Project', 'project']; - // Leading/trailing spaces. yield [' hello world ', '_hello_world_']; yield [' test ', '__test__']; - // Only special characters. yield ['@#$%', '']; yield ['!!!', '']; yield ['***', '']; - // Real-world examples. yield ['My Awesome Project', 'my_awesome_project']; yield ['DrevOps Vortex', 'drevops_vortex']; yield ['Site Name 2024', 'site_name_2024']; diff --git a/.vortex/installer/tests/Unit/Utils/EnvTest.php b/.vortex/installer/tests/Unit/Utils/EnvTest.php index 0522f617a..322c4054b 100644 --- a/.vortex/installer/tests/Unit/Utils/EnvTest.php +++ b/.vortex/installer/tests/Unit/Utils/EnvTest.php @@ -5,11 +5,11 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; -use PHPUnit\Framework\Attributes\DataProvider; use DrevOps\VortexInstaller\Utils\Env; use DrevOps\VortexInstaller\Utils\File; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; /** * Class InstallerDotEnvTest. @@ -21,15 +21,11 @@ class EnvTest extends UnitTestCase { /** - * Backup value of the $GLOBALS['_SERVER'] variable. - * * @var array */ protected $backupServer; /** - * Backup value of the $GLOBALS['_ENV'] variable. - * * @var array */ protected $backupEnv; @@ -142,9 +138,7 @@ public function testWriteValueDotenv(): void { Env::writeValueDotenv('BOOL_VAR', 'false', $actual_file); Env::writeValueDotenv('PATH_VAR', '/path/to/new file', $actual_file); Env::writeValueDotenv('EMAIL_VAR', 'new user@domain.com', $actual_file); - // Remove this variable. Env::writeValueDotenv('REMOVE_VAR', NULL, $actual_file); - // Add new variable. Env::writeValueDotenv('NEW_VAR', 'new_added_value', $actual_file); $this->assertDirectoriesIdentical(static::$sut, $fixture_dir . '/after'); @@ -199,12 +193,10 @@ public static function dataProviderFormatValueForDotenv(): \Iterator { yield ['command`substitution', '"command`substitution"']; yield ["single'quote", '"single\'quote"']; yield ['double"quote', '"double\\"quote"']; - // Combined cases (whitespace + special characters). yield ['value with "quotes"', '"value with \\"quotes\\""']; yield ['email|name with spaces', '"email|name with spaces"']; yield ['command; with spaces', '"command; with spaces"']; yield ['path with spaces & special', '"path with spaces & special"']; - // Edge cases. // = is not a special character, so no quoting needed. yield ['key=value', 'key=value']; yield ['webmaster@your-site-domain.example|Webmaster', '"webmaster@your-site-domain.example|Webmaster"']; @@ -229,16 +221,13 @@ public function testParseDotenv(string $content, ?array $expected, ?string $exce } public static function dataProviderParseDotenv(): \Iterator { - // Valid .env content. yield ['VAR1=value1', ['VAR1' => 'value1'], NULL]; yield ["VAR1=value1\nVAR2=value2", ['VAR1' => 'value1', 'VAR2' => 'value2'], NULL]; yield ['VAR="quoted value"', ['VAR' => 'quoted value'], NULL]; yield ['VAR=', ['VAR' => ''], NULL]; yield ['', [], NULL]; - // Valid content with comments. yield ["VAR1=value1\n# This is a comment\nVAR2=value2", ['VAR1' => 'value1', 'VAR2' => 'value2'], NULL]; yield ['VAR="value with # in quotes"', ['VAR' => 'value with # in quotes'], NULL]; - // Invalid .env content that should throw exceptions. yield ['VAR[invalid', NULL, 'Unable to parse file']; yield ['VAR=value1' . "\n" . 'INVALID[bracket', NULL, 'Unable to parse file']; yield ["VAR1=value1\nVAR2[invalid=value2", NULL, 'Unable to parse file']; @@ -250,14 +239,12 @@ public function testParseDotenvFileNotReadable(): void { } public function testParseDotenvFileReadFailure(): void { - // Create a file we can't read. $filename = $this->createFixtureEnvFile('VAR=value'); chmod($filename, 0000); $result = Env::parseDotenv($filename); $this->assertEquals([], $result); - // Clean up. chmod($filename, 0644); File::remove($filename); } @@ -269,18 +256,14 @@ public function testToValue(string $input, mixed $expected): void { } public static function dataProviderToValue(): \Iterator { - // String constants. yield ['true', TRUE]; yield ['false', FALSE]; yield ['null', NULL]; - // Numeric values. yield ['123', 123]; yield ['0', 0]; yield ['-456', -456]; - // Regular strings. yield ['regular_string', 'regular_string']; yield ['non-numeric', 'non-numeric']; - // List values (contains comma). yield ['item1,item2,item3', ['item1', 'item2', 'item3']]; yield ['single,item', ['single', 'item']]; } @@ -301,16 +284,13 @@ public function testGetFromDotenvFileNotReadable(): void { } public function testGetFromDotenvReturnsParsedValue(): void { - // Test the case when environment variable is not set but .env file exists. $content = "TEST_VAR=dotenv_value"; $filename = $this->createFixtureEnvFile($content); $dir = dirname($filename); - // Move the temp file to be named .env in the directory. $dotenv_file = $dir . '/.env'; rename($filename, $dotenv_file); - // Ensure no environment variable is set by clearing any existing value. static::envUnset('TEST_VAR'); $result = Env::getFromDotenv('TEST_VAR', $dir); @@ -327,7 +307,6 @@ public function testWriteValueDotenvFileNotReadable(): void { } public function testWriteValueDotenvFileReadFailure(): void { - // Create a file we can't read. $filename = $this->createFixtureEnvFile('VAR=value'); chmod($filename, 0000); @@ -338,14 +317,12 @@ public function testWriteValueDotenvFileReadFailure(): void { Env::writeValueDotenv('TEST_VAR', 'value', $filename); } finally { - // Clean up. chmod($filename, 0644); File::remove($filename); } } public function testWriteValueDotenvAddNewVariableToFileWithoutNewline(): void { - // No trailing newline. $filename = $this->createFixtureEnvFile('EXISTING_VAR=value'); Env::writeValueDotenv('NEW_VAR', 'new_value', $filename); @@ -358,7 +335,6 @@ public function testWriteValueDotenvAddNewVariableToFileWithoutNewline(): void { } public function testWriteValueDotenvReplaceVariableToFileWithoutNewline(): void { - // No trailing newline. $filename = $this->createFixtureEnvFile('EXISTING_VAR=old_value'); Env::writeValueDotenv('NEW_VAR', 'new value with spaces', $filename); @@ -373,7 +349,6 @@ public function testWriteValueDotenvReplaceVariableToFileWithoutNewline(): void public function testWriteValueDotenvAddEmptyVariable(): void { $filename = $this->createFixtureEnvFile("EXISTING_VAR=value\n"); - // Test adding a variable that doesn't exist with null value. Env::writeValueDotenv('NEW_VAR', NULL, $filename); $content = file_get_contents($filename); @@ -384,11 +359,8 @@ public function testWriteValueDotenvAddEmptyVariable(): void { } public function testWriteValueDotenvAddEmptyVariableToFileWithoutNewline(): void { - // No trailing newline. $filename = $this->createFixtureEnvFile('EXISTING_VAR=value'); - // Test adding a variable that doesn't exist with null value to a file - // without newline. Env::writeValueDotenv('NEW_VAR', NULL, $filename); $content = file_get_contents($filename); @@ -411,7 +383,6 @@ public function testWriteValueDotenvWithEnabled(string $initial_content, string } public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { - // Test commenting out an active variable. yield 'disable active variable' => [ "VAR=active_value\n", 'VAR', @@ -419,7 +390,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { FALSE, "# VAR=active_value\n", ]; - // Test activating a commented variable. yield 'enable commented variable' => [ "# VAR=commented_value\n", 'VAR', @@ -427,7 +397,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { TRUE, "VAR=new_value\n", ]; - // Test updating and commenting out an active variable. yield 'disable and update active variable' => [ "VAR=old_value\n", 'VAR', @@ -435,7 +404,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { FALSE, "# VAR=new_value\n", ]; - // Test updating and activating a commented variable. yield 'enable and update commented variable' => [ "# VAR=old_value\n", 'VAR', @@ -443,7 +411,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { TRUE, "VAR=new_value\n", ]; - // Test adding new disabled variable. yield 'add new disabled variable' => [ "EXISTING=value\n", 'NEW_VAR', @@ -451,7 +418,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { FALSE, "EXISTING=value\n# NEW_VAR=new_value\n", ]; - // Test adding new active variable (default behavior). yield 'add new active variable' => [ "EXISTING=value\n", 'NEW_VAR', @@ -459,7 +425,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { TRUE, "EXISTING=value\nNEW_VAR=new_value\n", ]; - // Test with commented variable with spaces after #. yield 'update variable commented with spaces' => [ "# VAR=old_value\n", 'VAR', @@ -467,7 +432,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { TRUE, "VAR=new_value\n", ]; - // Test disabled with NULL value (empty). yield 'disabled empty variable' => [ "EXISTING=value\n", 'NEW_VAR', @@ -475,7 +439,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { FALSE, "EXISTING=value\n# NEW_VAR=\n", ]; - // Test active with NULL value (empty). yield 'active empty variable' => [ "EXISTING=value\n", 'NEW_VAR', @@ -483,7 +446,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { TRUE, "EXISTING=value\nNEW_VAR=\n", ]; - // Test disabling variable with special characters. yield 'disable variable with special chars' => [ "VAR=value\n", 'VAR', @@ -491,7 +453,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { FALSE, "# VAR=\"value with spaces\"\n", ]; - // Test enabling variable with special characters. yield 'enable variable with special chars' => [ "# VAR=old\n", 'VAR', @@ -499,7 +460,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { TRUE, "VAR=\"value with spaces\"\n", ]; - // Test with multiple variables, disable one. yield 'disable one among multiple variables' => [ "VAR1=value1\nVAR2=value2\nVAR3=value3\n", 'VAR2', @@ -507,7 +467,6 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { FALSE, "VAR1=value1\n# VAR2=new_value2\nVAR3=value3\n", ]; - // Test with multiple variables, enable commented one. yield 'enable one among multiple variables' => [ "VAR1=value1\n# VAR2=value2\nVAR3=value3\n", 'VAR2', @@ -518,8 +477,7 @@ public static function dataProviderWriteValueDotenvWithEnabled(): \Iterator { } public function testParseDotenvFileGetContentsFailure(): void { - // Create a directory instead of a file (will cause file_get_contents - // to fail). + // A directory in place of the file makes file_get_contents() fail. $dirname = tempnam(sys_get_temp_dir(), '.env'); File::remove($dirname); mkdir($dirname); diff --git a/.vortex/installer/tests/Unit/Utils/FileManagerTest.php b/.vortex/installer/tests/Unit/Utils/FileManagerTest.php index db2cce207..43faef823 100644 --- a/.vortex/installer/tests/Unit/Utils/FileManagerTest.php +++ b/.vortex/installer/tests/Unit/Utils/FileManagerTest.php @@ -14,9 +14,6 @@ use DrevOps\VortexInstaller\Utils\UpdateRegistry; use PHPUnit\Framework\Attributes\CoversClass; -/** - * Tests for the FileManager class. - */ #[CoversClass(FileManager::class)] class FileManagerTest extends UnitTestCase { @@ -35,9 +32,6 @@ public function testConstructor(): void { $this->assertInstanceOf(FileManager::class, $fm); } - /** - * Tests for prepareDestination(). - */ public function testPrepareDestinationExistingDirWithGit(): void { $destination = self::$sut; mkdir($destination . '/.git', 0777, TRUE); @@ -88,9 +82,6 @@ public function testPrepareDestinationCreatesNewDir(): void { $this->assertTrue($has_git_msg); } - /** - * Tests for copyFiles(). - */ public function testCopyFilesCopiesToDestination(): void { $src = self::$sut . '/src_copy'; $destination = self::$sut . '/dst_copy'; @@ -158,7 +149,6 @@ public function testCopyFilesHandlesEmptySrc(): void { $config = new Config('/tmp/root', $destination, $src); $fm = new FileManager($config); - // Should not throw. $fm->copyFiles(); $this->addToAssertionCount(1); @@ -438,10 +428,9 @@ public function testCopyFilesKeepsHarnessPaths(): void { } public function testCopyFilesRemovesObsoleteScriptsVortex(): void { - // Simulate an upgrade from a Vortex version that shipped scripts at - // 'scripts/vortex/' before they were extracted into the - // 'drevops/vortex-tooling' Composer package. The legacy directory must - // be removed from the destination after the copy. + // The destination mimics an upgrade from a Vortex version that shipped + // scripts at 'scripts/vortex/'; the 'drevops/vortex-tooling' Composer + // package ships them instead, so the copy removes the legacy directory. $src = self::$sut . '/src_obsolete'; $destination = self::$sut . '/dst_obsolete'; mkdir($src, 0777, TRUE); @@ -467,42 +456,11 @@ public function testRemoveObsoletePathsSilentOnMissing(): void { $config = new Config('/tmp/root', $destination, '/tmp/tmp'); $fm = new FileManager($config); - // Should not throw when there is nothing to remove. $fm->removeObsoletePaths(); $this->addToAssertionCount(1); } - /** - * Snapshot a stubbed download of the version the project runs. - * - * @param \DrevOps\VortexInstaller\Utils\FileManager $fm - * The file manager to snapshot into. - * @param string $destination - * The project directory. - * @param array $files - * Content the previous version installed, keyed by relative path. - * @param callable|null $render - * Callback turning the download into installable content. - */ - protected function stubPreviousTemplate(FileManager $fm, string $destination, array $files, ?callable $render = NULL): void { - File::dump($destination . '/README.md', '[![Vortex](https://img.shields.io/badge/Vortex-1.40.0-65ACBC.svg)](https://github.com/drevops/vortex)'); - - $downloader = $this->createStub(RepositoryDownloader::class); - $downloader->method('download')->willReturnCallback(function (Artifact $artifact, ?string $dir = NULL) use ($files): string { - foreach ($files as $path => $contents) { - File::dump($dir . '/' . $path, $contents); - } - - return $artifact->getRef(); - }); - - $fm->snapshotPreviousTemplate($downloader, Artifact::create('https://github.com/drevops/vortex.git', '1.40.0'), $render); - } - - /** - * Tests for prepareDemo(). - */ public function testPrepareDemoNotDemoMode(): void { $config = new Config('/tmp/root', self::$sut, '/tmp/tmp'); $fm = new FileManager($config); @@ -609,4 +567,31 @@ public function testPrepareDemoCreatesDataDir(): void { $this->assertTrue($has_created_msg); } + /** + * Snapshot a stubbed download of the version the project runs. + * + * @param \DrevOps\VortexInstaller\Utils\FileManager $fm + * The file manager to snapshot into. + * @param string $destination + * The project directory. + * @param array $files + * Content the previous version installed, keyed by relative path. + * @param callable|null $render + * Callback turning the download into installable content. + */ + protected function stubPreviousTemplate(FileManager $fm, string $destination, array $files, ?callable $render = NULL): void { + File::dump($destination . '/README.md', '[![Vortex](https://img.shields.io/badge/Vortex-1.40.0-65ACBC.svg)](https://github.com/drevops/vortex)'); + + $downloader = $this->createStub(RepositoryDownloader::class); + $downloader->method('download')->willReturnCallback(function (Artifact $artifact, ?string $dir = NULL) use ($files): string { + foreach ($files as $path => $contents) { + File::dump($dir . '/' . $path, $contents); + } + + return $artifact->getRef(); + }); + + $fm->snapshotPreviousTemplate($downloader, Artifact::create('https://github.com/drevops/vortex.git', '1.40.0'), $render); + } + } diff --git a/.vortex/installer/tests/Unit/Utils/FileTest.php b/.vortex/installer/tests/Unit/Utils/FileTest.php index df402930d..af8d2fc1a 100644 --- a/.vortex/installer/tests/Unit/Utils/FileTest.php +++ b/.vortex/installer/tests/Unit/Utils/FileTest.php @@ -38,9 +38,7 @@ public function testToRelative(string $path, ?string $base, string $expected): v } public static function dataProviderToRelative(): \Iterator { - // Get the current working directory for test cases. $cwd = getcwd(); - // Test cases with explicit base path. yield 'absolute path with base' => [ '/var/www/project/file.txt', '/var/www', @@ -71,7 +69,6 @@ public static function dataProviderToRelative(): \Iterator { '/var/www/project', '/var/www/other/file.php', ]; - // Test cases with NULL base (should use current working directory). yield 'absolute path with null base' => [ $cwd . '/test/file.txt', NULL, @@ -92,7 +89,6 @@ public static function dataProviderToRelative(): \Iterator { NULL, 'test/file.txt', ]; - // Edge cases. yield 'empty path with base - resolves to base' => [ '', '/var/www', diff --git a/.vortex/installer/tests/Unit/Utils/GitTest.php b/.vortex/installer/tests/Unit/Utils/GitTest.php index 3f685d24d..d66c2e605 100644 --- a/.vortex/installer/tests/Unit/Utils/GitTest.php +++ b/.vortex/installer/tests/Unit/Utils/GitTest.php @@ -4,75 +4,17 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; -use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; +use CzProject\GitPhp\GitRepository; use CzProject\GitPhp\RunnerResult; -use PHPUnit\Framework\Attributes\CoversClass; -use PHPUnit\Framework\Attributes\DataProvider; +use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; use DrevOps\VortexInstaller\Utils\File; use DrevOps\VortexInstaller\Utils\Git; -use CzProject\GitPhp\GitRepository; +use PHPUnit\Framework\Attributes\CoversClass; +use PHPUnit\Framework\Attributes\DataProvider; -/** - * Class GitTest. - * - * GitTest fixture class. - */ #[CoversClass(Git::class)] class GitTest extends UnitTestCase { - /** - * Create a temporary git repository for testing. - * - * @param bool $with_remote - * Whether to add a remote to the repository. - * @param bool $with_commits - * Whether to add commits to the repository. - * - * @return array{string, \DrevOps\VortexInstaller\Utils\Git} - * Array with temp directory path and Git object. - */ - protected function createTempGitRepo(bool $with_remote = FALSE, bool $with_commits = FALSE): array { - $temp_dir = sys_get_temp_dir() . '/git_test_' . uniqid(); - mkdir($temp_dir); - - // Initialize the git repository and create our Git wrapper. - Git::init($temp_dir); - $repo = new Git($temp_dir); - - if ($with_commits) { - // Set git config locally for this repository to avoid CI issues. - $repo->run('config', 'user.name', 'Test User'); - $repo->run('config', 'user.email', 'test@example.com'); - - // Create a test file and make initial commit. - file_put_contents($temp_dir . '/test.txt', 'test content'); - $repo->addAllChanges(); - $repo->commit('Initial commit'); - - // Add another file and commit. - file_put_contents($temp_dir . '/another.txt', 'another test'); - $repo->addAllChanges(); - $repo->commit('Second commit'); - } - - if ($with_remote) { - // Add test remotes. - $repo->addRemote('origin', 'https://github.com/owner/repo.git'); - $repo->addRemote('upstream', 'https://github.com/upstream/repo.git'); - } - - return [$temp_dir, $repo]; - } - - /** - * Clean up temporary git repository. - */ - protected function cleanupTempGitRepo(string $temp_dir): void { - if (is_dir($temp_dir)) { - File::remove($temp_dir); - } - } - #[DataProvider('dataProviderExtractOwnerRepo')] public function testExtractOwnerRepo(string $uri, ?string $expected): void { $this->assertSame($expected, Git::extractOwnerRepo($uri)); @@ -108,11 +50,9 @@ public function testRun(): void { [$temp_dir, $repo] = $this->createTempGitRepo(FALSE, TRUE); try { - // Test that run method works and adds --no-pager. $result = $repo->run('status', '--porcelain'); $this->assertInstanceOf(RunnerResult::class, $result); - // Test with another command. $result = $repo->run('log', '--oneline', '--max-count=1'); $this->assertInstanceOf(RunnerResult::class, $result); } @@ -202,4 +142,48 @@ public function testGetLastShortCommitId(): void { } } + /** + * @param bool $with_remote + * Whether to add a remote to the repository. + * @param bool $with_commits + * Whether to add commits to the repository. + * + * @return array{string, \DrevOps\VortexInstaller\Utils\Git} + * Array with temp directory path and Git object. + */ + protected function createTempGitRepo(bool $with_remote = FALSE, bool $with_commits = FALSE): array { + $temp_dir = sys_get_temp_dir() . '/git_test_' . uniqid(); + mkdir($temp_dir); + + Git::init($temp_dir); + $repo = new Git($temp_dir); + + if ($with_commits) { + // CI runners have no global git identity, so commits need local config. + $repo->run('config', 'user.name', 'Test User'); + $repo->run('config', 'user.email', 'test@example.com'); + + file_put_contents($temp_dir . '/test.txt', 'test content'); + $repo->addAllChanges(); + $repo->commit('Initial commit'); + + file_put_contents($temp_dir . '/another.txt', 'another test'); + $repo->addAllChanges(); + $repo->commit('Second commit'); + } + + if ($with_remote) { + $repo->addRemote('origin', 'https://github.com/owner/repo.git'); + $repo->addRemote('upstream', 'https://github.com/upstream/repo.git'); + } + + return [$temp_dir, $repo]; + } + + protected function cleanupTempGitRepo(string $temp_dir): void { + if (is_dir($temp_dir)) { + File::remove($temp_dir); + } + } + } diff --git a/.vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php b/.vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php index 365c57aa0..5f398fd1f 100644 --- a/.vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php +++ b/.vortex/installer/tests/Unit/Utils/JsonManipulatorTest.php @@ -14,9 +14,6 @@ #[CoversClass(JsonManipulator::class)] class JsonManipulatorTest extends UnitTestCase { - /** - * Sample JSON content for testing. - */ protected const SAMPLE_JSON = '{ "name": "test/package", "description": "A test package", @@ -38,24 +35,12 @@ class JsonManipulatorTest extends UnitTestCase { } }'; - /** - * Invalid JSON content for testing error cases. - */ protected const INVALID_JSON = '{ "name": "test/package", "description": "A test package" "invalid": missing comma }'; - /** - * Create a temporary JSON file for testing. - */ - protected function createTempJsonFile(string $content): string { - $temp_file = tempnam(sys_get_temp_dir(), 'json_test_'); - file_put_contents($temp_file, $content); - return $temp_file; - } - public function testConstructor(): void { $manipulator = new JsonManipulator(self::SAMPLE_JSON); $this->assertInstanceOf(JsonManipulator::class, $manipulator); @@ -113,17 +98,14 @@ public function testGetProperty(string $property_name, mixed $expected): void { } public static function dataProviderGetProperty(): \Iterator { - // Top-level properties. yield 'name property' => ['name', 'test/package']; yield 'description property' => ['description', 'A test package']; yield 'version property' => ['version', '1.0.0']; - // Nested object properties. yield 'require.php' => ['require.php', '^8.1']; yield 'require.symfony/console' => ['require.symfony/console', '^6.0']; yield 'require-dev.phpunit/phpunit' => ['require-dev.phpunit/phpunit', '^9.0']; yield 'autoload.psr-4.Test\\' => ['autoload.psr-4.Test\\', 'src/']; yield 'scripts.test' => ['scripts.test', 'phpunit']; - // Entire objects. yield 'require object' => [ 'require', ['php' => '^8.1', 'symfony/console' => '^6.0'], @@ -132,12 +114,10 @@ public static function dataProviderGetProperty(): \Iterator { 'autoload.psr-4', ['Test\\' => 'src/'], ]; - // Non-existent properties. yield 'nonexistent top-level' => ['nonexistent', NULL]; yield 'nonexistent nested' => ['require.nonexistent', NULL]; yield 'nonexistent deep nested' => ['require.nested.deep', NULL]; yield 'empty property name' => ['', NULL]; - // Edge cases with dots. yield 'property with trailing dot' => ['require.', NULL]; yield 'property with multiple dots' => ['require..php', NULL]; } @@ -214,16 +194,13 @@ public function testGetPropertyArrayAccess(): void { $manipulator = new JsonManipulator($json_with_arrays); - // Should return the entire array. $users = $manipulator->getProperty('users'); $this->assertIsArray($users); $this->assertCount(2, $users); - // Test accessing array indices with dot notation. $result = $manipulator->getProperty('users.0'); $this->assertSame(['name' => 'John', 'age' => 30], $result); - // Test accessing primitive values inside objects inside arrays. $john_name = $manipulator->getProperty('users.0.name'); $this->assertSame('John', $john_name); @@ -231,4 +208,10 @@ public function testGetPropertyArrayAccess(): void { $this->assertSame(25, $jane_age); } + protected function createTempJsonFile(string $content): string { + $temp_file = tempnam(sys_get_temp_dir(), 'json_test_'); + file_put_contents($temp_file, $content); + return $temp_file; + } + } diff --git a/.vortex/installer/tests/Unit/Utils/NpmLockTest.php b/.vortex/installer/tests/Unit/Utils/NpmLockTest.php index ecbdd5ac0..58b3d9be1 100644 --- a/.vortex/installer/tests/Unit/Utils/NpmLockTest.php +++ b/.vortex/installer/tests/Unit/Utils/NpmLockTest.php @@ -226,8 +226,6 @@ public function testSyncThrowsWhenLockIsNotWritable(): void { } /** - * Write a manifest and its lock file into a directory of their own. - * * @return string * Path to the manifest. */ diff --git a/.vortex/installer/tests/Unit/Utils/OptionsResolverTest.php b/.vortex/installer/tests/Unit/Utils/OptionsResolverTest.php index f3c951f12..9e47c3256 100644 --- a/.vortex/installer/tests/Unit/Utils/OptionsResolverTest.php +++ b/.vortex/installer/tests/Unit/Utils/OptionsResolverTest.php @@ -13,9 +13,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Process\ExecutableFinder; -/** - * Tests for the OptionsResolver class. - */ #[CoversClass(OptionsResolver::class)] class OptionsResolverTest extends UnitTestCase { @@ -33,7 +30,6 @@ public function testCheckRequirementsPassesWhenAllPresent(): void { OptionsResolver::checkRequirements($finder); - // No exception means success. $this->addToAssertionCount(1); } @@ -205,7 +201,6 @@ public function testResolveSetsIsVortexProject(): void { [$config] = OptionsResolver::resolve($options); - // The SUT directory should not be a Vortex project. $this->assertNotNull($config->get(Config::IS_VORTEX_PROJECT)); } @@ -228,7 +223,6 @@ public function testResolveDemoDbDownloadSkipFromEnv(): void { } public function testResolveDestinationPriority(): void { - // Option takes priority over root. $destination = self::$sut; $options = self::defaultOptions([ 'destination' => $destination, @@ -241,8 +235,6 @@ public function testResolveDestinationPriority(): void { } /** - * Build a default options array with overrides. - * * @param array $overrides * Options to override. * diff --git a/.vortex/installer/tests/Unit/Utils/StringsTest.php b/.vortex/installer/tests/Unit/Utils/StringsTest.php index d9b3ee1f5..53cb82094 100644 --- a/.vortex/installer/tests/Unit/Utils/StringsTest.php +++ b/.vortex/installer/tests/Unit/Utils/StringsTest.php @@ -5,13 +5,10 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; +use DrevOps\VortexInstaller\Utils\Strings; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; -use DrevOps\VortexInstaller\Utils\Strings; -/** - * Tests for the Strings class. - */ #[CoversClass(Strings::class)] class StringsTest extends UnitTestCase { @@ -531,7 +528,6 @@ public function testIsRegex(string $value, mixed $expected): void { public static function dataProviderIsRegex(): \Iterator { yield ['', FALSE]; - // Valid regular expressions. yield ["/^[a-z]$/", TRUE]; yield ["#[a-z]*#i", TRUE]; // Invalid regular expressions (wrong delimiters or syntax). @@ -543,12 +539,9 @@ public static function dataProviderIsRegex(): \Iterator { yield ["[a-z]+/", FALSE]; yield ["{[a-z]*", FALSE]; yield ["(a-z]", FALSE]; - // Edge cases. // Valid, but '*' as delimiter would be invalid. yield ["/a*/", TRUE]; - // Empty string. yield ["", FALSE]; - // Just delimiters, no pattern. yield ["//", FALSE]; yield ['web/', FALSE]; yield ['web\/', FALSE]; diff --git a/.vortex/installer/tests/Unit/Utils/TuiTest.php b/.vortex/installer/tests/Unit/Utils/TuiTest.php index c79b1a0ba..ca9981a59 100644 --- a/.vortex/installer/tests/Unit/Utils/TuiTest.php +++ b/.vortex/installer/tests/Unit/Utils/TuiTest.php @@ -11,28 +11,22 @@ use PHPUnit\Framework\Attributes\DataProvider; use Symfony\Component\Console\Output\BufferedOutput; -/** - * Tests for the Tui class. - */ #[CoversClass(Tui::class)] class TuiTest extends UnitTestCase { public function testInit(): void { $output = new BufferedOutput(); - // Test basic initialization. Tui::init($output); $this->assertSame($output, Tui::output()); - // Test with non-interactive mode. Tui::init($output, FALSE); $this->assertSame($output, Tui::output()); } public function testOutputNotInitialized(): void { - // Since the output property is typed and doesn't allow null, - // we can't easily test the uninitialized state. - // Instead, we'll test that init properly sets the output. + // The typed output property cannot hold NULL, so the uninitialized state + // is not reachable; init() setting the output is asserted instead. $output = new BufferedOutput(); Tui::init($output); $this->assertSame($output, Tui::output()); @@ -113,8 +107,7 @@ public static function dataProviderColorMethodsMultiline(): \Iterator { } public function testEscapeMultilineViaPublicMethod(): void { - // Since escapeMultiline is protected, test it via public methods that - // use it. + // escapeMultiline() is protected, so green() exercises it. $text = <<<'TEXT' Line 1 Line 2 @@ -161,7 +154,6 @@ public function testBox( $output = new BufferedOutput(); Tui::init($output); - // Mock terminal width if specified. if ($terminal_width !== NULL) { static::envSet('COLUMNS', (string) $terminal_width); } @@ -175,7 +167,6 @@ public function testBox( $actual = $output->fetch(); - // Strip ANSI color codes using the same method as Strings::strlenPlain() $actual_clean = Strings::stripAnsiColors($actual); $expected_clean = Strings::stripAnsiColors($expected_output); @@ -507,7 +498,6 @@ public function testUtfPadding( ?string $term_program, string $expected_padding, ): void { - // Set test environment variables. if ($terminal_emulator !== NULL) { static::envSet('TERMINAL_EMULATOR', $terminal_emulator); } @@ -522,7 +512,6 @@ public function testUtfPadding( static::envUnset('TERM_PROGRAM'); } - // Use reflection to access the protected method. $reflection = new \ReflectionClass(Tui::class); $method = $reflection->getMethod('utfPadding'); @@ -531,7 +520,6 @@ public function testUtfPadding( } public static function dataProviderUtfPadding(): \Iterator { - // JetBrains terminal conditions. yield 'JetBrains with 1-byte UTF-8 char' => [ // 2 bytes, 1 mb_strlen 'char' => 'é', @@ -559,7 +547,6 @@ public static function dataProviderUtfPadding(): \Iterator { 'term_program' => NULL, 'expected_padding' => '', ]; - // Apple Terminal conditions. yield 'Apple Terminal with multi-byte char under 8 bytes' => [ // 2 chars × 3 bytes = 6 bytes total, mblen=2, len=6 < 8 'char' => 'あい', @@ -581,7 +568,6 @@ public static function dataProviderUtfPadding(): \Iterator { 'term_program' => 'Apple_Terminal', 'expected_padding' => '', ]; - // No special terminal conditions. yield 'No special terminal with UTF-8' => [ 'char' => '🌟', 'terminal_emulator' => NULL, @@ -594,7 +580,6 @@ public static function dataProviderUtfPadding(): \Iterator { 'term_program' => NULL, 'expected_padding' => '', ]; - // Both terminals set - JetBrains takes precedence. yield 'Both JetBrains and Apple set' => [ 'char' => 'é', 'terminal_emulator' => 'JetBrains-IDE', @@ -602,7 +587,6 @@ public static function dataProviderUtfPadding(): \Iterator { // JetBrains condition should trigger first. 'expected_padding' => ' ', ]; - // Empty/null environment values. yield 'Empty environment values' => [ 'char' => 'é', 'terminal_emulator' => '', @@ -622,9 +606,6 @@ public function testCenter( $this->assertSame($expected, $actual); } - /** - * Data provider for testCenter. - */ public static function dataProviderCenter(): \Iterator { yield 'single line text with default width' => [ 'text' => 'Hello', @@ -769,7 +750,6 @@ public function testNormalizeText(string $input, string $expected): void { } public static function dataProviderNormalizeText(): \Iterator { - // Test whitespace collapsing. yield 'multiple spaces' => [ 'input' => 'Hello world', 'expected' => 'Hello world', @@ -787,7 +767,6 @@ public static function dataProviderNormalizeText(): \Iterator { 'input' => 'éHello world', 'expected' => 'éHello world', ]; - // Test ASCII text processing (with UTF padding). yield 'simple ASCII text' => [ 'input' => 'Hello world', 'expected' => 'Hello world', @@ -810,9 +789,6 @@ public static function dataProviderNormalizeText(): \Iterator { ]; } - /** - * Test setOutput method. - */ public function testSetOutput(): void { $output1 = new BufferedOutput(); $output2 = new BufferedOutput(); @@ -824,9 +800,6 @@ public function testSetOutput(): void { $this->assertSame($output2, Tui::output()); } - /** - * Test success method. - */ public function testSuccess(): void { $output = new BufferedOutput(); Tui::init($output); @@ -837,9 +810,6 @@ public function testSuccess(): void { $this->assertStringContainsString('Operation succeeded', $actual); } - /** - * Test line method. - */ public function testLine(): void { $output = new BufferedOutput(); Tui::init($output); @@ -850,9 +820,6 @@ public function testLine(): void { $this->assertStringContainsString('Test line', $actual); } - /** - * Test line method with custom padding. - */ public function testLineWithPadding(): void { $output = new BufferedOutput(); Tui::init($output); @@ -863,14 +830,10 @@ public function testLineWithPadding(): void { $this->assertStringContainsString(' Test line', $actual); } - /** - * Test confirm in non-interactive mode (returns default). - */ public function testConfirmNonInteractive(): void { $output = new BufferedOutput(); Tui::init($output, FALSE); - // In non-interactive mode, confirm should return the default value. $result = Tui::confirm('Confirm action?', TRUE); $this->assertTrue($result); @@ -878,14 +841,10 @@ public function testConfirmNonInteractive(): void { $this->assertFalse($result); } - /** - * Test getChar in non-interactive mode. - */ public function testGetCharNonInteractive(): void { $output = new BufferedOutput(); Tui::init($output, FALSE); - // In non-interactive mode, getChar should return empty string. $result = Tui::getChar(); $this->assertEquals('', $result); } diff --git a/.vortex/installer/tests/Unit/Utils/UpdateRegistryTest.php b/.vortex/installer/tests/Unit/Utils/UpdateRegistryTest.php index 543a475ad..b2d3761e0 100644 --- a/.vortex/installer/tests/Unit/Utils/UpdateRegistryTest.php +++ b/.vortex/installer/tests/Unit/Utils/UpdateRegistryTest.php @@ -9,9 +9,6 @@ use DrevOps\VortexInstaller\Utils\UpdateRegistry; use PHPUnit\Framework\Attributes\CoversClass; -/** - * Tests for the UpdateRegistry class. - */ #[CoversClass(UpdateRegistry::class)] class UpdateRegistryTest extends UnitTestCase { diff --git a/.vortex/installer/tests/Unit/Utils/ValidatorTest.php b/.vortex/installer/tests/Unit/Utils/ValidatorTest.php index 98ba5566d..9abbc4aa5 100644 --- a/.vortex/installer/tests/Unit/Utils/ValidatorTest.php +++ b/.vortex/installer/tests/Unit/Utils/ValidatorTest.php @@ -5,9 +5,9 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; +use DrevOps\VortexInstaller\Utils\Validator; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; -use DrevOps\VortexInstaller\Utils\Validator; /** * Class InstallerHelpersTest. @@ -104,13 +104,11 @@ public function testIsGitCommitSha(string $sha, bool $expected): void { } public static function dataProviderIsGitCommitSha(): \Iterator { - // Valid SHA-1 hashes (40 hexadecimal characters) yield 'valid lowercase SHA' => ['a1b2c3d4e5f6789012345678901234567890abcd', TRUE]; yield 'valid uppercase SHA' => ['A1B2C3D4E5F6789012345678901234567890ABCD', TRUE]; yield 'valid mixed case SHA' => ['a1B2c3D4e5F6789012345678901234567890AbCd', TRUE]; yield 'valid all numbers SHA' => ['1234567890123456789012345678901234567890', TRUE]; yield 'valid all letters SHA' => ['abcdefabcdefabcdefabcdefabcdefabcdefabcd', TRUE]; - // Invalid SHA hashes. yield 'invalid too short' => ['a1b2c3d4e5f6789012345678901234567890abc', FALSE]; yield 'invalid too long' => ['a1b2c3d4e5f6789012345678901234567890abcdef', FALSE]; yield 'invalid with non-hex characters' => ['a1b2c3d4e5f6789012345678901234567890abcg', FALSE]; @@ -129,7 +127,6 @@ public function testIsGitCommitShaShort(string $sha_short, bool $expected): void } public static function dataProviderIsGitCommitShaShort(): \Iterator { - // Valid short SHA-1 hashes (7 hexadecimal characters) yield 'valid lowercase short SHA' => ['a1b2c3d', TRUE]; yield 'valid uppercase short SHA' => ['A1B2C3D', TRUE]; yield 'valid mixed case short SHA' => ['a1B2c3D', TRUE]; @@ -137,7 +134,6 @@ public static function dataProviderIsGitCommitShaShort(): \Iterator { yield 'valid all letters short SHA' => ['abcdef0', TRUE]; yield 'valid with f characters' => ['fffffff', TRUE]; yield 'valid with 0 characters' => ['0000000', TRUE]; - // Invalid short SHA hashes. yield 'invalid too short (6 chars)' => ['a1b2c3', FALSE]; yield 'invalid too short (1 char)' => ['a', FALSE]; yield 'invalid too long (8 chars)' => ['a1b2c3d4', FALSE]; @@ -160,13 +156,10 @@ public function testIsGitRef(string $ref, bool $expected): void { } public static function dataProviderIsGitRef(): \Iterator { - // Special keywords. yield 'special keyword stable' => ['stable', TRUE]; yield 'special keyword HEAD' => ['HEAD', TRUE]; - // Commit hashes (already tested, but included for completeness). yield 'valid 40-char commit hash' => ['a1b2c3d4e5f6789012345678901234567890abcd', TRUE]; yield 'valid 7-char commit hash' => ['a1b2c3d', TRUE]; - // Semantic versioning tags. yield 'semver without prefix' => ['1.2.3', TRUE]; yield 'semver with v prefix' => ['v1.2.3', TRUE]; yield 'semver with patch zero' => ['2.0.0', TRUE]; @@ -176,30 +169,24 @@ public static function dataProviderIsGitRef(): \Iterator { yield 'semver with build metadata' => ['1.2.3+20130313144700', TRUE]; yield 'semver with build metadata simple' => ['1.2.3+build', TRUE]; yield 'semver with pre-release and build' => ['1.2.3-alpha.1+build.123', TRUE]; - // Calendar versioning tags. yield 'calver YY.MM.PATCH' => ['24.10.0', TRUE]; yield 'calver YY.MM.PATCH with higher version' => ['25.11.0', TRUE]; yield 'calver YYYY.MM.PATCH' => ['2024.12.3', TRUE]; - // Drupal-style versioning. yield 'drupal 8.x version' => ['8.x-1.10', TRUE]; yield 'drupal 9.x version' => ['9.x-2.3', TRUE]; yield 'drupal 10.x version' => ['10.x-1.0', TRUE]; - // Hybrid versioning (SemVer with CalVer build metadata). yield 'semver+calver hybrid' => ['1.0.0+2025.11.0', TRUE]; yield 'semver+calver hybrid v2' => ['1.2.0+2025.12.0', TRUE]; yield 'semver+calver with pre-release' => ['1.0.0-beta+2025.11.0', TRUE]; - // Pre-release tags. yield 'pre-release rc' => ['1.x-rc1', TRUE]; yield 'pre-release beta' => ['2.0.0-beta', TRUE]; yield 'pre-release alpha' => ['3.0.0-alpha', TRUE]; - // Branch names. yield 'branch main' => ['main', TRUE]; yield 'branch master' => ['master', TRUE]; yield 'branch develop' => ['develop', TRUE]; yield 'branch feature with slash' => ['feature/my-feature', TRUE]; yield 'branch bugfix with slash' => ['bugfix/fix-123', TRUE]; yield 'branch release with slash' => ['release/1.0', TRUE]; - // Invalid formats - special characters. yield 'invalid with @' => ['invalid@ref', FALSE]; yield 'invalid with ^' => ['invalid^ref', FALSE]; yield 'invalid with ~' => ['invalid~ref', FALSE]; @@ -210,14 +197,12 @@ public static function dataProviderIsGitRef(): \Iterator { yield 'invalid with space' => ['invalid ref', FALSE]; yield 'invalid with backslash' => ['invalid\ref', FALSE]; yield 'invalid with @{' => ['invalid@{ref', FALSE]; - // Invalid formats - starting/ending patterns. yield 'invalid starting with dot' => ['.invalid', FALSE]; yield 'invalid starting with hyphen' => ['-invalid', FALSE]; yield 'invalid ending with .lock' => ['invalid.lock', FALSE]; yield 'invalid containing ..' => ['invalid..ref', FALSE]; yield 'invalid trailing slash' => ['feature/', FALSE]; yield 'invalid consecutive slashes' => ['feature//name', FALSE]; - // Empty and edge cases. yield 'invalid empty string' => ['', FALSE]; yield 'invalid only spaces' => [' ', FALSE]; } diff --git a/.vortex/installer/tests/Unit/Utils/VersionTest.php b/.vortex/installer/tests/Unit/Utils/VersionTest.php index 40471d221..4312faabc 100644 --- a/.vortex/installer/tests/Unit/Utils/VersionTest.php +++ b/.vortex/installer/tests/Unit/Utils/VersionTest.php @@ -4,8 +4,8 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; -use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; use AlexSkrypnyk\File\File; +use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; use DrevOps\VortexInstaller\Utils\Version; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; @@ -19,8 +19,6 @@ public function testMajor(?string $version, ?int $expected): void { } /** - * Data provider for testMajor(). - * * @return \Iterator * Test data. */ @@ -44,8 +42,6 @@ public function testReleasePrefix(?string $version, ?string $expected): void { } /** - * Data provider for testReleasePrefix(). - * * @return \Iterator * Test data. */ @@ -63,8 +59,6 @@ public function testMajorFromConstraint(?string $constraint, ?int $expected): vo } /** - * Data provider for testMajorFromConstraint(). - * * @return \Iterator * Test data. */ @@ -92,8 +86,6 @@ public function testDetectProjectMajor(?string $composer_json, ?int $expected): } /** - * Data provider for testDetectProjectMajor(). - * * @return \Iterator * Test data. */ diff --git a/.vortex/installer/tests/Unit/Utils/YamlTest.php b/.vortex/installer/tests/Unit/Utils/YamlTest.php index 12dcc9969..2957f7c61 100644 --- a/.vortex/installer/tests/Unit/Utils/YamlTest.php +++ b/.vortex/installer/tests/Unit/Utils/YamlTest.php @@ -5,9 +5,9 @@ namespace DrevOps\VortexInstaller\Tests\Unit\Utils; use DrevOps\VortexInstaller\Tests\Unit\UnitTestCase; +use DrevOps\VortexInstaller\Utils\Yaml; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; -use DrevOps\VortexInstaller\Utils\Yaml; #[CoversClass(Yaml::class)] class YamlTest extends UnitTestCase { @@ -29,14 +29,6 @@ public function testValidateFile(string $yaml_content, string $expected_exceptio } } - public function testValidateFileNonExistent(): void { - $this->expectException(\RuntimeException::class); - $this->expectExceptionMessage('File does not exist or is not readable'); - - $non_existent_file = sys_get_temp_dir() . '/non_existent_file.yml'; - Yaml::validateFile($non_existent_file); - } - public static function dataProviderValidateFile(): \Iterator { yield 'valid YAML file' => [ <<expectException(\RuntimeException::class); + $this->expectExceptionMessage('File does not exist or is not readable'); + + $non_existent_file = sys_get_temp_dir() . '/non_existent_file.yml'; + Yaml::validateFile($non_existent_file); + } + #[DataProvider('dataProviderValidate')] public function testValidate(string $content, string $expected_exception_message = ''): void { if ($expected_exception_message !== '' && $expected_exception_message !== '0') { diff --git a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php index f633ca3d3..ef8c55cd4 100644 --- a/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php +++ b/.vortex/tests/phpunit/Functional/AhoyWorkflowTest.php @@ -184,14 +184,14 @@ public function testAhoyWorkflowDatabaseFromImageStorageInImage(): void { $this->assertWebpageNotContains('/', 'This test page is sourced from the Vortex database container image', 'Homepage should not show initial test content after config change'); $this->logSubstep('Reload database from the container image and assert that the initial content is restored'); - $this->cmd('ahoy reload-db', txt: "`ahoy reload-db` restarts the stack fast", tio: 60); + $this->cmd('ahoy reload-db', txt: '`ahoy reload-db` restarts the stack fast', tio: 60); // @note Redis caches are not flushed automatically on cache clear as it // may be clearing too much. // For now, we are manually clearing Redis cache after DB reload. A human // operator would make a call to do it manually depending on the hosting, // website size, traffic, etc. // @see https://www.drupal.org/project/redis/issues/2765895 - $this->cmd('ahoy flush-redis', txt: "`ahoy flush-redis` flushes Redis cache after database reload", tio: 30); + $this->cmd('ahoy flush-redis', txt: '`ahoy flush-redis` flushes Redis cache after database reload', tio: 30); $this->subtestAhoyInfo(db_image: self::VORTEX_DB_IMAGE_TEST); $this->assertWebpageContains('/', 'This test page is sourced from the Vortex database container image', 'Homepage should show initial test content after database reload'); diff --git a/.vortex/tests/phpunit/Functional/FunctionalTestCase.php b/.vortex/tests/phpunit/Functional/FunctionalTestCase.php index 18ff30a20..37abd6617 100644 --- a/.vortex/tests/phpunit/Functional/FunctionalTestCase.php +++ b/.vortex/tests/phpunit/Functional/FunctionalTestCase.php @@ -18,9 +18,6 @@ use PHPUnit\Framework\TestStatus\Error; use PHPUnit\Framework\TestStatus\Failure; -/** - * Base class for all functional tests. - */ class FunctionalTestCase extends UnitTestCase { use AssertArrayTrait; @@ -28,32 +25,27 @@ class FunctionalTestCase extends UnitTestCase { use EnvTrait; use FileAssertionsTrait; use GitTrait; + use HelpersTrait; use LocationsTrait; use ProcessTrait; use SutTrait; - use HelpersTrait; protected function setUp(): void { // Initialize locations with the project root as the base directory. self::locationsInit(File::cwd() . '/../..'); - // We use 'Star Wars'-themed test assertions, so we need to create a named - // SUT directory for the installer to gather the answers from the directory - // name. + // Test assertions are 'Star Wars'-themed, so the SUT directory is given + // a name the installer gathers the answers from. static::$sut = static::locationsMkdir(static::$workspace . '/star_wars'); - // Export the current codebase to a fixture remote repository. - // Any uncommitted changes will not be included, so make sure to commit - // any changes you want to test against. + // The export takes only the committed state, so changes under test must + // be committed first. $this->fixtureExportCodebase(static::$root, static::$repo); - // Always show logger information. $this->logSetVerbose(TRUE); - // Show process output based on the debug flags. $this->processStreamingOutput = static::isDebug(); - // Setting up logger step method prefix. static::$logStepMethodPrefix = 'subtest'; static::logSection('TEST START | ' . $this->name(), double_border: TRUE); @@ -78,7 +70,6 @@ protected function tearDown(): void { $this->log(static::locationsInfo()); } else { - // Test passed and debug mode is off → cleanup. $this->dockerCleanup(); $this->processTearDown(); } diff --git a/.vortex/tests/phpunit/Functional/InstallerTest.php b/.vortex/tests/phpunit/Functional/InstallerTest.php index 25e38dcf3..9ecaee712 100644 --- a/.vortex/tests/phpunit/Functional/InstallerTest.php +++ b/.vortex/tests/phpunit/Functional/InstallerTest.php @@ -170,46 +170,6 @@ public function testUpdateKeepsProjectAuthoredFiles(): void { } } - /** - * Add a template-owned script to the template repository. - */ - protected function addLegacyScriptToTemplate(): string { - $this->logSubstep('Add a template-owned script to the Vortex template repository'); - File::dump(static::$repo . '/scripts/provision-50-legacy.sh', "#!/usr/bin/env bash\necho 'Legacy provision step.'\n"); - $commit = $this->gitCommitAll(static::$repo, 'Added a legacy provision script to Vortex'); - $this->logNote(sprintf('Vortex version with the script: %s', $commit)); - - return $commit; - } - - /** - * Drop the template-owned script from the template repository. - */ - protected function dropLegacyScriptFromTemplate(): string { - $this->logSubstep('Drop the script from the Vortex template repository'); - File::remove(static::$repo . '/scripts/provision-50-legacy.sh'); - $commit = $this->gitCommitAll(static::$repo, 'Removed the legacy provision script from Vortex'); - $this->logNote(sprintf('Vortex version without the script: %s', $commit)); - - return $commit; - } - - /** - * Install the SUT from a given template reference. - */ - protected function installSutFrom(string $ref): void { - $this->gitInitRepo(static::$sut); - // The shipped '.gitignore' is the only ignore source these tests assert - // on, so the developer's global excludes file must not reach the SUT. - $this->gitDisableGlobalExcludes(static::$sut); - - static::$sutInstallerEnv = [ - 'VORTEX_INSTALLER_TEMPLATE_REPO' => FALSE, - 'SHELL_VERBOSITY' => FALSE, - ]; - $this->runInstaller([sprintf('--uri=%s#%s', static::$repo, $ref)]); - } - #[Group('p3')] public function testInstallFromRef(): void { $this->logSubstep('Add custom files to SUT'); @@ -268,4 +228,44 @@ public function testInstallFromRef(): void { $this->gitAssertNotClean(static::$sut, 'Git working tree should not be clean after Vortex update'); } + /** + * Add a template-owned script to the template repository. + */ + protected function addLegacyScriptToTemplate(): string { + $this->logSubstep('Add a template-owned script to the Vortex template repository'); + File::dump(static::$repo . '/scripts/provision-50-legacy.sh', "#!/usr/bin/env bash\necho 'Legacy provision step.'\n"); + $commit = $this->gitCommitAll(static::$repo, 'Added a legacy provision script to Vortex'); + $this->logNote(sprintf('Vortex version with the script: %s', $commit)); + + return $commit; + } + + /** + * Drop the template-owned script from the template repository. + */ + protected function dropLegacyScriptFromTemplate(): string { + $this->logSubstep('Drop the script from the Vortex template repository'); + File::remove(static::$repo . '/scripts/provision-50-legacy.sh'); + $commit = $this->gitCommitAll(static::$repo, 'Removed the legacy provision script from Vortex'); + $this->logNote(sprintf('Vortex version without the script: %s', $commit)); + + return $commit; + } + + /** + * Install the SUT from a given template reference. + */ + protected function installSutFrom(string $ref): void { + $this->gitInitRepo(static::$sut); + // The shipped '.gitignore' is the only ignore source these tests assert + // on, so the developer's global excludes file must not reach the SUT. + $this->gitDisableGlobalExcludes(static::$sut); + + static::$sutInstallerEnv = [ + 'VORTEX_INSTALLER_TEMPLATE_REPO' => FALSE, + 'SHELL_VERBOSITY' => FALSE, + ]; + $this->runInstaller([sprintf('--uri=%s#%s', static::$repo, $ref)]); + } + } diff --git a/.vortex/tests/phpunit/Traits/DeploymentTrait.php b/.vortex/tests/phpunit/Traits/DeploymentTrait.php index 5b1dba5be..ac47920b5 100644 --- a/.vortex/tests/phpunit/Traits/DeploymentTrait.php +++ b/.vortex/tests/phpunit/Traits/DeploymentTrait.php @@ -11,34 +11,22 @@ */ trait DeploymentTrait { - /** - * Prepare deployment source directory. - */ protected function prepareDeploymentSource(string $src_dir): void { $this->logNote('Preparing deployment source at: ' . $src_dir); File::mkdir($src_dir); } - /** - * Prepare remote repository for artifact deployment. - */ protected function prepareRemoteRepository(string $remote_dir): void { $this->logNote('Preparing remote repository at: ' . $remote_dir); File::mkdir($remote_dir); $this->gitInitRepo($remote_dir); // Configure git to accept pushes to the checked-out branch. shell_exec('git -C ' . escapeshellarg($remote_dir) . ' config receive.denyCurrentBranch updateInstead'); - // Create an initial file so we can commit. + // Create an initial file so there is something to commit. File::dump($remote_dir . '/.gitkeep', ''); $this->gitCommitAll($remote_dir, 'Initial commit'); } - /** - * Assert deployment artifact files are present. - * - * These are the files that should exist in a deployment artifact after - * the build process has completed. - */ protected function assertDeploymentFilesPresent(string $dir, string $webroot = 'web'): void { $this->logNote('Asserting deployment files are present in: ' . $dir); @@ -130,7 +118,6 @@ protected function assertDeploymentFilesPresent(string $dir, string $webroot = ' $this->assertDirectoryDoesNotExist($dir . '/' . $webroot . '/themes/custom/star_wars/fonts', 'Fonts source directory should not exist in deployment'); $this->assertDirectoryDoesNotExist($dir . '/' . $webroot . '/themes/custom/star_wars/images', 'Images source directory should not exist in deployment'); - // Config directory should exist. $this->assertDirectoryExists($dir . '/config/default', 'Config directory should exist'); // Composer.json should exist for autoloading. diff --git a/.vortex/tests/phpunit/Traits/GitTrait.php b/.vortex/tests/phpunit/Traits/GitTrait.php index 869a7fcd7..7931d81c9 100644 --- a/.vortex/tests/phpunit/Traits/GitTrait.php +++ b/.vortex/tests/phpunit/Traits/GitTrait.php @@ -11,8 +11,6 @@ use CzProject\GitPhp\GitRepository; /** - * Trait GitTrait. - * * Helpers to work with Git repositories. */ trait GitTrait { @@ -67,7 +65,6 @@ protected function gitCheckout(string $path, string $branch): void { $output = $git_exception->getRunnerResult()->getErrorOutput(); } - // Re-throw exception if it is not one of the allowed ones. if (!isset($output) || empty(array_intersect($output, $allowed_fails))) { throw $git_exception; } @@ -92,7 +89,7 @@ protected function gitCreateBranch(string $path, string $branch): void { * @param string $path * Path to the repo. */ - protected function gitReset($path): void { + protected function gitReset(string $path): void { $repo = (new Git())->open($path); $repo->run('reset', ['--hard']); $repo->run('clean', ['-dfx']); @@ -136,7 +133,7 @@ protected function gitGetAllCommits(string $path, string $format = '%s'): array * Get a range of commits. * * @param array $range - * Array of commit indexes, stating from 1. + * Array of commit indexes, starting from 1. * @param string $path * Path to the repository directory. * @@ -189,9 +186,6 @@ protected function gitAddTag(string $path, string $name, bool $annotate = FALSE) } } - /** - * Assert if path is a Git repository. - */ protected function gitAssertIsRepository(?string $path = NULL): void { $path = $path ?: File::cwd(); @@ -200,7 +194,6 @@ protected function gitAssertIsRepository(?string $path = NULL): void { $git_dir = $path . DIRECTORY_SEPARATOR . '.git'; $this->assertDirectoryExists($git_dir, sprintf('Directory %s exists, but it is not a git repository', $path)); - // Run git status to verify it's a valid git repository. $command = sprintf('git --work-tree=%s --git-dir=%s status 2>&1', escapeshellarg($path), escapeshellarg($git_dir) diff --git a/.vortex/tests/phpunit/Traits/HelpersTrait.php b/.vortex/tests/phpunit/Traits/HelpersTrait.php index 168384a0e..517983308 100644 --- a/.vortex/tests/phpunit/Traits/HelpersTrait.php +++ b/.vortex/tests/phpunit/Traits/HelpersTrait.php @@ -38,7 +38,6 @@ public function syncToHost(string|array $paths = []): void { $rel_path = ltrim($path, '/'); $container_abs = '/app/' . $rel_path; - // Probe container to check if directory, file, or missing. $path_type_cmd = sprintf( 'docker compose exec -T cli bash -lc %s', escapeshellarg( diff --git a/.vortex/tests/phpunit/Traits/ProcessTrait.php b/.vortex/tests/phpunit/Traits/ProcessTrait.php index e7dac9b3a..601861478 100644 --- a/.vortex/tests/phpunit/Traits/ProcessTrait.php +++ b/.vortex/tests/phpunit/Traits/ProcessTrait.php @@ -9,8 +9,6 @@ use Symfony\Component\Process\Process; /** - * Trait ProcessTrait. - * * Runs a test process and provides assertions for its output. */ trait ProcessTrait { @@ -34,22 +32,20 @@ public function processRun( $env += [ 'AHOY_CONFIRM_RESPONSE' => 'y', 'AHOY_CONFIRM_WAIT_SKIP' => 1, - // Credentials for the test container registry to allow fetching public - // images to overcome the throttle limit of Docker Hub, and also used - // for pushing images during the build. + // Credentials for the test container registry allow fetching public + // images past the Docker Hub throttle limit. The build also uses them + // for pushing images. 'VORTEX_CONTAINER_REGISTRY_USER' => getenv('TEST_VORTEX_CONTAINER_REGISTRY_USER') ?: '', 'VORTEX_CONTAINER_REGISTRY_PASS' => getenv('TEST_VORTEX_CONTAINER_REGISTRY_PASS') ?: '', // GitHub token for API calls to avoid rate limiting. 'GITHUB_TOKEN' => getenv('TEST_GITHUB_TOKEN') ?: '', ]; - // If process streaming is disabled, also silence the output of the - // commands. if (!$this->processStreamingOutput) { // Silence the output of the Composer commands (but still output errors). $env += ['SHELL_VERBOSITY' => -1]; - // Silence the output of the Docker Composer commands. + // Silence the output of the 'docker compose' commands. if (str_starts_with($command, 'docker compose') && !str_contains($command, '--progress')) { $command = str_replace('docker compose', 'docker compose --progress quiet', $command); } diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php index a1d4ad85e..fe1d94f48 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestAhoyTrait.php @@ -82,7 +82,6 @@ protected function subtestAhoyDotEnv(): void { $this->assertFileNotContainsString('.env', 'my_custom_var_value', '.env does not contain test values'); $this->cmdFail('ahoy cli "printenv | grep -q MY_CUSTOM_VAR"', txt: 'Custom variable does not exist inside of container.'); $this->cmdFail('ahoy cli \'echo $MY_CUSTOM_VAR | grep -q my_custom_var_value\'', '! my_custom_var_value', txt: 'Custom variable does not exist and has no value inside of container.'); - // Add variable to the .env file and apply the change to container. $this->fileAddVar('.env', 'MY_CUSTOM_VAR', 'my_custom_var_value'); $this->cmd('ahoy up cli'); $this->syncToContainer('.env'); @@ -349,9 +348,9 @@ protected function subtestAhoyProvision(): void { protected function seedCacheTableRow(): void { $this->logSubstep('Seed a cache table row before the export'); - // Drupal creates its cache tables lazily and which bins reach the database - // depends on the configured backends, so the row proving that the export - // drops cache data is written into a table this suite owns. + // Drupal creates its cache tables lazily, and which bins reach the + // database depends on the configured backends. The row proving that the + // export drops cache data is written into a table this suite owns. $seed_file = '.data/probe-cache-seed.sql'; File::dump($seed_file, "CREATE TABLE IF NOT EXISTS cache_vortex_probe (cid VARCHAR(255) NOT NULL PRIMARY KEY, data LONGBLOB);\nINSERT INTO cache_vortex_probe (cid, data) VALUES ('SEEDED_CACHE_ROW_MARKER', 'probe');\n"); $this->syncToContainer($seed_file); @@ -395,8 +394,8 @@ protected function subtestAhoyExportDb(string $filename = '', bool $is_container txt: 'Export database dump ' . ($has_argument ? sprintf("to file '%s'", $filename) : 'to a default file') ); - // File export happens inside the container, so we need to sync the - // .data folder. Image export happens on the host, so no need to sync. + // File export happens inside the container, so the .data folder is + // synced. Image export happens on the host, so no sync is needed. if (!$is_container_image_archive) { $this->syncToHost('.data'); } @@ -476,10 +475,10 @@ protected function subtestAhoyLintBeRector(string $webroot = 'web'): void { $this->logStepStart(); // Rector reports success when its rule sets load nothing, so a passing - // `ahoy lint-be` is not evidence that the Drupal rules ran. Seed a - // deprecation that only those rules rewrite: the run has to fail, and the - // failure has to name the rule that caught it, because an unrelated rule - // firing on this file would otherwise mask a set that stopped loading. + // `ahoy lint-be` is not evidence that the Drupal rules ran. The canary + // seeds a deprecation that only those rules rewrite. The run has to fail + // and name the rule that caught it; an unrelated rule firing on this + // file would otherwise mask a set that stopped loading. $this->logSubstep('Assert that the Drupal Rector rule sets are loaded'); $test_file = $webroot . '/modules/custom/sw_base/src/RectorCanary.php'; $canary = <<<'PHP' @@ -906,7 +905,6 @@ protected function subtestAhoyReset(string $webroot = 'web'): void { sleep(10); $this->logSubstep('Assert expected files and directories present or absent after reset'); - // Assert that initial Vortex files have not been removed. $this->assertCommonFilesPresent($webroot); $this->assertDirectoryDoesNotExist($webroot . '/modules/contrib', 'Contributed modules directory has been removed.'); @@ -920,7 +918,6 @@ protected function subtestAhoyReset(string $webroot = 'web'): void { $this->assertFileExists('.idea/idea_file.txt', 'IDE config file still exists.'); $this->assertDirectoryExists('.git', 'Project is still a Git repository.'); - // Cleanup. $this->removeDevelopmentSettings($webroot); $this->logStepFinish(); @@ -961,7 +958,6 @@ protected function subtestAhoyResetHard(string $webroot = 'web'): void { $this->assertFileExists('.idea/idea_file.txt', 'IDE config file still exists.'); $this->assertDirectoryExists('.git', 'Project is still a Git repository.'); - // Cleanup. $this->removeDevelopmentSettings($webroot); $this->logStepFinish(); @@ -1025,7 +1021,7 @@ protected function subtestAhoyFast404(): void { $this->logStepStart(); // Drupal core serves a near-identical 404 page for the same extensions, - // so the DOCTYPE from `settings.fast_404.php` is what tells the two apart. + // so the DOCTYPE from `settings.fast_404.php` distinguishes the two. $error_page = '-//W3C//DTD XHTML+RDFa 1.0//EN'; $derivative = '/sites/default/files/styles/large/public/missing.jpg'; diff --git a/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php b/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php index 973be02f2..56c31751b 100644 --- a/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php +++ b/.vortex/tests/phpunit/Traits/Subtests/SubtestDockerComposeTrait.php @@ -82,8 +82,8 @@ protected function subtestDockerComposeDotEnv(): void { // Lagoon images have a ~/.bashrc that loads changes made to .env file on // every new shell session like `docker compose exec bash -c "..."`. - // This is a bit different from the usual Docker behaviour where env - // variables are loaded only by Docker Compose and only on container start. + // This differs from the usual Docker behaviour where env variables are + // loaded only by Docker Compose and only on container start. // // The order of variables loading is: // - Docker (re-)start: variables defined in docker-compose.yml file are @@ -104,7 +104,7 @@ protected function subtestDockerComposeDotEnv(): void { // (defined in the docker-compose.yml and .env) are available to the // process. // - // We need to test a matrix of: + // The test covers a matrix of: // - variable type: docker-compose-mapped vs custom // - docker-compose-mapped variable is set in the docker-compose.yml // file and is automatically populated from the .env file on container @@ -177,12 +177,10 @@ protected function subtestDockerComposeDotEnv(): void { $this->cmd('docker compose exec -T cli php -r "echo getenv(\'DRUPAL_SHIELD_USER\') ?: \'Not set\';"', ['* my_custom_shield_user1', '! Not set'], 'Docker-compose-mapped variable has value in PHP script after container restarts.'); $this->cmd('docker compose exec -T cli bash -c "printenv|sort"', 'my_custom_var_value1', 'Custom variable exists inside of container after container restarts.'); $this->cmd('docker compose exec -T cli bash -c \'echo $MY_CUSTOM_VAR1\'', 'my_custom_var_value1', 'Custom variable exists and has a value inside of container after container restarts.'); - // Important: getenv() uses variables available in the environments when the - // PHP process starts. Using `docker compose exec -T cli php` rather than - // `docker compose exec -T cli bash -c "php"` means that PHP is - // started directly by Docker Compose and not via bash, so the ~/.bashrc - // is not loaded and the custom variable is not available in the PHP - // process environment. + // getenv() reads the environment the PHP process started with. `docker + // compose exec -T cli php` starts PHP directly by Docker Compose rather + // than via bash, so ~/.bashrc is not loaded and the custom variable is + // absent from the PHP process environment. $this->cmd('docker compose exec -T cli php -r "echo getenv(\'MY_CUSTOM_VAR1\') ?: \'Not set\';"', ['! my_custom_var_value1', '* Not set'], 'Custom variable does not exist and has no value in PHP script after container restarts.'); $this->fileRestore('.env'); @@ -234,12 +232,10 @@ protected function subtestDockerComposeDotEnv(): void { $this->cmd('docker compose exec -T cli php -r "echo getenv(\'DRUPAL_SHIELD_PASS\') ?: \'Not set\';"', ['* my_custom_shield_pass1', '! Not set'], 'Docker-compose-mapped variable has value in PHP script after container restarts.'); $this->cmd('docker compose exec -T cli bash -c "printenv|sort"', 'my_custom_var_value2', 'Custom variable exists inside of container after container restarts.'); $this->cmd('docker compose exec -T cli bash -c \'echo $MY_CUSTOM_VAR2\'', 'my_custom_var_value2', 'Custom variable exists and has a value inside of container after container restarts.'); - // Important: getenv() uses variables available in the environments when the - // PHP process starts. Using `docker compose exec -T cli php` rather than - // `docker compose exec -T cli bash -c "php"` means that PHP is - // started directly by Docker Compose and not via bash, so the ~/.bashrc - // is not loaded and the custom variable is not available in the PHP - // process environment. + // getenv() reads the environment the PHP process started with. `docker + // compose exec -T cli php` starts PHP directly by Docker Compose rather + // than via bash, so ~/.bashrc is not loaded and the custom variable is + // absent from the PHP process environment. $this->cmd('docker compose exec -T cli php -r "echo getenv(\'MY_CUSTOM_VAR2\') ?: \'Not set\';"', ['! my_custom_var_value2', '* Not set'], 'Custom variable does not exist and has no value in PHP script after container restarts.'); $this->fileRestore('.env'); @@ -325,9 +321,9 @@ protected function subtestDockerComposeDrushPhpIni(): void { protected function subtestDockerComposeDatabaseConfig(): void { $this->logStepStart(); - // Asserting the effective value rather than the file contents: the config - // file is copied to a path the database image reads only if the copy - // destination in the Dockerfile matches the engine's include directory. + // Assert the effective value rather than the file contents. The config + // file takes effect only if the copy destination in the Dockerfile + // matches the engine's include directory. $this->cmd( 'docker compose exec -T database mysql -udrupal -pdrupal -e "SHOW VARIABLES LIKE \'innodb_redo_log_capacity\';"', '1073741824', diff --git a/.vortex/tests/phpunit/Traits/SutTrait.php b/.vortex/tests/phpunit/Traits/SutTrait.php index 963dc9d64..ef8c1e1b5 100644 --- a/.vortex/tests/phpunit/Traits/SutTrait.php +++ b/.vortex/tests/phpunit/Traits/SutTrait.php @@ -16,8 +16,8 @@ trait SutTrait { /** * URL to the test demo database. * - * Tests use demo database and 'ahoy fetch-db' command, so we need - * to set the CURL DB to test DB. + * Tests use the demo database via 'ahoy fetch-db', so the fetch URL is + * pointed at the test database. */ const VORTEX_INSTALLER_DEMO_DB_TEST = 'https://github.com/drevops/vortex/releases/download/1.40.0/db.test.sql'; @@ -36,7 +36,7 @@ trait SutTrait { * * @var array */ - protected static $sutInstallerEnv = []; + protected static array $sutInstallerEnv = []; /** * Prompt values to pass via --prompts option. @@ -87,12 +87,14 @@ protected function prepareSut(string $webroot = 'web'): void { * SUT's composer.json so consumer sites resolve drevops/vortex-tooling * from packagist. Until the package is published, the SUT cannot resolve * it, so the workflow tests would fail at the Dockerfile's composer - * install step. This method copies the in-tree tooling into the SUT at - * '.tooling-source' (deliberately outside '.vortex/' so the SUT keeps no - * '.vortex/' directory at runtime), re-injects the path repository into - * composer.json, re-injects the COPY into cli.dockerfile, and adjusts - * '.dockerignore' and '.gitignore.artifact' so the tooling source enters - * the build context but never the deployment artifact. + * install step. + * + * The method copies the in-tree tooling into the SUT at '.tooling-source', + * outside '.vortex/' so the SUT keeps no '.vortex/' directory at runtime. + * It re-injects the path repository into composer.json and the COPY into + * cli.dockerfile. It adjusts '.dockerignore' and '.gitignore.artifact' so + * the tooling source enters the build context but never the deployment + * artifact. * * @todo Remove once drevops/vortex-tooling is published to packagist. */ @@ -244,15 +246,12 @@ protected function runInstaller(array $arguments = []): void { // of the Vortex codebase. During development, ensure any pending // changes are committed to the template repository. 'VORTEX_INSTALLER_TEMPLATE_REPO' => static::locationsRoot(), - // Tests use the demo database and the 'ahoy fetch-db' command, - // so we need to point CURL to the test database instead. - // - // This overrides the *demo database* with the *test demo database*, - // which is required for running test assertions ("star wars") - // against an expected data set. + // Tests use the demo database via 'ahoy fetch-db', so the URL points + // to the test demo database. The "star wars" assertions expect its + // data set. // - // The installer will load this environment variable, and it will - // take precedence over the value in the .env file. + // The installer loads this variable, and it takes precedence over + // the value in the .env file. 'VORTEX_FETCH_DB_URL' => static::VORTEX_INSTALLER_DEMO_DB_TEST, ], txt: 'Run the installer' @@ -325,10 +324,9 @@ protected function fetchDatabase(bool $copy_to_container = FALSE): void { /** * Adjust the codebase for unmounted volumes. * - * This method modifies the codebase files to ensure - * that the project can be built and run without mounted Docker volumes in - * environments such as CI/CD pipelines (which also replicate some hosting - * environments). + * The method modifies the codebase files so the project can be built and + * run without mounted Docker volumes in environments such as CI/CD + * pipelines, which also replicate some hosting environments. */ protected function adjustCodebaseForUnmountedVolumes(): void { if ($this->volumesMounted()) { @@ -350,10 +348,9 @@ protected function adjustCodebaseForUnmountedVolumes(): void { /** * Adjust Ahoy configuration for unmounted volumes. * - * This is similar to adjustCodebaseForUnmountedVolumes() but is called only - * for local Ahoy-based workflows. We need to do this to allow testing local - * workflows where the volumes are mounted in the CI environment where the - * volumes are not mounted. + * Similar to adjustCodebaseForUnmountedVolumes(), but called only for + * local Ahoy-based workflows. This allows testing local workflows, which + * mount volumes, in the CI environment where volumes are not mounted. */ protected function adjustAhoyForUnmountedVolumes(): void { if ($this->volumesMounted()) { @@ -392,7 +389,6 @@ protected function assertCommonFilesPresent(string $webroot = 'web', string $pro $this->assertFileContainsString('README.md', 'This repository was created using the [Vortex](https://github.com/drevops/vortex) Drupal project template', 'Assert that Vortex footnote remains.'); - // Assert Drupal files are present. $this->assertDrupalFilesPresent($webroot); } @@ -542,7 +538,6 @@ protected function assertVortexFilesPresent(string $webroot = 'web'): void { $this->assertFileNotContainsString('README.md', '# Vortex'); } - // Check directory doesn't contain .vortex references. $this->assertDirectoryNotContainsString('.', '/\.vortex'); } @@ -626,8 +621,8 @@ protected function assertThemeFilesAbsent(string $webroot = 'web'): void { protected function assertFilesTrackedInGit(string $webroot = 'web', bool $skip_commit = FALSE): void { // Modified or new files in the webroot at this point mean that the - // committed Drupal Scaffold files drifted from the files shipped with the - // installed Drupal core version and have to be re-committed. + // committed Drupal Scaffold files drifted from the files shipped with + // the installed Drupal core version. They have to be re-committed. $this->gitAssertCleanPath($webroot, message: 'Drupal Scaffold files in the webroot should not be modified or added by the build'); $this->createDevelopmentSettings($webroot); @@ -752,7 +747,7 @@ public function createInstalledDependenciesStub(string $webroot = 'web'): void { File::dump($webroot . '/sites/default/services.local.yml'); File::dump($webroot . '/sites/default/settings.local.php'); - File::dump("docker-compose.override.yml", 'version: "2.3"'); + File::dump('docker-compose.override.yml', 'version: "2.3"'); } } diff --git a/.vortex/tests/phpunit/Unit/ArrayTraitTest.php b/.vortex/tests/phpunit/Unit/ArrayTraitTest.php index 031221a77..66b953147 100644 --- a/.vortex/tests/phpunit/Unit/ArrayTraitTest.php +++ b/.vortex/tests/phpunit/Unit/ArrayTraitTest.php @@ -15,17 +15,11 @@ class ArrayTraitTest extends TestCase { use ArrayTrait; - /** - * Tests replacing values throughout an array. - */ #[DataProvider('dataProviderArrayReplaceValue')] public function testArrayReplaceValue(array $array, callable $callback, array $expected): void { $this->assertSame($expected, static::arrayReplaceValue($array, $callback)); } - /** - * Data provider for testArrayReplaceValue(). - */ public static function dataProviderArrayReplaceValue(): \Iterator { $upper = static fn(mixed $value): mixed => is_string($value) ? strtoupper($value) : $value; @@ -42,9 +36,6 @@ public static function dataProviderArrayReplaceValue(): \Iterator { yield 'empty nested array preserved' => [['first', []], $upper, ['FIRST', []]]; } - /** - * Tests that the callback receives every leaf value. - */ public function testArrayReplaceValuePassesEveryLeafToCallback(): void { $seen = []; diff --git a/.vortex/tests/phpunit/Unit/AssertTraitTest.php b/.vortex/tests/phpunit/Unit/AssertTraitTest.php index e0eeb92d5..7afcb39ff 100644 --- a/.vortex/tests/phpunit/Unit/AssertTraitTest.php +++ b/.vortex/tests/phpunit/Unit/AssertTraitTest.php @@ -16,17 +16,11 @@ class AssertTraitTest extends TestCase { use AssertTrait; - /** - * Tests that a matching string passes the assertion. - */ #[DataProvider('dataProviderAssertArrayContainsString')] public function testAssertArrayContainsString(string $needle, array $haystack): void { $this->assertArrayContainsString($needle, $haystack); } - /** - * Data provider for testAssertArrayContainsString(). - */ public static function dataProviderAssertArrayContainsString(): \Iterator { yield 'exact match' => ['first', ['first', 'second']]; @@ -41,9 +35,6 @@ public static function dataProviderAssertArrayContainsString(): \Iterator { yield 'stringable element cast to string' => ['first', [new AssertStringableStub()]]; } - /** - * Tests that a missing string fails the assertion. - */ #[DataProvider('dataProviderAssertArrayContainsStringFails')] public function testAssertArrayContainsStringFails(string $needle, array $haystack): void { $this->expectException(AssertionFailedError::class); @@ -52,9 +43,6 @@ public function testAssertArrayContainsStringFails(string $needle, array $haysta $this->assertArrayContainsString($needle, $haystack); } - /** - * Data provider for testAssertArrayContainsStringFails(). - */ public static function dataProviderAssertArrayContainsStringFails(): \Iterator { yield 'empty haystack' => ['first', []]; @@ -65,14 +53,8 @@ public static function dataProviderAssertArrayContainsStringFails(): \Iterator { } -/** - * Stub returning a string from its string conversion. - */ class AssertStringableStub implements \Stringable { - /** - * Returns the value the assertion searches. - */ public function __toString(): string { return 'first'; } diff --git a/.vortex/tests/phpunit/Unit/MockTraitTest.php b/.vortex/tests/phpunit/Unit/MockTraitTest.php index 805777c1c..4e616906c 100644 --- a/.vortex/tests/phpunit/Unit/MockTraitTest.php +++ b/.vortex/tests/phpunit/Unit/MockTraitTest.php @@ -19,9 +19,6 @@ class MockTraitTest extends TestCase { use MockTrait; - /** - * Tests that mapped methods return their configured values. - */ public function testPrepareMockReturnsMappedValues(): void { $mock = $this->prepareMock(MockSubject::class, [ 'greet' => 'mocked greeting', @@ -33,9 +30,6 @@ public function testPrepareMockReturnsMappedValues(): void { $this->assertSame(42, $mock->total()); } - /** - * Tests that unmapped methods keep their original behaviour. - */ public function testPrepareMockLeavesUnmappedMethods(): void { $mock = $this->prepareMock(MockSubject::class, ['greet' => 'mocked greeting']); $this->assertInstanceOf(MockSubject::class, $mock); @@ -43,9 +37,6 @@ public function testPrepareMockLeavesUnmappedMethods(): void { $this->assertSame(0, $mock->total()); } - /** - * Tests that a callable value is used as a return callback. - */ public function testPrepareMockAcceptsCallable(): void { $mock = $this->prepareMock(MockSubject::class, ['echoBack' => strtoupper(...)]); $this->assertInstanceOf(MockSubject::class, $mock); @@ -63,9 +54,6 @@ public function testPrepareMockWithoutArguments(): void { $this->assertSame(['name' => ''], $mock->constructorArgs()); } - /** - * Tests that constructor arguments are passed through. - */ public function testPrepareMockPassesConstructorArguments(): void { $mock = $this->prepareMock(MockSubject::class, ['greet' => 'mocked greeting'], ['name' => 'constructed name']); $this->assertInstanceOf(MockSubject::class, $mock); @@ -73,9 +61,6 @@ public function testPrepareMockPassesConstructorArguments(): void { $this->assertSame(['name' => 'constructed name'], $mock->constructorArgs()); } - /** - * Tests that FALSE disables the original constructor. - */ public function testPrepareMockDisablesConstructor(): void { $mock = $this->prepareMock(MockSubject::class, ['greet' => 'mocked greeting'], FALSE); $this->assertInstanceOf(MockSubject::class, $mock); @@ -83,9 +68,6 @@ public function testPrepareMockDisablesConstructor(): void { $this->assertNull($mock->constructorArgs()); } - /** - * Tests mocking a class that does not exist. - */ public function testPrepareMockMissingClass(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Class NoSuchClass does not exist'); @@ -95,9 +77,6 @@ public function testPrepareMockMissingClass(): void { } -/** - * Subject class used to exercise mock preparation. - */ class MockSubject { /** @@ -107,9 +86,6 @@ class MockSubject { */ protected ?array $constructorArgs = NULL; - /** - * Constructs the subject. - */ public function __construct(string $name = '') { $this->constructorArgs = ['name' => $name]; } @@ -124,23 +100,14 @@ public function constructorArgs(): ?array { return $this->constructorArgs; } - /** - * Returns a fixed greeting. - */ public function greet(): string { return 'original greeting'; } - /** - * Returns a fixed total. - */ public function total(): int { return 0; } - /** - * Returns the given value unchanged. - */ public function echoBack(string $value): string { return $value; } diff --git a/.vortex/tests/phpunit/Unit/ReflectionTraitTest.php b/.vortex/tests/phpunit/Unit/ReflectionTraitTest.php index 44985bd7a..c8f253d06 100644 --- a/.vortex/tests/phpunit/Unit/ReflectionTraitTest.php +++ b/.vortex/tests/phpunit/Unit/ReflectionTraitTest.php @@ -15,9 +15,6 @@ class ReflectionTraitTest extends TestCase { use ReflectionTrait; - /** - * Tests reading a protected instance property. - */ #[DataProvider('dataProviderGetProtectedValue')] public function testGetProtectedValue(mixed $value): void { $object = new ReflectionStub(); @@ -26,9 +23,6 @@ public function testGetProtectedValue(mixed $value): void { $this->assertSame($value, static::getProtectedValue($object, 'instanceValue')); } - /** - * Data provider for testGetProtectedValue(). - */ public static function dataProviderGetProtectedValue(): \Iterator { yield ['instance value']; yield [42]; @@ -39,9 +33,6 @@ public static function dataProviderGetProtectedValue(): \Iterator { yield [new \stdClass()]; } - /** - * Tests that the value is read from the given instance. - */ public function testGetProtectedValueReadsGivenInstance(): void { $first = new ReflectionStub(); $first->setInstanceValue('first value'); @@ -53,18 +44,12 @@ public function testGetProtectedValueReadsGivenInstance(): void { $this->assertSame('second value', static::getProtectedValue($second, 'instanceValue')); } - /** - * Tests reading a protected static property. - */ public function testGetProtectedValueStaticProperty(): void { $object = new ReflectionStub(); $this->assertSame('static value', static::getProtectedValue($object, 'staticValue')); } - /** - * Tests reading a protected property declared on a parent class. - */ public function testGetProtectedValueInheritedProperty(): void { $object = new ReflectionChildStub(); $object->setInstanceValue('inherited value'); @@ -73,9 +58,6 @@ public function testGetProtectedValueInheritedProperty(): void { $this->assertSame('child value', static::getProtectedValue($object, 'childValue')); } - /** - * Tests reading a property that the object does not declare. - */ public function testGetProtectedValueMissingProperty(): void { $object = new ReflectionStub(); @@ -85,9 +67,6 @@ public function testGetProtectedValueMissingProperty(): void { static::getProtectedValue($object, 'missingValue'); } - /** - * Tests writing a protected instance property. - */ public function testSetProtectedValue(): void { $object = new ReflectionStub(); @@ -96,9 +75,6 @@ public function testSetProtectedValue(): void { $this->assertSame('assigned value', static::getProtectedValue($object, 'instanceValue')); } - /** - * Tests writing a protected property declared on a parent class. - */ public function testSetProtectedValueInheritedProperty(): void { $object = new ReflectionChildStub(); @@ -107,18 +83,12 @@ public function testSetProtectedValueInheritedProperty(): void { $this->assertSame('assigned to parent', static::getProtectedValue($object, 'instanceValue')); } - /** - * Tests calling a protected instance method. - */ public function testCallProtectedMethod(): void { $object = new ReflectionStub(); $this->assertSame('instance: first, second', static::callProtectedMethod($object, 'concatenate', ['first', 'second'])); } - /** - * Tests calling a protected static method on an object and on a class name. - */ public function testCallProtectedMethodStatic(): void { $object = new ReflectionStub(); @@ -126,9 +96,6 @@ public function testCallProtectedMethodStatic(): void { $this->assertSame('static: value', static::callProtectedMethod(ReflectionStub::class, 'prefix', ['value'])); } - /** - * Tests calling a protected method on a class that does not exist. - */ public function testCallProtectedMethodMissingClass(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Class NoSuchClass does not exist'); @@ -136,9 +103,6 @@ public function testCallProtectedMethodMissingClass(): void { static::callProtectedMethod('NoSuchClass', 'prefix'); } - /** - * Tests calling a protected method that the class does not declare. - */ public function testCallProtectedMethodMissingMethod(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('Method missingMethod does not exist'); @@ -146,9 +110,6 @@ public function testCallProtectedMethodMissingMethod(): void { static::callProtectedMethod(new ReflectionStub(), 'missingMethod'); } - /** - * Tests calling a non-static protected method without an instance. - */ public function testCallProtectedMethodWithoutInstance(): void { $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('An object instance is required for non-static methods'); @@ -158,9 +119,6 @@ public function testCallProtectedMethodWithoutInstance(): void { } -/** - * Stub with protected members to reach through reflection. - */ class ReflectionStub { /** @@ -173,32 +131,20 @@ class ReflectionStub { */ protected mixed $instanceValue = 'instance value'; - /** - * Assigns the protected instance property. - */ public function setInstanceValue(mixed $value): void { $this->instanceValue = $value; } - /** - * Joins the given arguments. - */ protected function concatenate(string $first, string $second): string { return sprintf('instance: %s, %s', $first, $second); } - /** - * Prefixes the given argument. - */ protected static function prefix(string $value): string { return sprintf('static: %s', $value); } } -/** - * Stub inheriting the protected members of its parent. - */ class ReflectionChildStub extends ReflectionStub { /** diff --git a/.vortex/tooling/src/vortex-deploy b/.vortex/tooling/src/vortex-deploy index c08455a0c..b539b4ef6 100755 --- a/.vortex/tooling/src/vortex-deploy +++ b/.vortex/tooling/src/vortex-deploy @@ -90,12 +90,8 @@ if [ "${VORTEX_DEPLOY_ALLOW_SKIP:-}" = "1" ]; then note "Found flag to skip a deployment." if [ -n "${VORTEX_DEPLOY_PR}" ] && [ -n "${VORTEX_DEPLOY_SKIP_PRS:-}" ]; then - # Allow skipping deployment by providing `$VORTEX_DEPLOY_SKIP_PRS` variable - # with PR numbers as a single value or comma-separated list. - # - # Examples: - # VORTEX_DEPLOY_SKIP_PRS=123 - # VORTEX_DEPLOY_SKIP_PRS=123,456,789 + # ${VORTEX_DEPLOY_SKIP_PRS} lists PR numbers to skip, as a single value or + # a comma-separated list. if echo ",${VORTEX_DEPLOY_SKIP_PRS}," | grep -q ",${VORTEX_DEPLOY_PR},"; then note "Found PR ${VORTEX_DEPLOY_PR} in skip list." note "Skipped deployment ${VORTEX_DEPLOY_TYPES}." @@ -104,14 +100,9 @@ if [ "${VORTEX_DEPLOY_ALLOW_SKIP:-}" = "1" ]; then fi if [ -n "${VORTEX_DEPLOY_BRANCH:-}" ] && [ -n "${VORTEX_DEPLOY_SKIP_BRANCHES:-}" ]; then - # Allow skipping deployment by providing `$VORTEX_DEPLOY_SKIP_BRANCHES` - # variable with branch names as a single value or comma-separated list. - # - # Branch names must match exactly as they appear in the repository. - # - # Examples: - # VORTEX_DEPLOY_SKIP_BRANCHES=feature/test - # VORTEX_DEPLOY_SKIP_BRANCHES=feature/test,hotfix/urgent,project/experimental + # ${VORTEX_DEPLOY_SKIP_BRANCHES} lists branches to skip, as a single value + # or a comma-separated list. Branch names must match exactly as they appear + # in the repository. if echo ",${VORTEX_DEPLOY_SKIP_BRANCHES}," | grep -qF ",${VORTEX_DEPLOY_BRANCH},"; then note "Found branch ${VORTEX_DEPLOY_BRANCH} in skip list." note "Skipped deployment ${VORTEX_DEPLOY_TYPES}." @@ -121,10 +112,6 @@ if [ "${VORTEX_DEPLOY_ALLOW_SKIP:-}" = "1" ]; then fi if [ -n "${VORTEX_DEPLOY_ALLOW_LABEL}" ] && [ -n "${VORTEX_DEPLOY_PR}" ]; then - # Gate a pull request deployment on the presence of a label. - # - # The PR's labels are provided as a comma-separated list in - # $VORTEX_DEPLOY_PR_LABELS, populated by the CI provider from the PR event. note "Found flag to gate deployment on the \"${VORTEX_DEPLOY_ALLOW_LABEL}\" label." if ! echo ",${VORTEX_DEPLOY_PR_LABELS}," | grep -qF ",${VORTEX_DEPLOY_ALLOW_LABEL},"; then diff --git a/.vortex/tooling/src/vortex-deploy-artifact b/.vortex/tooling/src/vortex-deploy-artifact index 323aaeab9..b8a8867a4 100755 --- a/.vortex/tooling/src/vortex-deploy-artifact +++ b/.vortex/tooling/src/vortex-deploy-artifact @@ -81,7 +81,6 @@ info "Started artifact deployment." # shellcheck disable=SC2043 for cmd in curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done -# Check all required values. [ -z "${VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE}" ] && fail "Missing required value for VORTEX_DEPLOY_ARTIFACT_GIT_REMOTE." [ -z "${VORTEX_DEPLOY_ARTIFACT_DST_BRANCH}" ] && fail "Missing required value for VORTEX_DEPLOY_ARTIFACT_DST_BRANCH." [ -z "${VORTEX_DEPLOY_ARTIFACT_SRC}" ] && fail "Missing required value for VORTEX_DEPLOY_ARTIFACT_SRC." @@ -90,7 +89,6 @@ for cmd in curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is no [ -z "${VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME}" ] && fail "Missing required value for VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME." [ -z "${VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL}" ] && fail "Missing required value for VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL." -# Configure global git settings, if they do not exist. [ -z "$(git config --global user.name)" ] && task "Configuring global git user name." && git config --global user.name "${VORTEX_DEPLOY_ARTIFACT_GIT_USER_NAME}" && pass "Configured global git user name." [ -z "$(git config --global user.email)" ] && task "Configuring global git user email." && git config --global user.email "${VORTEX_DEPLOY_ARTIFACT_GIT_USER_EMAIL}" && pass "Configured global git user email." @@ -105,11 +103,9 @@ fi chmod +x "${TMPDIR:-/tmp}"/git-artifact pass "Installed artifact builder." -# Try resolving absolute paths. if command -v realpath >/dev/null 2>&1; then - # Expand relative paths while also handling literal tilde expansion passed in - # singe quotes. This addresses the case where the paths are passed directly - # from YAML anchors as literal strings. + # Paths passed as literal strings (e.g. from single-quoted YAML anchors) + # arrive with an unexpanded "~", so substitute ${HOME} before realpath. # shellcheck disable=SC2116 VORTEX_DEPLOY_ARTIFACT_ROOT="$(realpath "${VORTEX_DEPLOY_ARTIFACT_ROOT/#\~/${HOME}}")" # shellcheck disable=SC2116 @@ -134,13 +130,11 @@ artifact_args=( --log="${VORTEX_DEPLOY_ARTIFACT_LOG}" ) -# Prune stale remote branches only when a pattern is set: git-artifact requires -# a pattern and errors without one. +# git-artifact errors when --cleanup-stale is passed without a pattern. if [ -n "${VORTEX_DEPLOY_ARTIFACT_CLEANUP_PATTERN}" ]; then artifact_args+=(--cleanup-stale --cleanup-pattern="${VORTEX_DEPLOY_ARTIFACT_CLEANUP_PATTERN}" --cleanup-age="${VORTEX_DEPLOY_ARTIFACT_CLEANUP_AGE}") fi -# Add --debug to debug any deployment issues. "${TMPDIR:-/tmp}"/git-artifact "${artifact_args[@]}" -vvv pass "Finished artifact deployment." diff --git a/.vortex/tooling/src/vortex-deploy-lagoon b/.vortex/tooling/src/vortex-deploy-lagoon index 86c1a97ae..aebdf56b8 100755 --- a/.vortex/tooling/src/vortex-deploy-lagoon +++ b/.vortex/tooling/src/vortex-deploy-lagoon @@ -87,8 +87,6 @@ task() { _TASK_START=$(date +%s); [ "${TERM:-}" != "dumb" ] && tput colors >/dev pass() { _d=""; [ -n "${_TASK_START:-}" ] && _d=" ($(($(date +%s) - _TASK_START))s)" && unset _TASK_START; [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[32m[ OK ] %s%s\033[0m\n" "${1}" "${_d}" || printf "[ OK ] %s%s\n" "${1}" "${_d}"; } fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; exit "${2:-1}"; } -# Check if error output contains Lagoon environment limit exceeded message. -# Returns 0 if limit exceeded error found, 1 otherwise. is_lagoon_env_limit_exceeded() { local error_output="${1:-}" echo "${error_output}" | grep -q "exceed" @@ -114,12 +112,8 @@ run_lagoon_deploy() { [ "${VORTEX_DEPLOY_LAGOON_FAIL_ENV_LIMIT_EXCEEDED}" = "0" ] && exit_code=0 fi - # The CLI output is the only record of why a deploy failed, so retain it - # rather than making the failure reproducible only with debug enabled. [ "${exit_code}" != "0" ] && deploy_error="${deploy_output}" - # A deploy that did not fail has nothing to explain, so its output remains - # verbose-only. [ "${exit_code}" = "0" ] && [ "${VORTEX_DEBUG-}" = "1" ] && [ -n "${deploy_output}" ] && printf '%s\n' "${deploy_output}" # Always succeed: the outcome is carried in ${exit_code}. Leaking the status @@ -136,14 +130,12 @@ deploy_error="" info "Started Lagoon deployment." -# Lagoon does not support tag deployments. Exit successfully with a message. if [ "${VORTEX_DEPLOY_MODE}" = "tag" ]; then note "Lagoon does not support tag deployments. Skipped." pass "Finished Lagoon deployment." exit 0 fi -## Check all required values. [ -z "${VORTEX_DEPLOY_LAGOON_PROJECT}" ] && fail "Missing required value for VORTEX_DEPLOY_LAGOON_PROJECT or LAGOON_PROJECT." { [ -z "${VORTEX_DEPLOY_LAGOON_BRANCH}" ] && [ -z "${VORTEX_DEPLOY_LAGOON_PR}" ]; } && fail "Missing required value for VORTEX_DEPLOY_LAGOON_BRANCH or VORTEX_DEPLOY_BRANCH or VORTEX_DEPLOY_LAGOON_PR or VORTEX_DEPLOY_PR." @@ -176,21 +168,16 @@ pass "Configured Lagoon instance." lagoon() { command lagoon --force --skip-update-check --ssh-key "${VORTEX_DEPLOY_LAGOON_SSH_FILE}" --lagoon "${VORTEX_DEPLOY_LAGOON_INSTANCE}" --project "${VORTEX_DEPLOY_LAGOON_PROJECT}" "$@"; } -# ACTION: 'destroy' -# Explicitly specifying "destroy" action as a failsafe. +# The "destroy" action is matched explicitly as a failsafe. if [ "${VORTEX_DEPLOY_LAGOON_ACTION}" = "destroy" ]; then task "Destroying environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, branch: ${VORTEX_DEPLOY_LAGOON_BRANCH}." lagoon delete environment --environment "${VORTEX_DEPLOY_LAGOON_BRANCH}" || true pass "Destroyed environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, branch: ${VORTEX_DEPLOY_LAGOON_BRANCH}." -# ACTION: 'deploy' OR 'deploy_override_db' else - # Deploy PR. if [ -n "${VORTEX_DEPLOY_LAGOON_PR:-}" ]; then deploy_pr_full="pr-${VORTEX_DEPLOY_LAGOON_PR}" - # Discover all available environments to check if this is a fresh deployment - # or a re-deployment of the existing environment. task "Discovering existing environments for PR deployments." lagoon list environments --output-json --pretty >/tmp/lagoon-envs.json names="$(jq -r '.data[] | select(.deploytype | contains("pullrequest")) | .name' /tmp/lagoon-envs.json /dev/null 2>&1 || echo '')" @@ -207,7 +194,6 @@ else [ "${is_redeploy:-}" = "0" ] && note "No existing environment found for PR \"${VORTEX_DEPLOY_LAGOON_PR}\"." pass "Completed environment discovery." - # Re-deployment of the existing environment. if [ "${is_redeploy:-}" = "1" ]; then # The flag value this deployment needs while Lagoon queues the build. if [ "${VORTEX_DEPLOY_LAGOON_ACTION}" = "deploy_override_db" ]; then @@ -216,9 +202,8 @@ else override_db_value=0 fi - # A deployment borrows the flag for a single build and then returns the - # environment to the state it was found in, so an environment that had no - # flag is not left with one. + # The flag is set for a single build and then restored to its previous + # state, so an environment that had no flag is not left with one. task "Discovering a database import override flag." override_db_state="unknown" override_db_original="" @@ -237,10 +222,9 @@ else fi if [ "${override_db_state}" = "unknown" ]; then - # An unreadable variable list is not an absent flag. Adding one and + # An unreadable variable list is not an absent flag: adding one and # deleting it afterwards would destroy a flag that already exists, so - # the flag is left alone. The build then runs against whatever value the - # environment already carries, which may not be the requested one. + # the flag is left alone. note "WARNING: Could not read environment variables. A database import override flag was left unchanged and may not match the requested deployment action." elif [ "${override_db_state}" = "absent" ]; then note "No existing database import override flag found." @@ -261,8 +245,8 @@ else task "Redeploying environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, PR: ${VORTEX_DEPLOY_LAGOON_PR}." run_lagoon_deploy deploy pullrequest --number "${VORTEX_DEPLOY_LAGOON_PR}" --base-branch-name "${VORTEX_DEPLOY_LAGOON_PR_BASE_BRANCH}" --base-branch-ref "origin/${VORTEX_DEPLOY_LAGOON_PR_BASE_BRANCH}" --head-branch-name "${VORTEX_DEPLOY_LAGOON_BRANCH}" --head-branch-ref "${VORTEX_DEPLOY_LAGOON_PR_HEAD}" --title "${deploy_pr_full}" - # A failed deploy is reported by the final status line, after the override - # flag has been returned to the state it was found in. + # A failed deploy is reported by the final status line, after the + # override flag is restored. [ "${exit_code}" = "0" ] && pass "Requested redeployment of environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, PR: ${VORTEX_DEPLOY_LAGOON_PR}." # Lagoon reads the flag when it queues the build. @@ -282,7 +266,8 @@ else # Deployment of the fresh environment. else - # If PR deployments are not configured in Lagoon - it will filter it out and will not deploy. + # Lagoon filters out the request and does not deploy when PR deployments + # are not configured for the project. task "Deploying environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, PR: ${VORTEX_DEPLOY_LAGOON_PR}." run_lagoon_deploy deploy pullrequest --number "${VORTEX_DEPLOY_LAGOON_PR}" --base-branch-name "${VORTEX_DEPLOY_LAGOON_PR_BASE_BRANCH}" --base-branch-ref "origin/${VORTEX_DEPLOY_LAGOON_PR_BASE_BRANCH}" --head-branch-name "${VORTEX_DEPLOY_LAGOON_BRANCH}" --head-branch-ref "${VORTEX_DEPLOY_LAGOON_PR_HEAD}" --title "${deploy_pr_full}" [ "${exit_code}" = "0" ] && pass "Requested deployment of environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, PR: ${VORTEX_DEPLOY_LAGOON_PR}." @@ -290,8 +275,6 @@ else # Deploy branch. else - # Discover all available environments to check if this is a fresh deployment - # or a re-deployment of the existing environment. task "Discovering existing environments for branch deployments." lagoon list environments --output-json --pretty >/tmp/lagoon-envs.json names="$(jq -r '.data[] | select(.deploytype | contains("branch")) | .name' /tmp/lagoon-envs.json /dev/null 2>&1 || echo '')" @@ -308,7 +291,6 @@ else [ "${is_redeploy:-}" = "0" ] && note "No existing environment found for branch \"${VORTEX_DEPLOY_LAGOON_BRANCH}\"." pass "Completed environment discovery." - # Re-deployment of the existing environment. if [ "${is_redeploy:-}" = "1" ]; then # The flag value this deployment needs while Lagoon queues the build. if [ "${VORTEX_DEPLOY_LAGOON_ACTION}" = "deploy_override_db" ]; then @@ -317,9 +299,8 @@ else override_db_value=0 fi - # A deployment borrows the flag for a single build and then returns the - # environment to the state it was found in, so an environment that had no - # flag is not left with one. + # The flag is set for a single build and then restored to its previous + # state, so an environment that had no flag is not left with one. task "Discovering a database import override flag." override_db_state="unknown" override_db_original="" @@ -338,10 +319,9 @@ else fi if [ "${override_db_state}" = "unknown" ]; then - # An unreadable variable list is not an absent flag. Adding one and + # An unreadable variable list is not an absent flag: adding one and # deleting it afterwards would destroy a flag that already exists, so - # the flag is left alone. The build then runs against whatever value the - # environment already carries, which may not be the requested one. + # the flag is left alone. note "WARNING: Could not read environment variables. A database import override flag was left unchanged and may not match the requested deployment action." elif [ "${override_db_state}" = "absent" ]; then note "No existing database import override flag found." @@ -362,8 +342,8 @@ else task "Redeploying environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, branch: ${VORTEX_DEPLOY_LAGOON_BRANCH}." run_lagoon_deploy deploy latest --environment "${VORTEX_DEPLOY_LAGOON_BRANCH}" - # A failed deploy is reported by the final status line, after the override - # flag has been returned to the state it was found in. + # A failed deploy is reported by the final status line, after the + # override flag is restored. [ "${exit_code}" = "0" ] && pass "Requested redeployment of environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, branch: ${VORTEX_DEPLOY_LAGOON_BRANCH}." # Lagoon reads the flag when it queues the build. @@ -383,7 +363,8 @@ else # Deployment of the fresh environment. else - # If current branch deployments does not match a regex in Lagoon - it will filter it out and will not deploy. + # Lagoon filters out the request and does not deploy when the branch does + # not match the configured deployment regex. task "Deploying environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, branch: ${VORTEX_DEPLOY_LAGOON_BRANCH}." run_lagoon_deploy deploy branch --branch "${VORTEX_DEPLOY_LAGOON_BRANCH}" [ "${exit_code}" = "0" ] && pass "Requested deployment of environment: project: ${VORTEX_DEPLOY_LAGOON_PROJECT}, branch: ${VORTEX_DEPLOY_LAGOON_BRANCH}." diff --git a/.vortex/tooling/src/vortex-deploy-webhook b/.vortex/tooling/src/vortex-deploy-webhook index fb91f19d4..f04323329 100755 --- a/.vortex/tooling/src/vortex-deploy-webhook +++ b/.vortex/tooling/src/vortex-deploy-webhook @@ -35,7 +35,6 @@ for cmd in curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is no info "Started webhook deployment." -# Check all required values. [ -z "${VORTEX_DEPLOY_WEBHOOK_URL}" ] && fail "Missing required value for VORTEX_DEPLOY_WEBHOOK_URL." [ -z "${VORTEX_DEPLOY_WEBHOOK_METHOD}" ] && fail "Missing required value for VORTEX_DEPLOY_WEBHOOK_METHOD." [ -z "${VORTEX_DEPLOY_WEBHOOK_RESPONSE_STATUS}" ] && fail "Missing required value for VORTEX_DEPLOY_WEBHOOK_RESPONSE_STATUS." diff --git a/.vortex/tooling/src/vortex-doctor b/.vortex/tooling/src/vortex-doctor index b853f2e9a..d8e34db30 100755 --- a/.vortex/tooling/src/vortex-doctor +++ b/.vortex/tooling/src/vortex-doctor @@ -75,9 +75,6 @@ warn() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\03 for cmd in docker pygmy ahoy; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done -# -# Main entry point. -# main() { [ "${1:-}" = "info" ] && system_info && exit @@ -115,7 +112,6 @@ main() { pass "Pygmy is running." fi - # Check that the stack is running. if [ "${VORTEX_DOCTOR_CHECK_CONTAINERS}" = "1" ]; then container_services=(cli php nginx database) for container_service in "${container_services[@]}"; do @@ -129,29 +125,11 @@ main() { fi if [ "${VORTEX_DOCTOR_CHECK_SSH}" = "1" ]; then - # SSH key injection is required to access Lagoon services from within - # containers. For example, to connect to a production environment to run - # a drush script. - # Pygmy makes this possible in the following way: - # 1. Pygmy starts the `amazeeio/ssh-agent` container with a volume `/tmp/amazeeio_ssh-agent` - # 2. Pygmy adds a default SSH key from the host into this volume. - # 3. `docker-compose.yml` should have volume inclusion specified for the CLI container: - # ``` - # volumes_from: - # - container:amazeeio-ssh-agent - # ``` - # 4. When the CLI container starts, the volume is mounted and an entrypoint - # script loads the SSH key into an agent. - # @see https://github.com/uselagoon/lagoon-images/blob/main/images/php-cli/entrypoints/10-ssh-agent.sh - # - # Running `ssh-add -l` within the CLI container should show that the SSH key - # was correctly loaded. - # - # As a rule of thumb, one must restart the CLI container after restarting - # Pygmy ONLY if the SSH key was not loaded in Pygmy before the stack starts. - # No need to restart the CLI container if the key was added, but Pygmy was - # restarted - the volume mount will be retained, and the key will still be - # available in the CLI container. + # The SSH key is used to access Lagoon services from within containers. + # Pygmy adds the host's default SSH key into the amazeeio/ssh-agent + # container volume. The CLI container mounts that volume via + # "volumes_from", and its entrypoint loads the key into an ssh-agent. + # @see https://github.com/uselagoon/lagoon-images/blob/main/images/php-cli/entrypoints/10-ssh-agent.sh ssh_key_added_to_pygmy=0 ssh_key_volume_mounted=0 @@ -175,7 +153,6 @@ main() { ssh_key_volume_mounted=1 fi - # Check that ssh key is available in the container, but only if the above checks passed. if [ "${ssh_key_added_to_pygmy}" = "1" ] && [ "${ssh_key_volume_mounted}" = "1" ]; then if ! docker compose exec -T cli bash -c "ssh-add -l >/dev/null 2>&1"; then fail "SSH key was not added to the container. Run 'pygmy restart'." @@ -207,9 +184,6 @@ main() { echo } -# -# Sanitize system information output to remove PII data. -# sanitize_system_info() { local username username="$(whoami)" @@ -265,15 +239,11 @@ system_info() { echo } -# -# Check that command exists. -# command_exists() { local cmd=${1} command -v "${cmd}" | grep -ohq "${cmd}" local res=$? - # Try homebrew lookup, if brew is available. if command -v "brew" | grep -ohq "brew" && [ "${res}" = "1" ]; then brew --prefix "${cmd}" >/dev/null res=$? diff --git a/.vortex/tooling/src/vortex-export-db-file b/.vortex/tooling/src/vortex-export-db-file index 319b66c63..489190440 100755 --- a/.vortex/tooling/src/vortex-export-db-file +++ b/.vortex/tooling/src/vortex-export-db-file @@ -32,22 +32,16 @@ info "Started database file export." drush() { ./vendor/bin/drush -y "$@"; } -# Create dump file name with a timestamp or use the file name provided -# as a first argument. dump_file=$([ "${1:-}" ] && echo "${VORTEX_EXPORT_DB_FILE_DIR}/${1}" || echo "${VORTEX_EXPORT_DB_FILE_DIR}/export_db_$(date +%Y%m%d_%H%M%S).sql") -# If dump file is relative - update it to the parent directory, because the -# `drush sql:dump` command result file is relative to Drupal root, but provided -# path is relative to the project root. +# `drush sql:dump` resolves --result-file relative to the Drupal root, while +# ${dump_file} is relative to the project root, so rewrite "./" to "../". dump_file_drush="${dump_file/#.\//../}" -# Create a directory to store database dump. mkdir -p "${VORTEX_EXPORT_DB_FILE_DIR}" -# Dump database into a file. drush sql:dump --skip-tables-key=common --structure-tables-list="${VORTEX_EXPORT_DB_FILE_STRUCTURE_TABLES}" --result-file="${dump_file_drush}" -q -# Check that file was saved and output saved dump file name. if [ -f "${dump_file}" ] && [ -s "${dump_file}" ]; then note "Exported database dump saved ${dump_file}." else diff --git a/.vortex/tooling/src/vortex-export-db-image b/.vortex/tooling/src/vortex-export-db-image index b123029b0..1978b0f2c 100755 --- a/.vortex/tooling/src/vortex-export-db-image +++ b/.vortex/tooling/src/vortex-export-db-image @@ -68,11 +68,9 @@ iid="${iid#sha256:}" note "Committed exported container image with id ${iid}." pass "Committed exported container image with name ${new_image}." -# Create directory to store database dump. mkdir -p "${VORTEX_EXPORT_DB_IMAGE_DIR}" -# Create dump file name with a timestamp or use the file name provided -# as a first argument. Also, make sure that the extension is correct. +# Replace a ".sql" extension in the provided name: the archive is a tar file. archive_file=$([ "${VORTEX_EXPORT_DB_IMAGE_ARCHIVE_FILE}" ] && echo "${VORTEX_EXPORT_DB_IMAGE_DIR}/${VORTEX_EXPORT_DB_IMAGE_ARCHIVE_FILE//.sql/.tar}" || echo "${VORTEX_EXPORT_DB_IMAGE_DIR}/export_db_$(date +%Y%m%d_%H%M%S).tar") task "Exporting database image archive to file ${archive_file}." @@ -81,7 +79,6 @@ task "Exporting database image archive to file ${archive_file}." mkdir -p "$(dirname "${archive_file}")" docker save -o "${archive_file}" "${new_image}" -# Check that file was saved and output saved dump file name. if [ -f "${archive_file}" ] && [ -s "${archive_file}" ]; then note "Exported database image saved to archive file ${archive_file}." else diff --git a/.vortex/tooling/src/vortex-fetch-db b/.vortex/tooling/src/vortex-fetch-db index 05cc029bd..64c4daf29 100755 --- a/.vortex/tooling/src/vortex-fetch-db +++ b/.vortex/tooling/src/vortex-fetch-db @@ -18,13 +18,12 @@ set -eu # VORTEX_FETCH_DB_SOURCE). _db_index="${VORTEX_DB_INDEX:-}" -# Note that `container_registry` works only for database-in-image -# database storage (when $VORTEX_DB_IMAGE variable has a value). +# The "container_registry" source works only for database-in-image storage +# (when the $VORTEX_DB_IMAGE variable has a value). _v="VORTEX_FETCH_DB${_db_index}_SOURCE" VORTEX_FETCH_DB_SOURCE="${!_v:-url}" # Force DB fetch even if the cache exists. -# Usually set in CircleCI UI to override per build cache. _v="VORTEX_FETCH_DB${_db_index}_FORCE" VORTEX_FETCH_DB_FORCE="${!_v:-}" @@ -103,7 +102,7 @@ fi ls -Alh "${VORTEX_FETCH_DB_DIR}" || true -# Create a semaphore file to indicate that the database has been fetched. +# The semaphore file marks a completed database fetch. [ -n "${VORTEX_FETCH_DB_SEMAPHORE:-}" ] && touch "${VORTEX_FETCH_DB_SEMAPHORE}" pass "Finished database${_db_index:+ ${_db_index}} fetch." diff --git a/.vortex/tooling/src/vortex-fetch-db-acquia b/.vortex/tooling/src/vortex-fetch-db-acquia index c612a5f05..973e42088 100755 --- a/.vortex/tooling/src/vortex-fetch-db-acquia +++ b/.vortex/tooling/src/vortex-fetch-db-acquia @@ -87,23 +87,16 @@ for cmd in php curl gunzip; do command -v "${cmd}" >/dev/null || fail "Command $ info "Started database dump fetch from Acquia." -# -# Extract last value from JSON object passed via STDIN. -# extract_json_last_value() { local key=${1} php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); \$last=array_pop(\$data); isset(\$last[\"${key}\"]) ? print trim(json_encode(\$last[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Extract keyed value from JSON object passed via STDIN. -# extract_json_value() { local key=${1} php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); isset(\$data[\"${key}\"]) ? print trim(json_encode(\$data[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# Check that all required variables are present. [ -z "${VORTEX_FETCH_DB_ACQUIA_KEY}" ] && fail "Missing required value for VORTEX_FETCH_DB_ACQUIA_KEY or VORTEX_ACQUIA_KEY." [ -z "${VORTEX_FETCH_DB_ACQUIA_SECRET}" ] && fail "Missing required value for VORTEX_FETCH_DB_ACQUIA_SECRET or VORTEX_ACQUIA_SECRET." [ -z "${VORTEX_FETCH_DB_ACQUIA_APP_NAME}" ] && fail "Missing required value for VORTEX_FETCH_DB_ACQUIA_APP_NAME or VORTEX_ACQUIA_APP_NAME." @@ -116,7 +109,6 @@ task "Retrieving authentication token." token_json=$(curl -s -L https://accounts.acquia.com/api/auth/oauth/token --data-urlencode "client_id=${VORTEX_FETCH_DB_ACQUIA_KEY}" --data-urlencode "client_secret=${VORTEX_FETCH_DB_ACQUIA_SECRET}" --data-urlencode "grant_type=client_credentials") [ "${VORTEX_DEBUG-}" = "1" ] && note "Token API response received (token redacted)." -# Check for HTTP errors in response if echo "${token_json}" | grep -q '"error"'; then fail "Authentication failed. Check VORTEX_FETCH_DB_ACQUIA_KEY or VORTEX_ACQUIA_KEY and VORTEX_FETCH_DB_ACQUIA_SECRET or VORTEX_ACQUIA_SECRET. API response: ${token_json}" fi @@ -130,7 +122,6 @@ task "Retrieving ${VORTEX_FETCH_DB_ACQUIA_APP_NAME} application UUID." app_uuid_json=$(curl -s -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "https://cloud.acquia.com/api/applications?filter=name%3D${VORTEX_FETCH_DB_ACQUIA_APP_NAME/ /%20}") [ "${VORTEX_DEBUG-}" = "1" ] && note "Application API response: ${app_uuid_json}" -# Check for empty items array (application not found) if echo "${app_uuid_json}" | grep -q '"items":\[\]'; then fail "Application \"${VORTEX_FETCH_DB_ACQUIA_APP_NAME}\" not found. Check application name and access permissions." fi @@ -144,7 +135,6 @@ task "Retrieving ${VORTEX_FETCH_DB_ENVIRONMENT} environment ID." envs_json=$(curl -s -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "https://cloud.acquia.com/api/applications/${app_uuid}/environments?filter=name%3D${VORTEX_FETCH_DB_ENVIRONMENT}") [ "${VORTEX_DEBUG-}" = "1" ] && note "Environments API response: ${envs_json}" -# Check for empty items array (environment not found) if echo "${envs_json}" | grep -q '"items":\[\]'; then fail "Environment \"${VORTEX_FETCH_DB_ENVIRONMENT}\" not found in application \"${VORTEX_FETCH_DB_ACQUIA_APP_NAME}\". Check environment name." fi @@ -154,20 +144,16 @@ env_id=$(echo "${envs_json}" | extract_json_value "_embedded" | extract_json_val [ -z "${env_id}" ] && fail "Unable to retrieve an environment ID for \"${VORTEX_FETCH_DB_ENVIRONMENT}\". API response: ${envs_json}" pass "Retrieved ${VORTEX_FETCH_DB_ENVIRONMENT} environment ID." -# If fresh backup requested, create a new backup and wait for it to complete. if [ "${VORTEX_FETCH_DB_FRESH}" = "1" ]; then task "Creating new database backup for ${VORTEX_FETCH_DB_ACQUIA_DB_NAME}." - # Trigger backup creation create_backup_json=$(curl -s -L -X POST -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "https://cloud.acquia.com/api/environments/${env_id}/databases/${VORTEX_FETCH_DB_ACQUIA_DB_NAME}/backups") [ "${VORTEX_DEBUG-}" = "1" ] && note "Create backup API response: ${create_backup_json}" - # Check for errors if echo "${create_backup_json}" | grep -q '"error"'; then fail "Unable to create backup for database \"${VORTEX_FETCH_DB_ACQUIA_DB_NAME}\". API response: ${create_backup_json}" fi - # Extract notification URL for status checking notification_url=$(echo "${create_backup_json}" | extract_json_value "_links" | extract_json_value "notification" | extract_json_value "href") [ "${VORTEX_DEBUG-}" = "1" ] && note "Notification URL: ${notification_url}" @@ -186,7 +172,6 @@ if [ "${VORTEX_FETCH_DB_FRESH}" = "1" ]; then sleep "${wait_interval}" elapsed=$((elapsed + wait_interval)) - # Check backup status status_json=$(curl -s -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "${notification_url}") [ "${VORTEX_DEBUG-}" = "1" ] && note "Status check (${elapsed}s): ${status_json}" @@ -213,12 +198,10 @@ task "Discovering latest backup ID for database ${VORTEX_FETCH_DB_ACQUIA_DB_NAME backups_json=$(curl --progress-bar -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "https://cloud.acquia.com/api/environments/${env_id}/databases/${VORTEX_FETCH_DB_ACQUIA_DB_NAME}/backups?sort=created") [ "${VORTEX_DEBUG-}" = "1" ] && note "Backups API response: ${backups_json}" -# Check for HTTP errors or database not found if echo "${backups_json}" | grep -q '"error"'; then fail "Database \"${VORTEX_FETCH_DB_ACQUIA_DB_NAME}\" not found in environment \"${VORTEX_FETCH_DB_ENVIRONMENT}\". Check database name. API response: ${backups_json}" fi -# Check for empty items array (no backups found) if echo "${backups_json}" | grep -q '"items":\[\]'; then fail "No backups found for database \"${VORTEX_FETCH_DB_ACQUIA_DB_NAME}\" in environment \"${VORTEX_FETCH_DB_ENVIRONMENT}\". Try creating a backup first." fi @@ -228,7 +211,6 @@ backup_id=$(echo "${backups_json}" | extract_json_value "_embedded" | extract_js [ "${VORTEX_DEBUG-}" = "1" ] && note "Extracted backup ID: ${backup_id}" [ -z "${backup_id}" ] && fail "Unable to discover backup ID for database \"${VORTEX_FETCH_DB_ACQUIA_DB_NAME}\". API response: ${backups_json}" -# Insert backup id as a suffix. file_extension="${VORTEX_FETCH_DB_ACQUIA_DB_FILE##*.}" file_prefix="${VORTEX_FETCH_DB_ACQUIA_DB_NAME}_backup_" file_name="${VORTEX_FETCH_DB_ACQUIA_DB_DIR}/${file_prefix}${backup_id}.${file_extension}" @@ -242,18 +224,15 @@ pass "Discovered latest backup ID ${backup_id} for database ${VORTEX_FETCH_DB_AC if [ -f "${file_name_discovered}" ]; then note "Found existing cached database file \"${file_name_discovered}\" for database \"${VORTEX_FETCH_DB_ACQUIA_DB_NAME}\"." else - # If the gzipped version exists, then we don't need to re-fetch it. if [ ! -f "${file_name_compressed}" ]; then note "Using the latest backup ID ${backup_id} for database ${VORTEX_FETCH_DB_ACQUIA_DB_NAME}." [ ! -d "${VORTEX_FETCH_DB_ACQUIA_DB_DIR:-}" ] && note "Creating dump directory ${VORTEX_FETCH_DB_ACQUIA_DB_DIR}." && mkdir -p "${VORTEX_FETCH_DB_ACQUIA_DB_DIR}" task "Discovering backup URL." - # The Acquia API responds with a 200 and a JSON body containing the - # temporary, pre-signed S3 download URL under the "url" key. Fetch the - # body and extract that URL. The Authorization header is required only to - # query the Acquia API; the returned S3 URL is pre-signed and is fetched - # later without it. + # The response carries a temporary pre-signed S3 download URL under the + # "url" key. The Authorization header applies only to this API call; the + # pre-signed URL is fetched later without it. backup_url_json=$(curl -s -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" "https://cloud.acquia.com/api/environments/${env_id}/databases/${VORTEX_FETCH_DB_ACQUIA_DB_NAME}/backups/${backup_id}/actions/download") [ "${VORTEX_DEBUG-}" = "1" ] && note "Download action API response: ${backup_url_json}" backup_url=$(echo "${backup_url_json}" | extract_json_value "url") @@ -268,8 +247,7 @@ else # shellcheck disable=SC2181 [ "${download_result}" -ne 0 ] && fail "Unable to fetch database ${VORTEX_FETCH_DB_ACQUIA_DB_NAME}. curl exit code: ${download_result}" - # Check if the fetched file exists and has content. Leave the file in - # place on failure so it can be inspected. + # The file is left in place on failure so it can be inspected. if [ ! -f "${file_name_compressed}" ] || [ ! -s "${file_name_compressed}" ]; then fail "Fetched file is empty or missing: ${file_name_compressed}" fi @@ -283,8 +261,7 @@ else task "Expanding database file ${file_name_compressed} into ${file_name}." [ "${VORTEX_DEBUG-}" = "1" ] && note "Starting decompression of ${file_name_compressed}." - # Test the gzip file first to ensure it's valid. Leave the file in place - # on failure so it can be inspected. + # The file is left in place on failure so it can be inspected. if ! gunzip -t "${file_name_compressed}" 2>/dev/null; then fail "Fetched file is not a valid gzip archive: ${file_name_compressed}" fi @@ -293,8 +270,7 @@ else decompress_result=$? [ "${VORTEX_DEBUG-}" = "1" ] && note "Decompression result: ${decompress_result}" - # Check decompression result and file validity. Leave both files in place - # on failure so they can be inspected. + # Both files are left in place on failure so they can be inspected. if [ "${decompress_result}" != 0 ] || [ ! -f "${file_name}" ] || [ ! -s "${file_name}" ]; then fail "Unable to process database dump file \"${file_name}\". Decompression exit code: ${decompress_result}" fi diff --git a/.vortex/tooling/src/vortex-fetch-db-container-registry b/.vortex/tooling/src/vortex-fetch-db-container-registry index 7d671a35c..4d6e9b294 100755 --- a/.vortex/tooling/src/vortex-fetch-db-container-registry +++ b/.vortex/tooling/src/vortex-fetch-db-container-registry @@ -82,11 +82,8 @@ fi image_expanded_successfully=0 if [ -f "${archive_file}" ]; then task "Found archived database container image file ${archive_file}. Expanding..." - # Always use archived image, even if such image already exists on the host. docker load -q --input "${archive_file}" - # Check that image was expanded and now exists on the host or notify - # that it will be downloaded from the registry. if docker image inspect "${VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE}" >/dev/null 2>&1; then note "Found expanded ${VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE} image on host." image_expanded_successfully=1 @@ -111,8 +108,7 @@ fi if [ "${should_fetch}" -eq 1 ]; then if [ ! -f "${archive_file}" ] && [ -n "${VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE_BASE:-}" ]; then - # If the image archive does not exist and base image was provided - use the - # base image which allows "clean slate" for the database. + # The base image provides a "clean slate" for the database. note "Database container image was not found. Using base image ${VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE_BASE}." export VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE="${VORTEX_FETCH_DB_CONTAINER_REGISTRY_IMAGE_BASE}" fi diff --git a/.vortex/tooling/src/vortex-fetch-db-ftp b/.vortex/tooling/src/vortex-fetch-db-ftp index 8476ade26..c7c190d19 100755 --- a/.vortex/tooling/src/vortex-fetch-db-ftp +++ b/.vortex/tooling/src/vortex-fetch-db-ftp @@ -60,7 +60,6 @@ fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\03 # shellcheck disable=SC2043 for cmd in curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done -# Check all required values. [ -z "${VORTEX_FETCH_DB_FTP_USER}" ] && fail "Missing required value for VORTEX_FETCH_DB_FTP_USER." [ -z "${VORTEX_FETCH_DB_FTP_PASS}" ] && fail "Missing required value for VORTEX_FETCH_DB_FTP_PASS." [ -z "${VORTEX_FETCH_DB_FTP_HOST}" ] && fail "Missing required value for VORTEX_FETCH_DB_FTP_HOST." diff --git a/.vortex/tooling/src/vortex-fetch-db-lagoon b/.vortex/tooling/src/vortex-fetch-db-lagoon index fa44790b6..aef7af48f 100755 --- a/.vortex/tooling/src/vortex-fetch-db-lagoon +++ b/.vortex/tooling/src/vortex-fetch-db-lagoon @@ -123,13 +123,6 @@ if [ "${VORTEX_FETCH_DB_FRESH}" = "1" ]; then note "Database dump refresh requested. Will create a new dump." fi -# Initiates an SSH connection to a remote server using provided SSH options. -# On the server: -# 1. Checks for the existence of a specific database dump file. -# 2. If the file doesn't exist or a refresh is requested: -# a. Optionally removes any previous database dumps. -# b. Uses `drush` to create a new database dump with specific table structure options. -# 3. If the file exists and no refresh is requested, notifies of using the existing dump. task "Discovering or creating a database dump on Lagoon." ssh \ "${ssh_opts[@]}" \ diff --git a/.vortex/tooling/src/vortex-fetch-db-s3 b/.vortex/tooling/src/vortex-fetch-db-s3 index 95b360fdc..b7703a5a5 100755 --- a/.vortex/tooling/src/vortex-fetch-db-s3 +++ b/.vortex/tooling/src/vortex-fetch-db-s3 @@ -68,7 +68,6 @@ for cmd in curl openssl; do command -v "${cmd}" >/dev/null || fail "Command ${cm info "Started database dump fetch from S3." -# Ensure prefix ends with a trailing slash if non-empty. [ -n "${VORTEX_FETCH_DB_S3_PREFIX}" ] && VORTEX_FETCH_DB_S3_PREFIX="${VORTEX_FETCH_DB_S3_PREFIX%/}/" mkdir -p "${VORTEX_FETCH_DB_S3_DB_DIR}" @@ -107,7 +106,6 @@ ${headers}\n ${signed_headers} ${payload_hash}" -# Create the signature. create_signature() { string_to_sign="${auth_type}\n${date_long}\n${date_short}/${VORTEX_FETCH_DB_S3_REGION}/${service}/aws4_request\n$(hash_sha256 "${request}")" date_key=$(hmac_sha256 key:"AWS4${VORTEX_FETCH_DB_S3_SECRET_KEY}" "${date_short}") diff --git a/.vortex/tooling/src/vortex-fetch-db-url b/.vortex/tooling/src/vortex-fetch-db-url index eb627a975..9d0ca81ba 100755 --- a/.vortex/tooling/src/vortex-fetch-db-url +++ b/.vortex/tooling/src/vortex-fetch-db-url @@ -50,7 +50,6 @@ for cmd in curl unzip; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} info "Started database dump fetch from URL." -# Check all required values. [ -z "${VORTEX_FETCH_DB_URL}" ] && fail "Missing required value for VORTEX_FETCH_DB_URL." mkdir -p "${VORTEX_FETCH_DB_URL_DB_DIR}" @@ -62,7 +61,6 @@ if [ "${VORTEX_FETCH_DB_URL%*.zip}" != "${VORTEX_FETCH_DB_URL}" ]; then note "Detecting zip file, preparing for extraction." mv "${VORTEX_FETCH_DB_URL_DB_DIR}/${VORTEX_FETCH_DB_URL_DB_FILE}" "${VORTEX_FETCH_DB_URL_DB_DIR}/${VORTEX_FETCH_DB_URL_DB_FILE}.zip" - # Create temporary directory for extraction temp_extract_dir="${VORTEX_FETCH_DB_URL_DB_DIR}/tmp_extract_$$" mkdir -p "${temp_extract_dir}" @@ -74,7 +72,6 @@ if [ "${VORTEX_FETCH_DB_URL%*.zip}" != "${VORTEX_FETCH_DB_URL}" ]; then unzip -o "${VORTEX_FETCH_DB_URL_DB_DIR}/${VORTEX_FETCH_DB_URL_DB_FILE}.zip" -d "${temp_extract_dir}" fi - # Find the first regular file (not directory) in the extracted content. note "Discovering database file in archive." extracted_file=$(find "${temp_extract_dir}" -type f -print | head -n 1) diff --git a/.vortex/tooling/src/vortex-import-db-file b/.vortex/tooling/src/vortex-import-db-file index 06c3a5dcd..ef562ceeb 100755 --- a/.vortex/tooling/src/vortex-import-db-file +++ b/.vortex/tooling/src/vortex-import-db-file @@ -29,8 +29,6 @@ info "Started database file import." drush() { ./vendor/bin/drush -y "$@"; } -# Use the dump file provided as the first argument or fall back to the default -# location. dump_file="${1:-${VORTEX_IMPORT_DB_FILE_DIR}/${VORTEX_IMPORT_DB_FILE}}" if [ ! -f "${dump_file}" ]; then diff --git a/.vortex/tooling/src/vortex-notify b/.vortex/tooling/src/vortex-notify index b764f1c63..c8b86a4e8 100755 --- a/.vortex/tooling/src/vortex-notify +++ b/.vortex/tooling/src/vortex-notify @@ -67,13 +67,11 @@ info "Started dispatching notifications." [ -n "${VORTEX_NOTIFY_SKIP:-}" ] && pass "Skipped dispatching notifications." && exit 0 -# Validate required variables. [ -z "${VORTEX_NOTIFY_BRANCH}" ] && fail "Missing required value for VORTEX_NOTIFY_BRANCH." [ -z "${VORTEX_NOTIFY_SHA}" ] && fail "Missing required value for VORTEX_NOTIFY_SHA." [ -z "${VORTEX_NOTIFY_LABEL}" ] && fail "Missing required value for VORTEX_NOTIFY_LABEL." [ -z "${VORTEX_NOTIFY_ENVIRONMENT_URL}" ] && fail "Missing required value for VORTEX_NOTIFY_ENVIRONMENT_URL." -# Auto-generate LOGIN_URL if not provided. if [ -z "${VORTEX_NOTIFY_LOGIN_URL}" ]; then VORTEX_NOTIFY_LOGIN_URL="${VORTEX_NOTIFY_ENVIRONMENT_URL}/user/login" fi @@ -82,14 +80,13 @@ fi # collected. VORTEX_NOTIFY_LOG_FILE="" -# All producer logs from the shared directory are combined into one file, each as -# its own titled section. Best-effort: a failure here must not abort dispatching. +# Log collection is best-effort, so failures here do not abort dispatching. if [ "${VORTEX_NOTIFY_LOG}" = "1" ] && [ -d "${VORTEX_NOTIFY_LOG_DIR}" ]; then set +e _combined="${VORTEX_NOTIFY_LOG_DIR}/combined" # Only publish the combined file when it was freshly truncated, so a failed - # write can never leave a previous run's log to be sent. It is owner-only, as it - # may aggregate logs containing secrets. + # write can never leave a previous run's log to be sent. It is owner-only, + # as it may aggregate logs containing secrets. if : >"${_combined}"; then chmod 600 "${_combined}" 2>/dev/null for _log in "${VORTEX_NOTIFY_LOG_DIR}"/*.log; do @@ -105,7 +102,6 @@ if [ "${VORTEX_NOTIFY_LOG}" = "1" ] && [ -d "${VORTEX_NOTIFY_LOG_DIR}" ]; then set -e fi -# Export variables so notification scripts can use them. export VORTEX_NOTIFY_BRANCH export VORTEX_NOTIFY_SHA export VORTEX_NOTIFY_PR_NUMBER @@ -116,7 +112,6 @@ export VORTEX_NOTIFY_LOG export VORTEX_NOTIFY_LOG_DIR export VORTEX_NOTIFY_LOG_FILE -# Validate event type (scripts will handle event-specific logic). if [ "${VORTEX_NOTIFY_EVENT}" != "pre_deployment" ] && [ "${VORTEX_NOTIFY_EVENT}" != "post_deployment" ]; then fail "Unsupported event ${VORTEX_NOTIFY_EVENT} provided." fi diff --git a/.vortex/tooling/src/vortex-notify-diffy b/.vortex/tooling/src/vortex-notify-diffy index a3f107e45..be1a3d2d9 100755 --- a/.vortex/tooling/src/vortex-notify-diffy +++ b/.vortex/tooling/src/vortex-notify-diffy @@ -78,8 +78,6 @@ if [ "${VORTEX_NOTIFY_DIFFY_EVENT}" = "pre_deployment" ]; then exit 0 fi -# Apply branch filter when configured. When empty (default), every -# deployment is dispatched and the workflow gates on PR + label. if [ -n "${VORTEX_NOTIFY_DIFFY_BRANCHES}" ]; then if ! echo ",${VORTEX_NOTIFY_DIFFY_BRANCHES}," | grep -qF ",${VORTEX_NOTIFY_DIFFY_BRANCH},"; then pass "Skipped Diffy notification for branch \"${VORTEX_NOTIFY_DIFFY_BRANCH}\" (not in branch allowlist)." @@ -87,14 +85,12 @@ if [ -n "${VORTEX_NOTIFY_DIFFY_BRANCHES}" ]; then fi fi -# Validate required values. [ -z "${VORTEX_NOTIFY_DIFFY_TOKEN}" ] && fail "Missing required value for VORTEX_NOTIFY_DIFFY_TOKEN." [ -z "${VORTEX_NOTIFY_DIFFY_REPOSITORY}" ] && fail "Missing required value for VORTEX_NOTIFY_DIFFY_REPOSITORY." [ -z "${VORTEX_NOTIFY_DIFFY_BRANCH}" ] && fail "Missing required value for VORTEX_NOTIFY_DIFFY_BRANCH." [ -z "${VORTEX_NOTIFY_DIFFY_ENVIRONMENT_URL}" ] && fail "Missing required value for VORTEX_NOTIFY_DIFFY_ENVIRONMENT_URL." [ -z "${VORTEX_NOTIFY_DIFFY_LABEL}" ] && fail "Missing required value for VORTEX_NOTIFY_DIFFY_LABEL." -# Sanitize repository (extract owner/repo, hide host if any). repository_sanitized=$(echo "${VORTEX_NOTIFY_DIFFY_REPOSITORY}" | sed -E 's|^https?://[^/]+/||; s|\.git$||') info "Diffy notification summary:" @@ -106,9 +102,7 @@ note "Source env : ${VORTEX_NOTIFY_DIFFY_SOURCE_ENV}" note "Dispatch event : ${VORTEX_NOTIFY_DIFFY_EVENT_TYPE}" note "Event : ${VORTEX_NOTIFY_DIFFY_EVENT}" -# Build dispatch payload. All values JSON-escaped via a single PHP call. -# The workflow parses the PR (if any) from the target URL's `pr-` -# segment, so we do not include a PR number in the payload. +# All values are JSON-escaped via a single PHP call. payload=$( VORTEX_NOTIFY_DIFFY_EVENT_TYPE="${VORTEX_NOTIFY_DIFFY_EVENT_TYPE}" \ VORTEX_NOTIFY_DIFFY_BRANCH="${VORTEX_NOTIFY_DIFFY_BRANCH}" \ diff --git a/.vortex/tooling/src/vortex-notify-email b/.vortex/tooling/src/vortex-notify-email index 16c68db5f..ad2245eb7 100755 --- a/.vortex/tooling/src/vortex-notify-email +++ b/.vortex/tooling/src/vortex-notify-email @@ -87,7 +87,6 @@ fi info "Started email notification." -# Set default message template if not provided. if [ -z "${VORTEX_NOTIFY_EMAIL_MESSAGE}" ]; then VORTEX_NOTIFY_EMAIL_MESSAGE="## This is an automated message ## @@ -98,7 +97,6 @@ Login at: %login_url% %deployment_log%" fi -# Skip if this is a pre-deployment event (email only for post-deployment). if [ "${VORTEX_NOTIFY_EMAIL_EVENT}" = "pre_deployment" ]; then pass "Skipped email notification for pre_deployment event." exit 0 @@ -116,11 +114,9 @@ else fail "Neither mail nor sendmail commands are available." fi -# Build message by replacing tokens. timestamp=$(date '+%d/%m/%Y %H:%M:%S %Z') subject="${VORTEX_NOTIFY_EMAIL_PROJECT} deployment notification of ${VORTEX_NOTIFY_EMAIL_LABEL}" -# Replace tokens in message template. content="${VORTEX_NOTIFY_EMAIL_MESSAGE}" content=$(REPLACEMENT="${VORTEX_NOTIFY_EMAIL_PROJECT}" TEMPLATE="${content}" php -r 'echo str_replace("%project%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') content=$(REPLACEMENT="${VORTEX_NOTIFY_EMAIL_LABEL}" TEMPLATE="${content}" php -r 'echo str_replace("%label%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') @@ -128,9 +124,9 @@ content=$(REPLACEMENT="${timestamp}" TEMPLATE="${content}" php -r 'echo str_repl content=$(REPLACEMENT="${VORTEX_NOTIFY_EMAIL_ENVIRONMENT_URL}" TEMPLATE="${content}" php -r 'echo str_replace("%environment_url%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') content=$(REPLACEMENT="${VORTEX_NOTIFY_EMAIL_LOGIN_URL}" TEMPLATE="${content}" php -r 'echo str_replace("%login_url%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') -# Replace the deployment log token last. The log file is read inside PHP (never -# interpolated into the shell) and inserted as literal text, so arbitrary log -# content cannot break out or re-trigger earlier token replacements. +# Replace the deployment log token last. The log file is read inside PHP and +# inserted as literal text, so log content cannot re-trigger earlier token +# replacements or reach the shell. log_file="" [ "${VORTEX_NOTIFY_EMAIL_LOG}" = "1" ] && log_file="${VORTEX_NOTIFY_EMAIL_LOG_FILE}" content=$(TEMPLATE="${content}" LOG_FILE="${log_file}" php -r ' diff --git a/.vortex/tooling/src/vortex-notify-github b/.vortex/tooling/src/vortex-notify-github index b815bed97..237a3c83b 100755 --- a/.vortex/tooling/src/vortex-notify-github +++ b/.vortex/tooling/src/vortex-notify-github @@ -89,9 +89,6 @@ extract_json_first_value() { php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); \$first=reset(\$data); isset(\$first[\"${key}\"]) ? print trim(json_encode(\$first[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Function to extract keyed value from JSON object passed via STDIN. -# extract_json_value() { local key=${1} php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); isset(\$data[\"${key}\"]) ? print trim(json_encode(\$data[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" @@ -123,7 +120,6 @@ echo json_encode([ deployment_id="$(echo "${payload}" | extract_json_value "id" || true)" - # Check deployment ID. if [ -z "${deployment_id}" ] || [ "${#deployment_id}" -lt 9 ] || [ "${#deployment_id}" -gt 11 ] || [ "$(expr "x${deployment_id}" : "x[0-9]*$")" -eq 0 ]; then note "Wait for GitHub checks to finish and try again." fail "Unable to get a deployment ID for a ${VORTEX_NOTIFY_GITHUB_EVENT} operation. Payload: ${payload}" @@ -133,7 +129,7 @@ echo json_encode([ else [ -z "${VORTEX_NOTIFY_GITHUB_ENVIRONMENT_URL}" ] && fail "Missing required value for VORTEX_NOTIFY_GITHUB_ENVIRONMENT_URL." - # Returns all deployment for this ref sorted from the latest to the oldest. + # The API returns all deployments for this ref sorted from latest to oldest. payload="$(curl \ -X GET \ -H "Authorization: token ${VORTEX_NOTIFY_GITHUB_TOKEN}" \ @@ -143,7 +139,6 @@ else deployment_id="$(echo "${payload}" | extract_json_first_value "id" || true)" - # Check deployment ID. if [ -z "${deployment_id}" ] || [ "${#deployment_id}" -lt 9 ] || [ "${#deployment_id}" -gt 11 ] || [ "$(expr "x${deployment_id}" : "x[0-9]*$")" -eq 0 ]; then note "Check that a pre_deployment notification was dispatched." fail "Unable to get a deployment ID for a ${VORTEX_NOTIFY_GITHUB_EVENT} operation. Payload: ${payload}" @@ -157,7 +152,6 @@ else note "Deployment ID : ${deployment_id}" note "Event : ${VORTEX_NOTIFY_GITHUB_EVENT}" - # Post status update. body="$(VORTEX_NOTIFY_GITHUB_ENVIRONMENT_URL="${VORTEX_NOTIFY_GITHUB_ENVIRONMENT_URL}" php -r ' echo json_encode([ "state" => "success", diff --git a/.vortex/tooling/src/vortex-notify-jira b/.vortex/tooling/src/vortex-notify-jira index 5618f0bb2..05df2c0cd 100755 --- a/.vortex/tooling/src/vortex-notify-jira +++ b/.vortex/tooling/src/vortex-notify-jira @@ -104,7 +104,6 @@ fi info "Started JIRA notification." -# Skip if this is a pre-deployment event (JIRA only processes post-deployment). if [ "${VORTEX_NOTIFY_JIRA_EVENT}" = "pre_deployment" ]; then pass "Skipped JIRA notification for pre_deployment event." exit 0 @@ -118,17 +117,11 @@ extract_json_first_value() { php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); \$first=reset(\$data); isset(\$first[\"${key}\"]) ? print trim(json_encode(\$first[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Function to extract keyed value from JSON object passed via STDIN. -# extract_json_value() { local key="${1}" php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); isset(\$data[\"${key}\"]) ? print trim(json_encode(\$data[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Function to extract keyed value from JSON object passed via STDIN. -# extract_json_value_by_value() { local key="${1}" local value="${2}" @@ -173,14 +166,12 @@ task "Posting a comment." [ -z "${VORTEX_NOTIFY_JIRA_ENVIRONMENT_URL}" ] && fail "Missing required value for VORTEX_NOTIFY_JIRA_ENVIRONMENT_URL." [ -z "${VORTEX_NOTIFY_JIRA_LOGIN_URL}" ] && fail "Missing required value for VORTEX_NOTIFY_JIRA_LOGIN_URL." -# Generate timestamp. timestamp=$(date '+%d/%m/%Y %H:%M:%S %Z') -# Read the deployment log (when enabled) to include it in the comment. log_file="" [ "${VORTEX_NOTIFY_JIRA_LOG}" = "1" ] && log_file="${VORTEX_NOTIFY_JIRA_LOG_FILE}" -# Build JIRA Atlassian Document Format (ADF) from the message using PHP with proper escaping. +# The comment body is JIRA Atlassian Document Format (ADF). # shellcheck disable=SC2016 comment_body=$(VORTEX_NOTIFY_JIRA_PROJECT="${VORTEX_NOTIFY_JIRA_PROJECT}" VORTEX_NOTIFY_JIRA_LABEL="${VORTEX_NOTIFY_JIRA_LABEL}" timestamp="${timestamp}" VORTEX_NOTIFY_JIRA_ENVIRONMENT_URL="${VORTEX_NOTIFY_JIRA_ENVIRONMENT_URL}" VORTEX_NOTIFY_JIRA_LOGIN_URL="${VORTEX_NOTIFY_JIRA_LOGIN_URL}" VORTEX_NOTIFY_JIRA_MESSAGE="${VORTEX_NOTIFY_JIRA_MESSAGE}" LOG_FILE="${log_file}" php -r ' $project = getenv("VORTEX_NOTIFY_JIRA_PROJECT"); @@ -193,8 +184,7 @@ $log_file = getenv("LOG_FILE"); $log = ($log_file !== "" && is_file($log_file)) ? rtrim((string) file_get_contents($log_file), "\r\n") : ""; if ($message !== "") { - // A configured template replaces the default body. Tokens are substituted - // and newlines become hard breaks so the text keeps its shape in JIRA. + // Newlines become hardBreak nodes so line breaks render in JIRA. $message = str_replace( ["%project%", "%label%", "%timestamp%", "%environment_url%", "%login_url%"], [$project, $label, $timestamp, $env_url, $login_url], diff --git a/.vortex/tooling/src/vortex-notify-newrelic b/.vortex/tooling/src/vortex-notify-newrelic index 7ca2b9637..91af001eb 100755 --- a/.vortex/tooling/src/vortex-notify-newrelic +++ b/.vortex/tooling/src/vortex-notify-newrelic @@ -108,8 +108,6 @@ for cmd in php curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} i info "Started New Relic notification." -# Auto-generate revision if not provided. -# Use SHA if available, otherwise fall back to LABEL-TIMESTAMP. if [ -z "${VORTEX_NOTIFY_NEWRELIC_REVISION}" ]; then if [ -n "${VORTEX_NOTIFY_NEWRELIC_SHA}" ]; then VORTEX_NOTIFY_NEWRELIC_REVISION="${VORTEX_NOTIFY_NEWRELIC_SHA}" @@ -121,28 +119,23 @@ if [ -z "${VORTEX_NOTIFY_NEWRELIC_REVISION}" ]; then note "Auto-generated revision: ${VORTEX_NOTIFY_NEWRELIC_REVISION}" fi -# Skip if this is a pre-deployment event (New Relic only for post-deployment). if [ "${VORTEX_NOTIFY_NEWRELIC_EVENT}" = "pre_deployment" ]; then pass "Skipped New Relic notification for pre_deployment event." exit 0 fi -# Set default description template if not provided. if [ -z "${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" ]; then VORTEX_NOTIFY_NEWRELIC_DESCRIPTION="Site %project% %label% has been deployed at %timestamp% and is available at %environment_url%" fi -# Build message by replacing tokens. timestamp=$(date '+%d/%m/%Y %H:%M:%S %Z') -# Replace tokens in description template. VORTEX_NOTIFY_NEWRELIC_DESCRIPTION=$(REPLACEMENT="${VORTEX_NOTIFY_NEWRELIC_PROJECT}" TEMPLATE="${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" php -r 'echo str_replace("%project%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') VORTEX_NOTIFY_NEWRELIC_DESCRIPTION=$(REPLACEMENT="${VORTEX_NOTIFY_NEWRELIC_LABEL}" TEMPLATE="${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" php -r 'echo str_replace("%label%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') VORTEX_NOTIFY_NEWRELIC_DESCRIPTION=$(REPLACEMENT="${timestamp}" TEMPLATE="${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" php -r 'echo str_replace("%timestamp%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') VORTEX_NOTIFY_NEWRELIC_DESCRIPTION=$(REPLACEMENT="${VORTEX_NOTIFY_NEWRELIC_ENVIRONMENT_URL}" TEMPLATE="${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" php -r 'echo str_replace("%environment_url%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') VORTEX_NOTIFY_NEWRELIC_DESCRIPTION=$(REPLACEMENT="${VORTEX_NOTIFY_NEWRELIC_LOGIN_URL}" TEMPLATE="${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" php -r 'echo str_replace("%login_url%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') -# Build changelog if not provided (defaults to description). if [ -z "${VORTEX_NOTIFY_NEWRELIC_CHANGELOG}" ]; then VORTEX_NOTIFY_NEWRELIC_CHANGELOG="${VORTEX_NOTIFY_NEWRELIC_DESCRIPTION}" fi @@ -157,8 +150,6 @@ fi pass "Completed application ID discovery." -# Check if the VORTEX_NOTIFY_NEWRELIC_APPID variable is empty OR -# if the variable doesn't contain only numeric values and exit. task "Checking if the application ID is valid." if [ -z "${VORTEX_NOTIFY_NEWRELIC_APPID}" ] || [ "$(expr "x${VORTEX_NOTIFY_NEWRELIC_APPID}" : "x[0-9]*$")" -eq 0 ]; then pass "Notification skipped: No New Relic application ID found for ${VORTEX_NOTIFY_NEWRELIC_APP_NAME}. This is expected for non-configured environments." diff --git a/.vortex/tooling/src/vortex-notify-slack b/.vortex/tooling/src/vortex-notify-slack index 2f60d3f32..ecd0eca26 100755 --- a/.vortex/tooling/src/vortex-notify-slack +++ b/.vortex/tooling/src/vortex-notify-slack @@ -92,7 +92,6 @@ fi info "Started Slack notification." -# Set default message template if not provided. if [ -z "${VORTEX_NOTIFY_SLACK_MESSAGE}" ]; then VORTEX_NOTIFY_SLACK_MESSAGE="## This is an automated message ## @@ -101,10 +100,8 @@ Site %project% %label% has been deployed at %timestamp% and is available at %env Login at: %login_url%" fi -# Generate timestamp. timestamp=$(date '+%d/%m/%Y %H:%M:%S %Z') -# Build fallback message by replacing tokens. fallback_message="${VORTEX_NOTIFY_SLACK_MESSAGE}" fallback_message=$(REPLACEMENT="${VORTEX_NOTIFY_SLACK_PROJECT}" TEMPLATE="${fallback_message}" php -r 'echo str_replace("%project%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') fallback_message=$(REPLACEMENT="${VORTEX_NOTIFY_SLACK_LABEL}" TEMPLATE="${fallback_message}" php -r 'echo str_replace("%label%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') @@ -112,7 +109,6 @@ fallback_message=$(REPLACEMENT="${timestamp}" TEMPLATE="${fallback_message}" php fallback_message=$(REPLACEMENT="${VORTEX_NOTIFY_SLACK_ENVIRONMENT_URL}" TEMPLATE="${fallback_message}" php -r 'echo str_replace("%environment_url%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') fallback_message=$(REPLACEMENT="${VORTEX_NOTIFY_SLACK_LOGIN_URL}" TEMPLATE="${fallback_message}" php -r 'echo str_replace("%login_url%", getenv("REPLACEMENT"), getenv("TEMPLATE"));') -# Determine color based on event type. color="good" event_label="Deployment Complete" if [ "${VORTEX_NOTIFY_SLACK_EVENT}" = "pre_deployment" ]; then @@ -120,14 +116,12 @@ if [ "${VORTEX_NOTIFY_SLACK_EVENT}" = "pre_deployment" ]; then event_label="Deployment Starting" fi -# Build the message title. title="${event_label}: ${VORTEX_NOTIFY_SLACK_PROJECT}" -# Read the deployment log (when enabled) to include it in the message. log_file="" [ "${VORTEX_NOTIFY_SLACK_LOG}" = "1" ] && log_file="${VORTEX_NOTIFY_SLACK_LOG_FILE}" -# Build payload using PHP with proper escaping from environment variables. +# All values are passed via the environment and JSON-escaped in PHP. payload=$(VORTEX_NOTIFY_SLACK_USERNAME="${VORTEX_NOTIFY_SLACK_USERNAME}" VORTEX_NOTIFY_SLACK_ICON_EMOJI="${VORTEX_NOTIFY_SLACK_ICON_EMOJI}" color="${color}" fallback_message="${fallback_message}" title="${title}" VORTEX_NOTIFY_SLACK_LABEL="${VORTEX_NOTIFY_SLACK_LABEL}" VORTEX_NOTIFY_SLACK_ENVIRONMENT_URL="${VORTEX_NOTIFY_SLACK_ENVIRONMENT_URL}" VORTEX_NOTIFY_SLACK_LOGIN_URL="${VORTEX_NOTIFY_SLACK_LOGIN_URL}" timestamp="${timestamp}" VORTEX_NOTIFY_SLACK_CHANNEL="${VORTEX_NOTIFY_SLACK_CHANNEL}" VORTEX_NOTIFY_SLACK_EVENT="${VORTEX_NOTIFY_SLACK_EVENT}" LOG_FILE="${log_file}" php -r ' $username = getenv("VORTEX_NOTIFY_SLACK_USERNAME"); $icon = getenv("VORTEX_NOTIFY_SLACK_ICON_EMOJI"); @@ -148,8 +142,8 @@ $fields = [ ["title" => "Time", "value" => $timestamp, "short" => true] ]; -// Only include Environment and Login links for post-deployment notifications. -// Pre-deployment notifications should not show these as the site is not yet available. +// The site is not yet available on pre_deployment, so the Environment and +// Login links are omitted. if ($event !== "pre_deployment") { $fields[] = ["title" => "Environment", "value" => "<" . $env_url . "|View Site>", "short" => true]; $fields[] = ["title" => "Login", "value" => "<" . $login_url . "|Login Here>", "short" => true]; @@ -181,7 +175,7 @@ if (!empty($channel)) { echo json_encode($data, JSON_UNESCAPED_SLASHES); ') -# Extract webhook domain for display (hide secret path). +# The webhook path is a secret, so only the domain is displayed. webhook_domain=$(echo "${VORTEX_NOTIFY_SLACK_WEBHOOK}" | sed -E 's|(https?://[^/]+).*|\1|') info "Slack notification summary:" @@ -194,7 +188,6 @@ note "Channel : ${VORTEX_NOTIFY_SLACK_CHANNEL:-}" note "Username : ${VORTEX_NOTIFY_SLACK_USERNAME}" note "Event : ${event_label}" -# Send notification to Slack. response=$(curl -s -o /dev/null -w "%{http_code}" \ -X POST \ -H "Content-Type: application/json" \ diff --git a/.vortex/tooling/src/vortex-notify-webhook b/.vortex/tooling/src/vortex-notify-webhook index 77c28d087..f3ea68df1 100755 --- a/.vortex/tooling/src/vortex-notify-webhook +++ b/.vortex/tooling/src/vortex-notify-webhook @@ -84,22 +84,20 @@ fi info "Started webhook notification." -# Skip if this is a pre-deployment event (webhook only for post-deployment). if [ "${VORTEX_NOTIFY_WEBHOOK_EVENT}" = "pre_deployment" ]; then pass "Skipped webhook notification for pre_deployment event." exit 0 fi -# Set default payload template if not provided. if [ -z "${VORTEX_NOTIFY_WEBHOOK_PAYLOAD}" ]; then VORTEX_NOTIFY_WEBHOOK_PAYLOAD='{"channel": "Channel 1", "message": "%message%", "project": "%project%", "label": "%label%", "timestamp": "%timestamp%", "environment_url": "%environment_url%", "login_url": "%login_url%"}' fi -# Build and replace tokens (%variable_name%) for webhook payload. timestamp=$(date '+%d/%m/%Y %H:%M:%S %Z') message='## This is an automated message ##\nSite %project% %label% has been deployed at %timestamp% and is available at %environment_url%.\nLogin at: %login_url%\n\n%deployment_log%' -# JSON-escape each replacement value before substituting into JSON template. +# Each replacement value is JSON-escaped before substitution into the JSON +# template. # shellcheck disable=SC2016 VORTEX_NOTIFY_WEBHOOK_PAYLOAD=$(REPLACEMENT="${message}" TEMPLATE="${VORTEX_NOTIFY_WEBHOOK_PAYLOAD}" php -r '$escaped = json_encode(getenv("REPLACEMENT")); $escaped = substr($escaped, 1, -1); echo str_replace("%message%", $escaped, getenv("TEMPLATE"));') VORTEX_NOTIFY_WEBHOOK_PAYLOAD=$(REPLACEMENT="${timestamp}" TEMPLATE="${VORTEX_NOTIFY_WEBHOOK_PAYLOAD}" php -r '$escaped = json_encode(getenv("REPLACEMENT")); $escaped = substr($escaped, 1, -1); echo str_replace("%timestamp%", $escaped, getenv("TEMPLATE"));') @@ -114,7 +112,7 @@ log_file="" [ "${VORTEX_NOTIFY_WEBHOOK_LOG}" = "1" ] && log_file="${VORTEX_NOTIFY_WEBHOOK_LOG_FILE}" VORTEX_NOTIFY_WEBHOOK_PAYLOAD=$(LOG_FILE="${log_file}" TEMPLATE="${VORTEX_NOTIFY_WEBHOOK_PAYLOAD}" php -r '$file = getenv("LOG_FILE"); $log = ($file !== "" && is_file($file)) ? rtrim((string) file_get_contents($file), "\r\n") : ""; $escaped = json_encode($log, JSON_INVALID_UTF8_SUBSTITUTE); $escaped = substr($escaped, 1, -1); echo str_replace("%deployment_log%", $escaped, getenv("TEMPLATE"));') -# Sanitize webhook URL (extract domain, hide path that may contain secrets). +# The webhook path may contain secrets, so only the domain is displayed. webhook_domain=$(echo "${VORTEX_NOTIFY_WEBHOOK_URL}" | sed -E 's|(https?://[^/]+).*|\1|') info "Webhook notification summary:" @@ -128,14 +126,12 @@ note "Headers : ${VORTEX_NOTIFY_WEBHOOK_HEADERS}" note "Expected Status : ${VORTEX_NOTIFY_WEBHOOK_RESPONSE_STATUS}" note "Payload (first 200): ${VORTEX_NOTIFY_WEBHOOK_PAYLOAD:0:200}..." -# Build headers. headers=() IFS=\| read -ra webhook_headers <<<"${VORTEX_NOTIFY_WEBHOOK_HEADERS}" for item in "${webhook_headers[@]}"; do headers+=('-H' "${item}") done -# Make curl request. if ! curl -L -s -o /dev/null -w '%{http_code}' \ -X "${VORTEX_NOTIFY_WEBHOOK_METHOD}" \ "${headers[@]}" \ diff --git a/.vortex/tooling/src/vortex-provision b/.vortex/tooling/src/vortex-provision index d04ce324d..aaa0ad7a9 100755 --- a/.vortex/tooling/src/vortex-provision +++ b/.vortex/tooling/src/vortex-provision @@ -20,11 +20,12 @@ VORTEX_PROVISION_LOG="${VORTEX_PROVISION_LOG:-0}" # own '.log' here and 'vortex-notify' collects them all. VORTEX_NOTIFY_LOG_DIR="${VORTEX_NOTIFY_LOG_DIR:-/tmp/vortex-logs}" -# When enabled, re-run this script once through 'tee' to capture this run's output -# into the shared log directory (truncated fresh each run) while still streaming to -# the console. PIPESTATUS returns the provision (not tee) exit code and 'errexit' is -# disabled around the pipeline, so a logging failure never masks a deployment failure. -# Collected logs may contain secrets, so the directory and log file are owner-only. +# The script re-runs itself once through 'tee' to capture this run's output +# into the shared log directory (truncated fresh each run) while still +# streaming to the console. PIPESTATUS returns the provision (not tee) exit +# code and 'errexit' is disabled around the pipeline, so a logging failure +# never masks a deployment failure. Collected logs may contain secrets, so the +# directory and log file are owner-only. if [ "${VORTEX_PROVISION_LOG}" = "1" ] && [ -z "${VORTEX_PROVISION_LOG_ACTIVE:-}" ]; then export VORTEX_PROVISION_LOG_ACTIVE=1 set +e @@ -129,7 +130,6 @@ if [ "${VORTEX_PROVISION_SKIP}" = "1" ]; then exit 0 fi -# Convert DB dir starting with './' to a full path. [ "${VORTEX_PROVISION_DB_DIR#./}" != "${VORTEX_PROVISION_DB_DIR}" ] && VORTEX_PROVISION_DB_DIR="$(pwd)${VORTEX_PROVISION_DB_DIR#.}" if [ -z "${VORTEX_PROVISION_DB}" ]; then @@ -140,18 +140,13 @@ drush_version="$(drush --version | cut -d' ' -f4)" drupal_version="$(drush status --field=drupal-version 2>/dev/null || echo "Unknown")" site_is_installed="$(drush status --fields=bootstrap 2>/dev/null | grep -q "Successful" && echo "1" || echo "0")" -# Discover the configuration directory path from the Drupal settings. config_path="$(drush php:eval 'print realpath(\Drupal\Core\Site\Settings::get("config_sync_directory"));')" [ -z "${config_path}" ] && fail "Config directory was not found in the Drupal settings." [ ! -d "${config_path}" ] && fail "Config directory \"${config_path:-}\" does not exist." site_has_config_files="$(test "$(ls -1 ${config_path}/*.yml 2>/dev/null | wc -l | tr -d ' ')" -gt 0 && echo "1" || echo "0")" -# Normalize the provision type. [ "${VORTEX_PROVISION_TYPE}" = "profile" ] || VORTEX_PROVISION_TYPE=database -################################################################################ -# Print provisioning information. -################################################################################ echo note "Drupal core version : ${drupal_version}" note "Drush version : ${drush_version}" @@ -178,15 +173,11 @@ note "Skip post-provision operations : $(yesno "${VORTEX_PROVISION_POST_OPERATIO note "Verify config after update : $(yesno "${VORTEX_PROVISION_VERIFY_CONFIG_UNCHANGED_AFTER_UPDATE}")" note "Use maintenance mode : $(yesno "${VORTEX_PROVISION_USE_MAINTENANCE_MODE}")" echo -################################################################################ if [ "${VORTEX_PROVISION_VERIFY_CONFIG_UNCHANGED_AFTER_UPDATE}" = "1" ]; then for cmd in diff mktemp; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done fi -# -# Provision site by importing the database from the dump file. -# provision_from_db() { if [ ! -f "${VORTEX_PROVISION_DB}" ]; then if [ "${VORTEX_PROVISION_FALLBACK_TO_PROFILE}" = "1" ]; then @@ -205,9 +196,6 @@ provision_from_db() { "$(dirname "${BASH_SOURCE[0]}")/vortex-import-db-file" "${VORTEX_PROVISION_DB}" } -# -# Provision site from the profile. -# provision_from_profile() { local is_fallback="${1:-0}" local has_config="${2:-0}" @@ -231,8 +219,8 @@ provision_from_profile() { drush site:install "${opts[@]}" - # On fallback, enable Shield to protect the site and skip post-provision - # operations since the site was installed from profile without configuration. + # A fallback install has no real content or configuration, so Shield + # protects the site and post-provision operations are skipped. if [ "${is_fallback}" = "1" ]; then drush pm:install shield export VORTEX_PROVISION_POST_OPERATIONS_SKIP=1 @@ -241,11 +229,6 @@ provision_from_profile() { pass "Installed a site from the profile." } -# Provision site from DB dump or profile. -# -# The code block below has explicit if-else conditions and verbose output to -# ensure that this significant operation is executed correctly and has -# sufficient output for debugging. if [ "${VORTEX_PROVISION_TYPE}" = "database" ]; then info "Provisioning site from the database dump file." note "Dump file path: ${VORTEX_PROVISION_DB}" @@ -256,8 +239,8 @@ if [ "${VORTEX_PROVISION_TYPE}" = "database" ]; then if [ -n "${VORTEX_PROVISION_DB_IMAGE-}" ]; then note "Database is baked into the container image." note "Site content will be preserved." - # Container image restarts with a fresh database. Let the downstream - # scripts know that the database is fresh. + # The container image restarts with a fresh database. The exported flag + # tells downstream scripts that the database is fresh. export VORTEX_PROVISION_OVERRIDE_DB=1 elif [ "${VORTEX_PROVISION_OVERRIDE_DB}" = "1" ]; then note "Existing site content will be removed and fresh content will be imported from the database dump file." @@ -283,7 +266,7 @@ if [ "${VORTEX_PROVISION_TYPE}" = "database" ]; then else note "Fresh site content will be imported from the database dump file." provision_from_db - # Let the downstream scripts know that the database is fresh. + # The exported flag tells downstream scripts that the database is fresh. export VORTEX_PROVISION_OVERRIDE_DB=1 fi fi @@ -297,7 +280,7 @@ else if [ "${VORTEX_PROVISION_OVERRIDE_DB}" = "1" ]; then note "Existing site content will be removed and new content will be created from the profile." provision_from_profile 0 "${site_has_config_files}" - # Let the downstream scripts know that the database is fresh. + # The exported flag tells downstream scripts that the database is fresh. export VORTEX_PROVISION_OVERRIDE_DB=1 else note "Site content will be preserved." @@ -333,7 +316,6 @@ if [ "${VORTEX_PROVISION_USE_MAINTENANCE_MODE}" = "1" ]; then echo fi -# Set site UUID from configuration if config files are present. if [ "${site_has_config_files}" = "1" ]; then if [ -f "${config_path}/system.site.yml" ]; then config_uuid="$(awk '/^uuid:/ {print $2; exit}' "${config_path}/system.site.yml")" @@ -390,7 +372,6 @@ else echo fi -# Import configuration if config files are present. if [ "${site_has_config_files}" = "1" ]; then task "Importing configuration." drush config:import @@ -404,7 +385,6 @@ if [ "${site_has_config_files}" = "1" ]; then echo fi - # Import config_split configuration if the module is installed. # Drush deploy does not import config_split configuration on the first run. # @see https://github.com/drush-ops/drush/issues/2449 # @see https://www.drupal.org/project/drupal/issues/3241439 @@ -426,7 +406,6 @@ drush deploy:hook pass "Completed deployment hooks." echo -# Sanitize database. if [ "${VORTEX_PROVISION_SANITIZE_DB_SKIP}" != "1" ]; then "$(dirname "${BASH_SOURCE[0]}")/vortex-provision-sanitize-db" else @@ -434,9 +413,6 @@ else echo fi -# Run custom provision scripts. -# The files should be located in VORTEX_PROVISION_SCRIPTS_DIR directory, -# must have "provision-" prefix and ".sh" extension. if [ -d "${VORTEX_PROVISION_SCRIPTS_DIR}" ]; then for file in "${VORTEX_PROVISION_SCRIPTS_DIR}"/provision-*.sh; do if [ -f "${file}" ]; then diff --git a/.vortex/tooling/src/vortex-provision-sanitize-db b/.vortex/tooling/src/vortex-provision-sanitize-db index dd20784cf..c6e8ef4ea 100755 --- a/.vortex/tooling/src/vortex-provision-sanitize-db +++ b/.vortex/tooling/src/vortex-provision-sanitize-db @@ -38,7 +38,6 @@ info "Sanitizing database." drush() { ./vendor/bin/drush -y "$@"; } -# Always sanitize password and email using standard methods. drush sql:sanitize --sanitize-password="${VORTEX_PROVISION_SANITIZE_DB_PASSWORD}" --sanitize-email="${VORTEX_PROVISION_SANITIZE_DB_EMAIL}" pass "Sanitized database using drush sql:sanitize." @@ -47,7 +46,6 @@ if [ "${VORTEX_PROVISION_SANITIZE_DB_REPLACE_USERNAME_WITH_EMAIL:-}" = "1" ]; th pass "Updated username with user email." fi -# Sanitize using additional SQL commands provided in file. if [ -f "${VORTEX_PROVISION_SANITIZE_DB_ADDITIONAL_FILE:-}" ]; then # The file path is relative to the project root, but drush expects it to be # relative to the Drupal root. @@ -55,12 +53,13 @@ if [ -f "${VORTEX_PROVISION_SANITIZE_DB_ADDITIONAL_FILE:-}" ]; then pass "Applied custom sanitization commands from file." fi -# User mail and name for use 0 could have been sanitized - resetting it. +# The user 0 mail and name could have been sanitized, so they are reset. drush sql:query "UPDATE \`users_field_data\` SET mail = '', name = '' WHERE uid = '0';" drush sql:query "UPDATE \`users_field_data\` SET name = '' WHERE uid = '0';" pass "Reset user 0 username and email." -# User email could have been sanitized - setting it back to a pre-defined email. +# The user 1 email could have been sanitized, so it is set back to the +# pre-defined email. if [ -n "${DRUPAL_ADMIN_EMAIL:-}" ]; then drush sql:query "UPDATE \`users_field_data\` SET mail = '${DRUPAL_ADMIN_EMAIL:-}' WHERE uid = '1';" pass "Updated user 1 email." diff --git a/.vortex/tooling/src/vortex-push-container-registry b/.vortex/tooling/src/vortex-push-container-registry index 0275423a0..42f8a091f 100755 --- a/.vortex/tooling/src/vortex-push-container-registry +++ b/.vortex/tooling/src/vortex-push-container-registry @@ -13,8 +13,8 @@ t=$(mktemp) && export -p >"${t}" && set -a && . ./.env && if [ -f ./.env.local ] set -eu -# Xtrace (VORTEX_DEBUG=1) is intentionally enabled only after the registry -# login below, so the registry password is never written to the trace output. +# Xtrace (VORTEX_DEBUG=1) is enabled only after the registry login below, so +# the registry password is never written to the trace output. # Comma-separated map of container services and images to push in # format "service1=org/image1,service2=org/image2". @@ -49,8 +49,8 @@ for cmd in docker; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is info "Started container registry push." -# Only push if the map was provided, but do not fail if it has not as this -# may be called as a part of another task. +# An empty map skips the push without failing: the script may run as a part +# of another task. # @todo: Handle this better - empty $VORTEX_PUSH_CONTAINER_REGISTRY_MAP should use defaults. if [ -z "${VORTEX_PUSH_CONTAINER_REGISTRY_MAP}" ]; then note "Services map is not specified in VORTEX_PUSH_CONTAINER_REGISTRY_MAP variable. Container registry push will not continue." @@ -63,7 +63,6 @@ fi services=() images=() -# Parse and validate map. IFS=',' read -r -a values <<<"${VORTEX_PUSH_CONTAINER_REGISTRY_MAP}" for value in "${values[@]}"; do IFS='=' read -r -a parts <<<"${value}" @@ -86,7 +85,6 @@ for key in "${!services[@]}"; do image="${images[${key}]}" task "Processing service ${service}." - # Check if the service is running. cid=$(docker compose ps -q "${service}") [ -z "${cid}" ] && fail "Service \"${service}\" is not running." diff --git a/.vortex/tooling/src/vortex-push-db-s3 b/.vortex/tooling/src/vortex-push-db-s3 index 2e31201ac..3bead90ef 100755 --- a/.vortex/tooling/src/vortex-push-db-s3 +++ b/.vortex/tooling/src/vortex-push-db-s3 @@ -62,7 +62,6 @@ local_file="${VORTEX_PUSH_DB_S3_DB_DIR}/${VORTEX_PUSH_DB_S3_DB_FILE}" info "Started database dump push to S3." -# Ensure prefix ends with a trailing slash if non-empty. [ -n "${VORTEX_PUSH_DB_S3_PREFIX}" ] && VORTEX_PUSH_DB_S3_PREFIX="${VORTEX_PUSH_DB_S3_PREFIX%/}/" request_type="PUT" diff --git a/.vortex/tooling/src/vortex-setup-ssh b/.vortex/tooling/src/vortex-setup-ssh index 60c5b43a8..d5b991ce9 100755 --- a/.vortex/tooling/src/vortex-setup-ssh +++ b/.vortex/tooling/src/vortex-setup-ssh @@ -89,7 +89,6 @@ if [ -n "${fingerprint-}" ]; then done fi - # Cleanup the fingerprint and create a file name. file="${fingerprint//:/}" file="${HOME}/.ssh/id_rsa_${file//\"/}" fi @@ -131,9 +130,9 @@ if [ -n "${VORTEX_SSH_KNOWN_HOSTS-}" ]; then task "Pinning SSH host keys to known_hosts." mkdir -p "${HOME}/.ssh/" - # Strip insecure directives that an earlier run with strict checking disabled - # may have left in the SSH config, so the pinned host keys are actually - # enforced rather than bypassed by a stale "UserKnownHostsFile /dev/null". + # An earlier run with strict checking disabled may have left insecure + # directives in the SSH config; strip them so the pinned host keys are + # enforced. if [ -f "${HOME}/.ssh/config" ]; then grep -v -E '^[[:space:]]*(StrictHostKeyChecking[[:space:]]+no|UserKnownHostsFile[[:space:]]+/dev/null)[[:space:]]*$' "${HOME}/.ssh/config" >"${HOME}/.ssh/config.tmp" || true mv "${HOME}/.ssh/config.tmp" "${HOME}/.ssh/config" diff --git a/.vortex/tooling/src/vortex-task-copy-db-acquia b/.vortex/tooling/src/vortex-task-copy-db-acquia index 4c6102de7..47af6c8ce 100755 --- a/.vortex/tooling/src/vortex-task-copy-db-acquia +++ b/.vortex/tooling/src/vortex-task-copy-db-acquia @@ -55,26 +55,18 @@ fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\03 info "Started database copying between environments in Acquia." -# -# Extract last value from JSON object passed via STDIN. -# extract_json_last_value() { local key=${1} php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); \$last=array_pop(\$data); isset(\$last[\"${key}\"]) ? print trim(json_encode(\$last[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Extract keyed value from JSON object passed via STDIN. -# extract_json_value() { local key=${1} php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); isset(\$data[\"${key}\"]) ? print trim(json_encode(\$data[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# Pre-flight checks. for cmd in curl php; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done -# Check that all required variables are present. [ -z "${VORTEX_TASK_COPY_DB_ACQUIA_KEY}" ] && fail "Missing required value for VORTEX_TASK_COPY_DB_ACQUIA_KEY or VORTEX_ACQUIA_KEY." [ -z "${VORTEX_TASK_COPY_DB_ACQUIA_SECRET}" ] && fail "Missing required value for VORTEX_TASK_COPY_DB_ACQUIA_SECRET or VORTEX_ACQUIA_SECRET." [ -z "${VORTEX_TASK_COPY_DB_ACQUIA_APP_NAME}" ] && fail "Missing required value for VORTEX_TASK_COPY_DB_ACQUIA_APP_NAME or VORTEX_ACQUIA_APP_NAME." diff --git a/.vortex/tooling/src/vortex-task-copy-files-acquia b/.vortex/tooling/src/vortex-task-copy-files-acquia index 2ca0684d8..21cd64fb9 100755 --- a/.vortex/tooling/src/vortex-task-copy-files-acquia +++ b/.vortex/tooling/src/vortex-task-copy-files-acquia @@ -50,28 +50,20 @@ pass() { _d=""; [ -n "${_TASK_START:-}" ] && _d=" ($(($(date +%s) - _TASK_START) fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; exit "${2:-1}"; } # @formatter:on -# Pre-flight checks. for cmd in php curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done info "Started files copying between environments in Acquia." -# -# Extract last value from JSON object passed via STDIN. -# extract_json_last_value() { local key="${1}" php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); \$last=array_pop(\$data); isset(\$last[\"${key}\"]) ? print trim(json_encode(\$last[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Extract keyed value from JSON object passed via STDIN. -# extract_json_value() { local key="${1}" php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); isset(\$data[\"${key}\"]) ? print trim(json_encode(\$data[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# Check that all required variables are present. [ -z "${VORTEX_TASK_COPY_FILES_ACQUIA_KEY}" ] && fail "Missing required value for VORTEX_TASK_COPY_FILES_ACQUIA_KEY or VORTEX_ACQUIA_KEY." [ -z "${VORTEX_TASK_COPY_FILES_ACQUIA_SECRET}" ] && fail "Missing required value for VORTEX_TASK_COPY_FILES_ACQUIA_SECRET or VORTEX_ACQUIA_SECRET." [ -z "${VORTEX_TASK_COPY_FILES_ACQUIA_APP_NAME}" ] && fail "Missing required value for VORTEX_TASK_COPY_FILES_ACQUIA_APP_NAME or VORTEX_ACQUIA_APP_NAME." diff --git a/.vortex/tooling/src/vortex-task-custom-lagoon b/.vortex/tooling/src/vortex-task-custom-lagoon index fcf2c8363..a780662e8 100755 --- a/.vortex/tooling/src/vortex-task-custom-lagoon +++ b/.vortex/tooling/src/vortex-task-custom-lagoon @@ -62,7 +62,6 @@ fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\03 info "Started Lagoon task ${VORTEX_TASK_CUSTOM_LAGOON_NAME}." -## Check all required values. [ -z "${VORTEX_TASK_CUSTOM_LAGOON_NAME}" ] && fail "Missing required value for VORTEX_TASK_CUSTOM_LAGOON_NAME." [ -z "${VORTEX_TASK_CUSTOM_LAGOON_BRANCH}" ] && fail "Missing required value for VORTEX_TASK_CUSTOM_LAGOON_BRANCH." [ -z "${VORTEX_TASK_CUSTOM_LAGOON_COMMAND}" ] && fail "Missing required value for VORTEX_TASK_CUSTOM_LAGOON_COMMAND." diff --git a/.vortex/tooling/src/vortex-task-purge-cache-acquia b/.vortex/tooling/src/vortex-task-purge-cache-acquia index 432c1149c..59b659014 100755 --- a/.vortex/tooling/src/vortex-task-purge-cache-acquia +++ b/.vortex/tooling/src/vortex-task-purge-cache-acquia @@ -50,28 +50,20 @@ pass() { _d=""; [ -n "${_TASK_START:-}" ] && _d=" ($(($(date +%s) - _TASK_START) fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; exit "${2:-1}"; } # @formatter:on -# Pre-flight checks. for cmd in php curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done info "Started cache purging in Acquia." -# -# Extract last value from JSON object passed via STDIN. -# extract_json_last_value() { local key="${1}" php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); \$last=array_pop(\$data); isset(\$last[\"${key}\"]) ? print trim(json_encode(\$last[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# -# Extract keyed value from JSON object passed via STDIN. -# extract_json_value() { local key="${1}" php -r "\$data=json_decode(file_get_contents('php://stdin'), TRUE); isset(\$data[\"${key}\"]) ? print trim(json_encode(\$data[\"${key}\"], JSON_UNESCAPED_SLASHES), '\"') : exit(1);" } -# Check that all required variables are present. [ -z "${VORTEX_TASK_PURGE_CACHE_ACQUIA_KEY}" ] && fail "Missing required value for VORTEX_TASK_PURGE_CACHE_ACQUIA_KEY or VORTEX_ACQUIA_KEY." [ -z "${VORTEX_TASK_PURGE_CACHE_ACQUIA_SECRET}" ] && fail "Missing required value for VORTEX_TASK_PURGE_CACHE_ACQUIA_SECRET or VORTEX_ACQUIA_SECRET." [ -z "${VORTEX_TASK_PURGE_CACHE_ACQUIA_APP_NAME}" ] && fail "Missing required value for VORTEX_TASK_PURGE_CACHE_ACQUIA_APP_NAME or VORTEX_ACQUIA_APP_NAME." @@ -103,7 +95,8 @@ task "Compiling a list of domains." target_env="${VORTEX_TASK_PURGE_CACHE_ACQUIA_ENV}" domain_list=() while read -r domain; do - # Special variable to remap target env to the sub-domain prefix based on UI name. + # TARGET_ENV_REMAP maps the target env to the sub-domain prefix based on + # the UI name. TARGET_ENV_REMAP="${target_env}" # Strip placeholder for PROD environment. if [ "${target_env}" = "prod" ]; then @@ -121,11 +114,9 @@ while read -r domain; do TARGET_ENV_REMAP="" fi - # Proceed only if the environment was provided. if [ -n "${TARGET_ENV_REMAP}" ]; then # Interpolate variables in domain name. domain="$(eval echo "${domain}")" - # Add domain to list. domain_list+=("${domain}") fi done <"${VORTEX_TASK_PURGE_CACHE_ACQUIA_DOMAINS_FILE}" @@ -133,16 +124,15 @@ done <"${VORTEX_TASK_PURGE_CACHE_ACQUIA_DOMAINS_FILE}" pass "Compiled a list of ${#domain_list[@]} domains." if [ "${#domain_list[@]}" -gt 0 ]; then - # Acquia API stops clearing purging caches if at least 1 domain fails, so - # we are clearing caches for every domain separately and not failing if - # the domain is not found. + # The Acquia API aborts a batched purge when any domain fails, so caches + # are cleared per domain and a missing domain is not a failure. for domain in "${domain_list[@]}"; do task "Purging cache for ${VORTEX_TASK_PURGE_CACHE_ACQUIA_ENV} environment domain ${domain}." task_status_json=$(curl -X POST -s -L -H 'Accept: application/json, version=2' -H "Authorization: Bearer ${token}" -H "Content-Type: application/json" -d "{\"domains\":[\"${domain}\"]}" "https://cloud.acquia.com/api/environments/${ENV_ID}/domains/actions/clear-varnish") notification_url=$(echo "${task_status_json}" | extract_json_value "_links" | extract_json_value "notification" | extract_json_value "href") || true - # If domain does not exist - notification will be empty; we are skipping - # non-existing domains without a failure. + # A missing domain yields an empty notification URL and is skipped + # without a failure. if [ -z "${notification_url}" ]; then note "Warning: Unable to purge cache for ${VORTEX_TASK_PURGE_CACHE_ACQUIA_ENV} environment domain ${domain} as it does not exist." pass "Completed cache purging for ${VORTEX_TASK_PURGE_CACHE_ACQUIA_ENV} environment domain ${domain}." diff --git a/.vortex/tooling/src/vortex-update b/.vortex/tooling/src/vortex-update index dfc2569b0..a3485279b 100755 --- a/.vortex/tooling/src/vortex-update +++ b/.vortex/tooling/src/vortex-update @@ -55,7 +55,6 @@ pass() { _d=""; [ -n "${_TASK_START:-}" ] && _d=" ($(($(date +%s) - _TASK_START) fail() { [ "${TERM:-}" != "dumb" ] && tput colors >/dev/null 2>&1 && printf "\033[31m[FAIL] %s\033[0m\n" "${1}" || printf "[FAIL] %s\n" "${1}"; exit "${2:-1}"; } # @formatter:on -# Pre-flight checks. for cmd in php curl; do command -v "${cmd}" >/dev/null || fail "Command ${cmd} is not available."; done for arg in "$@"; do diff --git a/.vortex/tooling/tests/_helper.bash b/.vortex/tooling/tests/_helper.bash index 3a33b7952..69c20ea7f 100644 --- a/.vortex/tooling/tests/_helper.bash +++ b/.vortex/tooling/tests/_helper.bash @@ -405,3 +405,12 @@ ${global_bin} "\$@" EOL chmod +x "${path_with_bin}" } + +# Replaces a sibling tooling script with a stub that prints a marker. The script +# dispatches to siblings by explicit path, so a PATH-based mock cannot intercept +# them - the file itself must be replaced. +stub_sibling() { + mkdir -p .vortex/tooling/src + printf '#!/usr/bin/env bash\necho "%s"\n' "${2}" >".vortex/tooling/src/${1}" + chmod +x ".vortex/tooling/src/${1}" +} diff --git a/.vortex/tooling/tests/unit/export-db.bats b/.vortex/tooling/tests/unit/export-db.bats index 0a817cfee..a6176ac67 100644 --- a/.vortex/tooling/tests/unit/export-db.bats +++ b/.vortex/tooling/tests/unit/export-db.bats @@ -6,15 +6,6 @@ load ../_helper.bash -# Replaces a sibling tooling script with a stub that prints a marker. The router -# dispatches to siblings by explicit path, so a PATH-based mock cannot intercept -# them - the file itself must be replaced. -stub_sibling() { - mkdir -p .vortex/tooling/src - printf '#!/usr/bin/env bash\necho "%s"\n' "${2}" >".vortex/tooling/src/${1}" - chmod +x ".vortex/tooling/src/${1}" -} - @test "export-db: Exports as a file in place when not on the host" { pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 diff --git a/.vortex/tooling/tests/unit/fetch-db-acquia.bats b/.vortex/tooling/tests/unit/fetch-db-acquia.bats index 3314811ac..c8b546062 100644 --- a/.vortex/tooling/tests/unit/fetch-db-acquia.bats +++ b/.vortex/tooling/tests/unit/fetch-db-acquia.bats @@ -75,7 +75,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -137,7 +137,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -200,7 +200,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -264,7 +264,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -294,7 +294,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -328,7 +328,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -366,7 +366,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -408,7 +408,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -450,7 +450,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -528,7 +528,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success assert_file_exists ".data/db.sql" @@ -575,7 +575,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -619,7 +619,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -668,7 +668,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -726,7 +726,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -778,7 +778,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -830,7 +830,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -882,7 +882,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -940,7 +940,7 @@ bats_require_minimum_version 1.5.0 mocks="$(steps_run "setup")" run --separate-stderr .vortex/tooling/src/vortex-fetch-db-acquia - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success diff --git a/.vortex/tooling/tests/unit/fetch-db-lagoon.bats b/.vortex/tooling/tests/unit/fetch-db-lagoon.bats index 3db41ca79..bea829b2b 100644 --- a/.vortex/tooling/tests/unit/fetch-db-lagoon.bats +++ b/.vortex/tooling/tests/unit/fetch-db-lagoon.bats @@ -47,7 +47,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-lagoon - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -99,7 +99,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-lagoon - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -152,7 +152,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-lagoon - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success diff --git a/.vortex/tooling/tests/unit/fetch-db-s3.bats b/.vortex/tooling/tests/unit/fetch-db-s3.bats index 0b5a94ee8..76499dbd0 100644 --- a/.vortex/tooling/tests/unit/fetch-db-s3.bats +++ b/.vortex/tooling/tests/unit/fetch-db-s3.bats @@ -37,7 +37,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -80,7 +80,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -123,7 +123,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -162,7 +162,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -202,7 +202,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -229,7 +229,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -254,7 +254,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -279,7 +279,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -304,7 +304,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-fetch-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure diff --git a/.vortex/tooling/tests/unit/helpers.bats b/.vortex/tooling/tests/unit/helpers.bats index aec0c3188..7a8421f8f 100644 --- a/.vortex/tooling/tests/unit/helpers.bats +++ b/.vortex/tooling/tests/unit/helpers.bats @@ -9,42 +9,42 @@ load ../_helper.bash @test "helper_vortex" { echo " > Bats version: ${BATS_VERSION}" >&3 - [ "${BATS_TMPDIR}" != "" ] + assert_not_empty "${BATS_TMPDIR}" echo " > BATS temp dir: ${BATS_TMPDIR}" >&3 - [ "${BATS_RUN_TMPDIR}" != "" ] + assert_not_empty "${BATS_RUN_TMPDIR}" echo " > BATS run temp dir: ${BATS_RUN_TMPDIR}" >&3 - [ "${BATS_FILE_TMPDIR}" != "" ] + assert_not_empty "${BATS_FILE_TMPDIR}" echo " > BATS file temp dir: ${BATS_FILE_TMPDIR}" >&3 - [ "${BATS_TEST_TMPDIR}" != "" ] + assert_not_empty "${BATS_TEST_TMPDIR}" echo " > BATS test temp dir: ${BATS_TEST_TMPDIR}" >&3 - [ "${BATS_SUITE_TMPDIR}" != "" ] + assert_not_empty "${BATS_SUITE_TMPDIR}" echo " > BATS suit temp dir: ${BATS_SUITE_TMPDIR}" >&3 - [ "${ROOT_DIR}" != "" ] + assert_not_empty "${ROOT_DIR}" echo " > Current dir: ${ROOT_DIR}" >&3 assert_string_not_contains "${ROOT_DIR}" "//" - [ "${BUILD_DIR}" != "" ] + assert_not_empty "${BUILD_DIR}" echo " > Build dir: ${BUILD_DIR}" >&3 assert_string_not_contains "${BUILD_DIR}" "//" - [ "${CURRENT_PROJECT_DIR}" != "" ] + assert_not_empty "${CURRENT_PROJECT_DIR}" echo " > Project dir: ${CURRENT_PROJECT_DIR}" >&3 assert_string_not_contains "${CURRENT_PROJECT_DIR}" "//" - [ "${DST_PROJECT_DIR}" != "" ] + assert_not_empty "${DST_PROJECT_DIR}" echo " > DST dir: ${DST_PROJECT_DIR}" >&3 assert_string_not_contains "${DST_PROJECT_DIR}" "//" - [ "${LOCAL_REPO_DIR}" != "" ] + assert_not_empty "${LOCAL_REPO_DIR}" echo " > Local repo dir: ${LOCAL_REPO_DIR}" >&3 assert_string_not_contains "${LOCAL_REPO_DIR}" "//" - [ "${APP_TMP_DIR}" != "" ] + assert_not_empty "${APP_TMP_DIR}" echo " > App temp dir: ${APP_TMP_DIR}" >&3 assert_string_not_contains "${APP_TMP_DIR}" "//" } diff --git a/.vortex/tooling/tests/unit/import-db.bats b/.vortex/tooling/tests/unit/import-db.bats index 0a1b7f982..47cf6350b 100644 --- a/.vortex/tooling/tests/unit/import-db.bats +++ b/.vortex/tooling/tests/unit/import-db.bats @@ -6,15 +6,6 @@ load ../_helper.bash -# Replaces a sibling tooling script with a stub that prints a marker. The router -# dispatches to siblings by explicit path, so a PATH-based mock cannot intercept -# them - the file itself must be replaced. -stub_sibling() { - mkdir -p .vortex/tooling/src - printf '#!/usr/bin/env bash\necho "%s"\n' "${2}" >".vortex/tooling/src/${1}" - chmod +x ".vortex/tooling/src/${1}" -} - @test "import-db: Imports a file in place when not on the host" { pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 diff --git a/.vortex/tooling/tests/unit/login-container-registry.bats b/.vortex/tooling/tests/unit/login-container-registry.bats index 931c7ba7a..5f5b7a8d4 100644 --- a/.vortex/tooling/tests/unit/login-container-registry.bats +++ b/.vortex/tooling/tests/unit/login-container-registry.bats @@ -79,7 +79,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-login-container-registry assert_success - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" popd >/dev/null } diff --git a/.vortex/tooling/tests/unit/notify-diffy.bats b/.vortex/tooling/tests/unit/notify-diffy.bats index c1e81184a..12c7d4a32 100644 --- a/.vortex/tooling/tests/unit/notify-diffy.bats +++ b/.vortex/tooling/tests/unit/notify-diffy.bats @@ -18,7 +18,7 @@ load ../_helper.bash export VORTEX_NOTIFY_DIFFY_TOKEN="token12345" export VORTEX_NOTIFY_DIFFY_REPOSITORY="myorg/myrepo" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started Diffy notification." @@ -42,7 +42,7 @@ load ../_helper.bash export VORTEX_NOTIFY_DIFFY_TOKEN="token12345" export VORTEX_NOTIFY_DIFFY_REPOSITORY="myorg/myrepo" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started Diffy notification." @@ -66,7 +66,7 @@ load ../_helper.bash export VORTEX_NOTIFY_DIFFY_REPOSITORY="myorg/myrepo" export VORTEX_NOTIFY_DIFFY_BRANCHES="develop,main" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains 'Skipped Diffy notification for branch "feature/random"' @@ -89,7 +89,7 @@ load ../_helper.bash export VORTEX_NOTIFY_DIFFY_TOKEN="badtoken" export VORTEX_NOTIFY_DIFFY_REPOSITORY="myorg/myrepo" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Unable to send GitHub repository_dispatch: HTTP 401" @@ -111,7 +111,7 @@ load ../_helper.bash unset VORTEX_NOTIFY_DIFFY_TOKEN export VORTEX_NOTIFY_DIFFY_REPOSITORY="myorg/myrepo" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Missing required value for VORTEX_NOTIFY_DIFFY_TOKEN" diff --git a/.vortex/tooling/tests/unit/notify-email.bats b/.vortex/tooling/tests/unit/notify-email.bats index 50a54d922..702b5f817 100644 --- a/.vortex/tooling/tests/unit/notify-email.bats +++ b/.vortex/tooling/tests/unit/notify-email.bats @@ -17,7 +17,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="develop" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -47,7 +47,7 @@ load ../_helper.bash export VORTEX_NOTIFY_PR_NUMBER="123" export VORTEX_NOTIFY_LABEL="PR-123" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -77,7 +77,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="develop" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -102,7 +102,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" export VORTEX_NOTIFY_EMAIL_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -122,7 +122,7 @@ load ../_helper.bash unset VORTEX_NOTIFY_BRANCH export VORTEX_NOTIFY_EMAIL_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify-email + run .vortex/tooling/src/vortex-notify-email assert_success assert_output_contains 'Skipped email notification for branch "".' @@ -147,11 +147,11 @@ load ../_helper.bash # Ensure test file doesn't exist before rm -f /tmp/injected_email_test - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Verify the injection file was NOT created (injection did not execute) - [ ! -f /tmp/injected_email_test ] + assert_file_not_exists "/tmp/injected_email_test" # Verify the malicious string is treated as literal text in the message assert_output_contains "test'); file_put_contents('/tmp/injected_email_test', 'HACKED'); //" @@ -177,7 +177,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Each collected log is a titled section, below the login URL. @@ -203,7 +203,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/nologs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Site testproject develop has been deployed" @@ -230,7 +230,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Site testproject develop has been deployed" @@ -260,7 +260,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # The log body keeps the %project% token and the command substitution as literal @@ -292,7 +292,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=0 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_not_contains "Provision line one" @@ -319,7 +319,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # A custom template's %deployment_log% token is substituted with the log. @@ -349,7 +349,7 @@ load ../_helper.bash export VORTEX_NOTIFY_LOG=1 export VORTEX_NOTIFY_EMAIL_LOG=0 - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_not_contains "Provision line one" diff --git a/.vortex/tooling/tests/unit/notify-github.bats b/.vortex/tooling/tests/unit/notify-github.bats index e823ff315..a2077baeb 100644 --- a/.vortex/tooling/tests/unit/notify-github.bats +++ b/.vortex/tooling/tests/unit/notify-github.bats @@ -30,7 +30,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="existingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success steps_run "assert" "${mocks[@]}" @@ -63,7 +63,7 @@ load ../_helper.bash export VORTEX_NOTIFY_PR_NUMBER="123" export VORTEX_NOTIFY_LABEL="PR-123" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success steps_run "assert" "${mocks[@]}" @@ -95,7 +95,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="existingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success steps_run "assert" "${mocks[@]}" @@ -125,7 +125,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="nonexistingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure steps_run "assert" "${mocks[@]}" @@ -158,7 +158,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="existingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success steps_run "assert" "${mocks[@]}" @@ -191,7 +191,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="existingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success steps_run "assert" "${mocks[@]}" @@ -222,7 +222,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="nonexistingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure steps_run "assert" "${mocks[@]}" @@ -243,7 +243,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" export VORTEX_NOTIFY_GITHUB_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -263,7 +263,7 @@ load ../_helper.bash unset VORTEX_NOTIFY_BRANCH export VORTEX_NOTIFY_GITHUB_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify-github + run .vortex/tooling/src/vortex-notify-github assert_success assert_output_contains 'Skipped GitHub notification for branch "".' @@ -286,7 +286,7 @@ load ../_helper.bash export VORTEX_NOTIFY_LABEL="existingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Missing required value for VORTEX_NOTIFY_GITHUB_TOKEN" @@ -323,7 +323,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="existingbranch" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure steps_run "assert" "${mocks[@]}" diff --git a/.vortex/tooling/tests/unit/notify-jira.bats b/.vortex/tooling/tests/unit/notify-jira.bats index d846fdd11..3a1571961 100644 --- a/.vortex/tooling/tests/unit/notify-jira.bats +++ b/.vortex/tooling/tests/unit/notify-jira.bats @@ -52,7 +52,7 @@ load ../_helper.bash export VORTEX_NOTIFY_LOGIN_URL="https://develop.testproject.com/user/login" export VORTEX_NOTIFY_JIRA_TRANSITION="QA" export VORTEX_NOTIFY_JIRA_ASSIGNEE_EMAIL="jane.doe@example.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success steps_run "assert" "${mocks[@]}" @@ -72,7 +72,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="feature/proj-1234-some-description" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -97,7 +97,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" export VORTEX_NOTIFY_JIRA_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -117,7 +117,7 @@ load ../_helper.bash unset VORTEX_NOTIFY_BRANCH export VORTEX_NOTIFY_JIRA_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify-jira + run .vortex/tooling/src/vortex-notify-jira assert_success assert_output_contains 'Skipped JIRA notification for branch "".' @@ -157,11 +157,11 @@ load ../_helper.bash # Ensure test file doesn't exist before rm -f /tmp/injected_jira_test - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Verify the injection file was NOT created (injection did not execute) - [ ! -f /tmp/injected_jira_test ] + assert_file_not_exists "/tmp/injected_jira_test" # Verify the malicious string is treated as literal text assert_output_contains "test'); file_put_contents('/tmp/injected_jira_test', 'HACKED'); //" @@ -193,7 +193,7 @@ load ../_helper.bash at %timestamp% to %environment_url% Login: %login_url%" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success run mock_get_call_args "${mock_curl}" 2 @@ -245,7 +245,7 @@ Login: %login_url%" export VORTEX_NOTIFY_JIRA_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # The whole log is added to the comment as an ADF code block. diff --git a/.vortex/tooling/tests/unit/notify-newrelic.bats b/.vortex/tooling/tests/unit/notify-newrelic.bats index d37969599..18848c80e 100644 --- a/.vortex/tooling/tests/unit/notify-newrelic.bats +++ b/.vortex/tooling/tests/unit/notify-newrelic.bats @@ -18,7 +18,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" # VORTEX_NOTIFY_NEWRELIC_ENABLED is intentionally not set - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -49,7 +49,7 @@ load ../_helper.bash export VORTEX_NOTIFY_LABEL="main" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -96,7 +96,7 @@ load ../_helper.bash export VORTEX_NOTIFY_LABEL="main" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Missing required value for VORTEX_NOTIFY_NEWRELIC_USER_KEY" @@ -121,7 +121,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="main" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -146,7 +146,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" # VORTEX_NOTIFY_NEWRELIC_BRANCHES defaults to "main,master,develop" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -167,7 +167,7 @@ load ../_helper.bash export VORTEX_NOTIFY_NEWRELIC_ENABLED=true export VORTEX_NOTIFY_NEWRELIC_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify-newrelic + run .vortex/tooling/src/vortex-notify-newrelic assert_success assert_output_contains 'Skipped New Relic notification for branch "".' @@ -195,7 +195,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" export VORTEX_NOTIFY_NEWRELIC_BRANCHES="main,staging" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -228,11 +228,11 @@ load ../_helper.bash # Ensure test file doesn't exist before rm -f /tmp/injected_newrelic_test - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Verify the injection file was NOT created (injection did not execute) - [ ! -f /tmp/injected_newrelic_test ] + assert_file_not_exists "/tmp/injected_newrelic_test" # Verify the malicious string is treated as literal text in the description assert_output_contains "test'); file_put_contents('/tmp/injected_newrelic_test', 'HACKED'); //" diff --git a/.vortex/tooling/tests/unit/notify-slack.bats b/.vortex/tooling/tests/unit/notify-slack.bats index a3488e225..9339d98e3 100644 --- a/.vortex/tooling/tests/unit/notify-slack.bats +++ b/.vortex/tooling/tests/unit/notify-slack.bats @@ -22,7 +22,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_ICON_EMOJI=":rocket:" export VORTEX_NOTIFY_SLACK_EVENT="pre_deployment" - run ./.vortex/tooling/src/vortex-notify-slack + run .vortex/tooling/src/vortex-notify-slack assert_success # Assert script output @@ -63,7 +63,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_ICON_EMOJI=":rocket:" export VORTEX_NOTIFY_SLACK_EVENT="post_deployment" - run ./.vortex/tooling/src/vortex-notify-slack + run .vortex/tooling/src/vortex-notify-slack assert_success # Assert script output @@ -104,7 +104,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_ICON_EMOJI=":rocket:" export VORTEX_NOTIFY_SLACK_EVENT="pre_deployment" - run ./.vortex/tooling/src/vortex-notify-slack + run .vortex/tooling/src/vortex-notify-slack assert_success # Assert script output @@ -145,7 +145,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_ICON_EMOJI=":rocket:" export VORTEX_NOTIFY_SLACK_EVENT="post_deployment" - run ./.vortex/tooling/src/vortex-notify-slack + run .vortex/tooling/src/vortex-notify-slack assert_success # Assert script output @@ -181,7 +181,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" # No VORTEX_NOTIFY_SLACK_WEBHOOK set - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Started dispatching notifications." @@ -206,7 +206,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" export VORTEX_NOTIFY_SLACK_WEBHOOK="https://hooks.slack.com/services/INVALID" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Started dispatching notifications." @@ -234,7 +234,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_USERNAME="Custom Deploy Bot" export VORTEX_NOTIFY_SLACK_ICON_EMOJI=":ship:" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -260,7 +260,7 @@ load ../_helper.bash export VORTEX_NOTIFY_ENVIRONMENT_URL="https://develop.testproject.com" export VORTEX_NOTIFY_SLACK_WEBHOOK="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -284,7 +284,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_WEBHOOK="https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXX" export VORTEX_NOTIFY_SLACK_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -304,7 +304,7 @@ load ../_helper.bash unset VORTEX_NOTIFY_BRANCH export VORTEX_NOTIFY_SLACK_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify-slack + run .vortex/tooling/src/vortex-notify-slack assert_success assert_output_contains 'Skipped Slack notification for branch "".' @@ -331,11 +331,11 @@ load ../_helper.bash # Ensure test file doesn't exist before rm -f /tmp/injected_slack_test - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Verify the injection file was NOT created (injection did not execute) - [ ! -f /tmp/injected_slack_test ] + assert_file_not_exists "/tmp/injected_slack_test" # Verify the malicious string is treated as literal text in the fallback message assert_output_contains "test'); file_put_contents('/tmp/injected_slack_test', 'HACKED'); //" @@ -363,7 +363,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SLACK_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # The whole log is placed in the attachment text. diff --git a/.vortex/tooling/tests/unit/notify-webhook.bats b/.vortex/tooling/tests/unit/notify-webhook.bats index 3c6d749ac..1ffe91f3e 100644 --- a/.vortex/tooling/tests/unit/notify-webhook.bats +++ b/.vortex/tooling/tests/unit/notify-webhook.bats @@ -24,7 +24,7 @@ load ../_helper.bash export VORTEX_NOTIFY_WEBHOOK_HEADERS="Content-type: application/json|Authorization: Bearer API_KEY" export VORTEX_NOTIFY_WEBHOOK_PAYLOAD='{"channel": "Test channel 1", "message": "Test channel 1 message"}' - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -56,7 +56,7 @@ load ../_helper.bash export VORTEX_NOTIFY_WEBHOOK_HEADERS="Content-type: application/json|Authorization: Bearer API_KEY" export VORTEX_NOTIFY_WEBHOOK_PAYLOAD='{"channel": "Test channel 1", "message": "Test channel 1 message"}' - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure popd >/dev/null || exit 1 @@ -76,7 +76,7 @@ load ../_helper.bash export VORTEX_NOTIFY_WEBHOOK_METHOD="POST" export VORTEX_NOTIFY_WEBHOOK_HEADERS="Content-type: application/json|Authorization: Bearer API_KEY" export VORTEX_NOTIFY_WEBHOOK_PAYLOAD='{"channel": "Test channel 1", "message": "Test channel 1 message"}' - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -101,7 +101,7 @@ load ../_helper.bash export VORTEX_NOTIFY_WEBHOOK_HEADERS="Content-type: application/json" export VORTEX_NOTIFY_WEBHOOK_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -121,7 +121,7 @@ load ../_helper.bash unset VORTEX_NOTIFY_BRANCH export VORTEX_NOTIFY_WEBHOOK_BRANCHES="main,develop" - run ./.vortex/tooling/src/vortex-notify-webhook + run .vortex/tooling/src/vortex-notify-webhook assert_success assert_output_contains 'Skipped webhook notification for branch "".' @@ -148,11 +148,11 @@ load ../_helper.bash # Ensure test file doesn't exist before rm -f /tmp/injected_webhook_test - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Verify the injection file was NOT created (injection did not execute) - [ ! -f /tmp/injected_webhook_test ] + assert_file_not_exists "/tmp/injected_webhook_test" # Verify the malicious string is treated as literal text in the payload assert_output_contains "test'); file_put_contents('/tmp/injected_webhook_test', 'HACKED'); //" @@ -180,7 +180,7 @@ load ../_helper.bash export VORTEX_NOTIFY_WEBHOOK_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # The whole log is JSON-escaped into the default payload's message. @@ -212,7 +212,7 @@ load ../_helper.bash export VORTEX_NOTIFY_WEBHOOK_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success run mock_get_call_args "${mock_curl}" 1 diff --git a/.vortex/tooling/tests/unit/notify.bats b/.vortex/tooling/tests/unit/notify.bats index e5530a936..05e5e7703 100644 --- a/.vortex/tooling/tests/unit/notify.bats +++ b/.vortex/tooling/tests/unit/notify.bats @@ -18,7 +18,7 @@ load ../_helper.bash pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 export VORTEX_NOTIFY_SKIP=1 - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -36,7 +36,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="develop" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_failure assert_output_contains "Started dispatching notifications." @@ -85,7 +85,7 @@ load ../_helper.bash export VORTEX_NOTIFY_JIRA_USER_EMAIL="test@example.com" export VORTEX_NOTIFY_JIRA_TOKEN="test_token" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -129,7 +129,7 @@ load ../_helper.bash export VORTEX_NOTIFY_SHA="abc123def456" export VORTEX_NOTIFY_LABEL="develop" export VORTEX_NOTIFY_ENVIRONMENT_URL="https://test.example.com" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success assert_output_contains "Started dispatching notifications." @@ -158,7 +158,7 @@ load ../_helper.bash export VORTEX_NOTIFY_EMAIL_LOG=1 export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" - run ./.vortex/tooling/src/vortex-notify + run .vortex/tooling/src/vortex-notify assert_success # Every '*.log' in the directory is collected, each as its own titled section. diff --git a/.vortex/tooling/tests/unit/provision.bats b/.vortex/tooling/tests/unit/provision.bats index d0e72e0a5..cab7fb51e 100644 --- a/.vortex/tooling/tests/unit/provision.bats +++ b/.vortex/tooling/tests/unit/provision.bats @@ -211,7 +211,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -377,7 +377,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -552,7 +552,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -742,7 +742,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -810,7 +810,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_failure steps_run "assert" "${mocks[@]}" @@ -983,7 +983,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1152,7 +1152,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1328,7 +1328,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1466,7 +1466,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1516,7 +1516,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_failure steps_run "assert" "${mocks[@]}" @@ -1687,7 +1687,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1860,7 +1860,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1924,7 +1924,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -1989,7 +1989,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -2153,7 +2153,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -2344,7 +2344,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -2392,7 +2392,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_failure steps_run "assert" "${mocks[@]}" @@ -2587,7 +2587,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success steps_run "assert" "${mocks[@]}" @@ -2679,7 +2679,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_failure steps_run "assert" "${mocks[@]}" @@ -2695,7 +2695,7 @@ assert_provision_info() { export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" unset VORTEX_PROVISION_LOG_ACTIVE - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success assert_output_contains "Started site provisioning." @@ -2721,7 +2721,7 @@ assert_provision_info() { export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/logs" unset VORTEX_PROVISION_LOG_ACTIVE - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success # Without VORTEX_PROVISION_LOG=1 the log file is never written. @@ -2742,7 +2742,7 @@ assert_provision_info() { export VORTEX_NOTIFY_LOG_DIR="${BATS_TEST_TMPDIR}/blocker/logs" unset VORTEX_PROVISION_LOG_ACTIVE - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success assert_output_contains "Finished site provisioning." @@ -2917,7 +2917,7 @@ assert_provision_info() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-provision + run .vortex/tooling/src/vortex-provision assert_success console_output="${output}" diff --git a/.vortex/tooling/tests/unit/push-container-registry.bats b/.vortex/tooling/tests/unit/push-container-registry.bats index ca3a091dd..34b373a95 100644 --- a/.vortex/tooling/tests/unit/push-container-registry.bats +++ b/.vortex/tooling/tests/unit/push-container-registry.bats @@ -5,16 +5,6 @@ # shellcheck disable=SC2030,SC2031,SC2129,SC2155,SC2034 load ../_helper.bash -setup_robo_fixture() { - export HOME="${BUILD_DIR}" - fixture_prepare_dir "${HOME}/.composer/vendor/bin" - touch "${HOME}/.composer/vendor/bin/robo" - chmod +x "${HOME}/.composer/vendor/bin/robo" - - # Also create a mock for git-artifact - touch "${HOME}/.composer/vendor/bin/git-artifact" - chmod +x "${HOME}/.composer/vendor/bin/git-artifact" -} @test "Missing VORTEX_PUSH_CONTAINER_REGISTRY_MAP - push should not proceed" { pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 @@ -105,7 +95,7 @@ setup_robo_fixture() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-push-container-registry + run .vortex/tooling/src/vortex-push-container-registry assert_success steps_run "assert" "${mocks[@]}" @@ -136,7 +126,7 @@ setup_robo_fixture() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-push-container-registry + run .vortex/tooling/src/vortex-push-container-registry assert_failure steps_run "assert" "${mocks[@]}" @@ -157,7 +147,7 @@ setup_robo_fixture() { # No key/value pair export VORTEX_PUSH_CONTAINER_REGISTRY_MAP="service1" - run ./.vortex/tooling/src/vortex-push-container-registry + run .vortex/tooling/src/vortex-push-container-registry assert_failure assert_output_contains 'Invalid key/value pair "service1" provided.' @@ -225,7 +215,7 @@ setup_robo_fixture() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-push-container-registry + run .vortex/tooling/src/vortex-push-container-registry assert_success steps_run "assert" "${mocks[@]}" @@ -254,7 +244,7 @@ setup_robo_fixture() { mocks="$(steps_run "setup")" - run ./.vortex/tooling/src/vortex-push-container-registry + run .vortex/tooling/src/vortex-push-container-registry assert_success assert_output_not_contains "supersecretpass" diff --git a/.vortex/tooling/tests/unit/push-db-image.bats b/.vortex/tooling/tests/unit/push-db-image.bats index 2368a0732..0a0a35fed 100644 --- a/.vortex/tooling/tests/unit/push-db-image.bats +++ b/.vortex/tooling/tests/unit/push-db-image.bats @@ -10,15 +10,6 @@ load ../_helper.bash -# Replaces a sibling tooling script with a stub that prints a marker. The script -# dispatches to siblings by explicit path, so a PATH-based mock cannot intercept -# them - the file itself must be replaced. -stub_sibling() { - mkdir -p .vortex/tooling/src - printf '#!/usr/bin/env bash\necho "%s"\n' "${2}" >".vortex/tooling/src/${1}" - chmod +x ".vortex/tooling/src/${1}" -} - @test "push-db-image: Skips the push when it is not requested" { pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 diff --git a/.vortex/tooling/tests/unit/push-db-s3.bats b/.vortex/tooling/tests/unit/push-db-s3.bats index 23d79fd88..f3d85fdd7 100644 --- a/.vortex/tooling/tests/unit/push-db-s3.bats +++ b/.vortex/tooling/tests/unit/push-db-s3.bats @@ -38,7 +38,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -77,7 +77,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -120,7 +120,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -158,7 +158,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -197,7 +197,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -237,7 +237,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_success @@ -273,7 +273,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -300,7 +300,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -325,7 +325,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -350,7 +350,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -375,7 +375,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure @@ -405,7 +405,7 @@ load ../_helper.bash mocks="$(steps_run "setup")" run .vortex/tooling/src/vortex-push-db-s3 - steps_run "assert" "${mocks}" + steps_run "assert" "${mocks[@]}" assert_failure diff --git a/.vortex/tooling/tests/unit/task.bats b/.vortex/tooling/tests/unit/task.bats index 7715245d9..fdb6078aa 100644 --- a/.vortex/tooling/tests/unit/task.bats +++ b/.vortex/tooling/tests/unit/task.bats @@ -14,7 +14,7 @@ load ../_helper.bash @test "Task: missing operation" { pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 - run ./.vortex/tooling/src/vortex-task + run .vortex/tooling/src/vortex-task assert_failure assert_output_contains "Missing task operation." @@ -24,7 +24,7 @@ load ../_helper.bash @test "Task: unsupported operation" { pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 - run ./.vortex/tooling/src/vortex-task invalid-operation + run .vortex/tooling/src/vortex-task invalid-operation assert_failure assert_output_contains 'Unsupported task operation "invalid-operation".' @@ -36,7 +36,7 @@ load ../_helper.bash unset VORTEX_TASK_PLATFORM unset VORTEX_PLATFORM - run ./.vortex/tooling/src/vortex-task copy-db + run .vortex/tooling/src/vortex-task copy-db assert_failure assert_output_contains "Missing hosting platform. Set VORTEX_PLATFORM or VORTEX_TASK_PLATFORM." @@ -48,7 +48,7 @@ load ../_helper.bash unset VORTEX_TASK_PLATFORM export VORTEX_PLATFORM=invalid-platform - run ./.vortex/tooling/src/vortex-task copy-db + run .vortex/tooling/src/vortex-task copy-db assert_failure assert_output_contains 'Unsupported hosting platform "invalid-platform".' @@ -61,7 +61,7 @@ load ../_helper.bash # Lagoon has no copy-db implementation, so the sibling resolution fails. unset VORTEX_TASK_PLATFORM export VORTEX_PLATFORM=lagoon - run ./.vortex/tooling/src/vortex-task copy-db + run .vortex/tooling/src/vortex-task copy-db assert_failure assert_output_contains 'Operation "copy-db" is not supported on the "lagoon" platform.' @@ -75,7 +75,7 @@ load ../_helper.bash # missing-key guard before any network call, which proves the routing. unset VORTEX_TASK_PLATFORM export VORTEX_PLATFORM=acquia - run ./.vortex/tooling/src/vortex-task copy-db + run .vortex/tooling/src/vortex-task copy-db assert_failure assert_output_contains "Started database copying between environments in Acquia." @@ -87,7 +87,7 @@ load ../_helper.bash export VORTEX_PLATFORM=lagoon export VORTEX_TASK_PLATFORM=acquia - run ./.vortex/tooling/src/vortex-task copy-db + run .vortex/tooling/src/vortex-task copy-db assert_failure assert_output_contains "Started database copying between environments in Acquia." @@ -98,7 +98,7 @@ load ../_helper.bash pushd "${LOCAL_REPO_DIR}" >/dev/null || exit 1 export VORTEX_TASK_PLATFORM=acquia - run ./.vortex/tooling/src/vortex-task copy-files + run .vortex/tooling/src/vortex-task copy-files assert_failure assert_output_contains "Started files copying between environments in Acquia." @@ -110,7 +110,7 @@ load ../_helper.bash unset VORTEX_TASK_PLATFORM export VORTEX_PLATFORM=acquia - run ./.vortex/tooling/src/vortex-task purge-cache + run .vortex/tooling/src/vortex-task purge-cache assert_failure assert_output_contains "Started cache purging in Acquia." @@ -124,7 +124,7 @@ load ../_helper.bash # missing-value guard before any network call, which proves the routing. unset VORTEX_TASK_PLATFORM export VORTEX_PLATFORM=lagoon - run ./.vortex/tooling/src/vortex-task custom + run .vortex/tooling/src/vortex-task custom assert_failure assert_output_contains "Started Lagoon task Automation task." assert_output_contains "Missing required value for VORTEX_TASK_CUSTOM_LAGOON_BRANCH."