commercetools-8.x-1.2-alpha1/tests/modules/commercetools_test/src/CommerceToolsTesting.php

tests/modules/commercetools_test/src/CommerceToolsTesting.php
<?php

declare(strict_types=1);

namespace Drupal\commercetools_test;

use Drupal\commercetools\CommercetoolsApiService;
use Drupal\commercetools\CommercetoolsApiServiceInterface;
use Drupal\commercetools\CommercetoolsService;
use Drupal\Component\Serialization\Yaml;
use Drupal\test_helpers\Stub\HttpClientFactoryStub;
use Drupal\test_helpers\TestHelpers;
use Drupal\test_helpers_http_client_mock\HttpClientFactoryMock;
use GraphQL\Language\Parser;
use GraphQL\Language\Printer;

/**
 * A class with helper functions for tests.
 */
class CommerceToolsTesting {

  /**
   * Env variable to control the mocking commercetools API mode.
   *
   * Allowed values:
   * - mock (default): reads responses only from the stored asset files, if
   *   missing - throws an exception.
   * - append: uses the asset files first, if missing - makes a real call and
   *   create/overwrite asset files.
   * - store: always makes direct calls to API and create/overwrite asset files.
   */
  const ENV_CT_API_MOCK_MODE = 'CT_API_MOCK_MODE';

  const CT_API_MOCK_MODE_MOCK = 'mock';
  const CT_API_MOCK_MODE_STORE = 'store';
  const CT_API_MOCK_MODE_APPEND = 'append';


  /**
   * Env variable to log usage of stored responses to a file.
   */
  const ENV_LOG_STORED_RESPONSES_FILE = 'CT_LOG_STORED_RESPONSES_FILE';
  /**
   * Env variables to override the test configuration variables.
   */
  /**
   * A directory to store responses of the real Commercetools API calls.
   */
  const RESPONSES_STORAGE_DIRECTORY = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'assets';

  /**
   * Regexp to match the Commercetools API URL.
   *
   * @var string
   */
  const COMMERCETOOLS_API_URL_REGEXP = '/commercetools\.com/';

  /**
   * Get the module test configuration.
   *
   * @param string $moduleName
   *   The original module, the test module is assumed with the '_test' suffix.
   *
   * @return array
   *   Array of the module configuration.
   */
  public static function getTestConfiguration(string $moduleName): array {
    $moduleTestName = $moduleName . '_test';
    $modulePaths = [
      'commercetools' => __DIR__ . '/../../../..',
      'commercetools_content' => __DIR__ . '/../../../../modules/commercetools_content',
      'commercetools_decoupled' => __DIR__ . '/../../../../modules/commercetools_decoupled',
      'commercetools_demo' => __DIR__ . '/../../../../modules/commercetools_demo',
    ];

    if (!isset($modulePaths[$moduleName])) {
      throw new \Exception("The path for the module $moduleName is not configured.");
    }

    $configInstallDirectory = $modulePaths[$moduleName] . "/config/install";
    if (file_exists($configInstallDirectory)) {
      $configInstallFiles = scandir($configInstallDirectory);
      foreach ($configInstallFiles ?? [] as $file) {
        if (!preg_match('#^(.+)\.yml#', $file, $matches)) {
          continue;
        }
        $configName = $matches[1];
        $config[$configName] = Yaml::decode(file_get_contents($configInstallDirectory . DIRECTORY_SEPARATOR . $file));
      }
    }

    $configOverrideFile = $modulePaths[$moduleName] . "/tests/modules/$moduleTestName/config/override/config.yml";
    if (file_exists($configOverrideFile)) {
      $configOverride = Yaml::decode(file_get_contents($configOverrideFile));
      foreach ($configOverride as $key => $value) {
        $config[$key] = array_merge($config[$key] ?? [], $configOverride[$key]);
      }
    }

    if (empty($config)) {
      return [];
    }
    // Get just the first key of the array, should work for our cases.
    $configName = key($config);

    // Set the demo API credentials only for the non-mock modes.
    if (self::getCtApiMockMode() !== self::CT_API_MOCK_MODE_MOCK) {
      $demoAccountName = 'b2c_lifestyle';
      $demoAccount = Yaml::decode(
        file_get_contents(
          __DIR__ . '/../../../../modules/commercetools_demo/fixtures/demo_accounts/' . $demoAccountName . '.yml'
        )
      );
      if (!is_array($demoAccount)) {
        throw new \Exception("Can't read the demo account configuration for $demoAccountName.");
      }
      $config['commercetools.api'] = $demoAccount['config']['commercetools.api'];
    }
    return $config;
  }

  /**
   * Gets the commercetools API mocking mode.
   *
   * Controllable by the CT_API_MOCK_MODE environment variable.
   * To make real requests, it is required to pass real credentials via the
   * environment variables.
   */
  public static function getCtApiMockMode(): string {
    return getenv(self::ENV_CT_API_MOCK_MODE) ?: self::CT_API_MOCK_MODE_MOCK;
  }

  /**
   * Initiates the working `commercetools.api` Drupal service for unit tests.
   *
   * @param string|null $testName
   *   The name of the test.
   *
   * @return \Drupal\commercetools\CommercetoolsApiServiceInterface
   *   A working instance of the `commercetools.api` Drupal service.
   */
  public static function getCommercetoolsApiService(?string $testName = NULL): CommercetoolsApiServiceInterface {
    $config = self::getTestConfiguration('commercetools');
    $moduleDirectory = TestHelpers::getModuleRoot(CommercetoolsService::class);
    TestHelpers::service('string_translation');
    TestHelpers::service('commercetools.psr_cache_adapter', servicesYamlFile: $moduleDirectory . '/commercetools.services.yml', initService: TRUE);
    TestHelpers::service('path.current');
    foreach ($config as $configName => $configData) {
      TestHelpers::service('config.factory')->stubSetConfig($configName, $configData);
    }
    TestHelpers::service('commercetools.config', servicesYamlFile: $moduleDirectory . '/commercetools.services.yml', initService: TRUE);

    $httpClientMockConfig = self::getHttpClientFactoryTestConfiguration();

    // Initiate the CtHttpClientFactoryStub service with custom configuration.
    $ctHttpClientFactoryStub = new CommercetoolsHttpClientFactoryStub(
      requestMockMode: $httpClientMockConfig[HttpClientFactoryMock::SETTING_KEY_REQUEST_MOCK_MODE],
      responsesStorageDirectory: $httpClientMockConfig[HttpClientFactoryMock::SETTING_KEY_RESPONSES_STORAGE_DIRECTORY],
      testName: $testName,
      options: [
        HttpClientFactoryStub::OPTION_URI_REGEXP => $httpClientMockConfig[HttpClientFactoryMock::SETTING_KEY_URI_REGEXP],
        HttpClientFactoryStub::OPTION_LOG_STORED_RESPONSES_USAGE_FILE => $httpClientMockConfig[HttpClientFactoryMock::SETTING_KEY_LOG_STORED_RESPONSES_USAGE_FILE] ?? NULL,
      ],
    );
    TestHelpers::service('http_client_factory', $ctHttpClientFactoryStub, forceOverride: TRUE);

    TestHelpers::service('logger.channel.commercetools', TestHelpers::service('logger.factory')->get('commercetools'));

    /**
     * @var \Drupal\commercetools\CommercetoolsApiServiceInterface $service
     */
    $service = TestHelpers::service(
      'commercetools.api',
      CommercetoolsApiService::class,
      forceOverride: TRUE,
      initService: TRUE
    );
    return $service;
  }

  /**
   * Configures the HTTP client factory mock with test environment.
   */
  public static function configureHttpClientFactoryMock(): void {
    // Deploys a configuration from environment variables or mocked values.
    $httpClientFactoryStub = \Drupal::service('http_client_factory');

    $config = self::getHttpClientFactoryTestConfiguration();
    foreach ($config as $key => $value) {
      $httpClientFactoryStub->stubSetConfig($key, $value);
    }
  }

  /**
   * Generates the configuration for the HttpClientFactoryMock service.
   *
   * @return array
   *   The configuration array.
   */
  public static function getHttpClientFactoryTestConfiguration(): array {
    $config = [];
    $config[HttpClientFactoryMock::SETTING_KEY_RESPONSES_STORAGE_DIRECTORY] = self::RESPONSES_STORAGE_DIRECTORY;
    $config[HttpClientFactoryMock::SETTING_KEY_URI_REGEXP] = self::COMMERCETOOLS_API_URL_REGEXP;

    $config[HttpClientFactoryMock::SETTING_KEY_REQUEST_MOCK_MODE] =
      match (CommerceToolsTesting::getCtApiMockMode()) {
      self::CT_API_MOCK_MODE_STORE => HttpClientFactoryStub::HTTP_CLIENT_MODE_STORE,
        self::CT_API_MOCK_MODE_MOCK => HttpClientFactoryStub::HTTP_CLIENT_MODE_MOCK,
        self::CT_API_MOCK_MODE_APPEND => HttpClientFactoryStub::HTTP_CLIENT_MODE_APPEND,
        default => HttpClientFactoryStub::HTTP_CLIENT_MODE_MOCK,
      };

    if ($logFile = getenv(self::ENV_LOG_STORED_RESPONSES_FILE)) {
      $config[HttpClientFactoryMock::SETTING_KEY_LOG_STORED_RESPONSES_USAGE_FILE] = $logFile;
    }
    return $config;
  }

  /**
   * Initiates the working `commercetools.api` Drupal service for tests.
   *
   * @param string|null $testName
   *   The name of the test.
   *
   * @return \Drupal\commercetools\CommercetoolsService
   *   A working instance of the `commercetools` Drupal service.
   */
  public static function getCommercetoolsService(?string $testName = NULL): CommercetoolsService {
    $container = TestHelpers::getContainer();
    if (!$container->has('commercetools.api')) {
      self::getCommercetoolsApiService($testName);
    }
    TestHelpers::service('language_manager', initService: TRUE);
    $moduleDirectory = TestHelpers::getModuleRoot(CommercetoolsService::class);
    TestHelpers::service('commercetools.localization', servicesYamlFile: $moduleDirectory . '/commercetools.services.yml', initService: TRUE);
    $service = TestHelpers::service('commercetools', servicesYamlFile: $moduleDirectory . '/commercetools.services.yml', initService: TRUE);
    return $service;
  }

  /**
   * Set the module test configuration.
   *
   * @param string $moduleName
   *   The module name.
   */
  public static function setCommercetoolsTestConfig(string $moduleName) {
    $configFactory = \Drupal::configFactory();
    foreach (self::getTestConfiguration($moduleName) as $configName => $configData) {
      $config = $configFactory->getEditable($configName);
      foreach ($configData as $key => $value) {
        $config->set($key, $value);
      }
      $config->save();
    }
  }

  /**
   * Formats the Commercetools request asset metadata body as multiline YAML.
   *
   * To make reviewing of the modified stored asset files metadata easier with
   * better diffs.
   *
   * @param array $thMetadata
   *   The metadata array.
   *
   * @return array
   *   The formatted metadata array.
   */
  public static function formatCtRequestAssetMetadata(array $thMetadata) {
    if (!empty($thMetadata['body'])) {
      try {
        $bodyJsonDecoded = json_decode($thMetadata['body'], TRUE);
        // If the body is not in JSON format, it will return NULL.
        // In this case, we do not modify the body.
        if ($bodyJsonDecoded === NULL) {
          return $thMetadata;
        }
        // Detect GraphQL requests and force prettify them.
        if (isset($bodyJsonDecoded['query'])) {
          $graphqlQuery = $bodyJsonDecoded['query'];
          $queryObject = Parser::parse($graphqlQuery);
          $bodyJsonDecoded['query'] = Printer::doPrint($queryObject);
        }
        $encoded = Yaml::encode($bodyJsonDecoded);
        unset($thMetadata['body']);
        $thMetadata['body_formatted'] = '# Encoded using YAML format by the function .CommerceToolsTesting::formatCtRequestAssetMetadata()' . PHP_EOL . $encoded;
      }
      catch (\JsonException) {
        // Do not modify if the body is not in JSON format.
      }
    }
    return $thMetadata;
  }

}

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc