test_helpers-1.0.0-alpha6/tests/modules/test_helpers_http_client_mock/src/HttpClientFactoryMock.php

tests/modules/test_helpers_http_client_mock/src/HttpClientFactoryMock.php
<?php

declare(strict_types=1);

namespace Drupal\test_helpers_http_client_mock;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Lock\LockBackendInterface;
use Drupal\Core\State\StateInterface;
use Drupal\test_helpers\Stub\HttpClientFactoryStub;
use GuzzleHttp\HandlerStack;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

/**
 * Extension of the HttpClientFactoryStub service for functional tests.
 *
 * Overrides the constructor to set the test configuration from the State.
 *
 * Also, adds a custom HTTP header to the response with used requests hashes.
 */
class HttpClientFactoryMock extends HttpClientFactoryStub implements EventSubscriberInterface {

  /**
   * The key to store the requests mocking mode in the configuration.
   *
   * @var string
   */

  const SETTINGS_CONFIG_KEY = 'test_helpers_http_client_mock.settings';
  /**
   * The key to store the requests mocking mode in the configuration.
   *
   * @var string
   */
  const SETTING_KEY_REQUEST_MOCK_MODE = 'request_mock_mode';

  /**
   * The key to store the responses storage directory in the configuration.
   *
   * @var string
   */
  const SETTING_KEY_RESPONSES_STORAGE_DIRECTORY = 'responses_storage_directory';

  /**
   * The key to store the responses context in the configuration.
   *
   * @var string
   */
  const SETTING_KEY_CONTEXT = 'context';

  /**
   * The key to store the log stored responses file in the configuration.
   *
   * @var string
   */
  const SETTING_KEY_LOG_STORED_RESPONSES_USAGE_FILE = 'log_stored_responses_usage_file';

  /**
   * The key to store the test name in the configuration.
   *
   * @var string
   */
  const SETTING_KEY_TEST_NAME = 'test_name';

  /**
   * The key to store the URI regular expression in the configuration.
   *
   * @var string
   */
  const SETTING_KEY_URI_REGEXP = 'uri_regexp';

  /**
   * The key to store the list of requests hashes in the State.
   *
   * @var string
   */
  const STATE_KEY_LAST_REQUESTS_HASHES = 'test_helpers_http_client_mock.last_requests_hashes';

  /**
   * The custom HTTP header name to pass the stored requests hashes.
   *
   * @var string
   */
  const HTTP_HEADER_NAME = 'X-Test-Helpers-Mocked-Requests-Hashes';

  /**
   * The custom meta tag name to pass the stored requests hashes.
   *
   * @var string
   */
  const META_TAG_NAME = 'TestHelpersHttpClientMockRequestsHashes';

  /**
   * The custom meta tag key to use in the Drupal render array.
   *
   * @var string
   */
  const META_TAG_KEY = 'test_helpers_http_client_mock_requests_hashes';

  /**
   * The limit of the last requests hashes to store.
   *
   * @var int
   */
  const LAST_REQUESTS_HASHES_STORE_LIMIT = 64;

  /**
   * The key to store the last requests hashes in the State.
   *
   * @var string
   */
  const LOCK_KEY_LAST_REQUESTS_HASHES_UPDATE = 'test_helpers_http_client_mock.last_requests_hashes_update';

  /**
   * HttpClientFactoryMock constructor.
   *
   * @param \GuzzleHttp\HandlerStack $stack
   *   The GuzzleHttp handler stack.
   * @param \Drupal\Core\State\StateInterface $stateService
   *   The Drupal state service.
   * @param \Drupal\Core\Config\ConfigFactoryInterface $configFactory
   *   The configuration factory.
   * @param \Drupal\Core\Lock\LockBackendInterface $lock
   *   The lock backend.
   * @param string|null $requestMockMode
   *   The requests mocking mode: NULL, 'store', 'mock'.
   * @param string|null $responsesStorageDirectory
   *   The directory to store responses.
   * @param string|null $testName
   *   The name of the test.
   * @param string|null $uriRegexp
   *   A regular expression to match URIs and process only matched ones.
   */
  public function __construct(
    HandlerStack $stack,
    protected StateInterface $stateService,
    protected ConfigFactoryInterface $configFactory,
    protected LockBackendInterface $lock,
    protected ?string $requestMockMode = NULL,
    protected ?string $responsesStorageDirectory = NULL,
    protected ?string $testName = NULL,
    protected ?string $uriRegexp = NULL,
  ) {
    $this->requestMockMode ??= $this->stubGetConfig(self::SETTING_KEY_REQUEST_MOCK_MODE);
    $this->responsesStorageDirectory ??= $this->stubGetConfig(self::SETTING_KEY_RESPONSES_STORAGE_DIRECTORY);
    $this->testName ??= $this->stubGetConfig(self::SETTING_KEY_TEST_NAME);
    $this->uriRegexp ??= $this->stubGetConfig(self::SETTING_KEY_URI_REGEXP);

    $options = [
      HttpClientFactoryStub::OPTION_URI_REGEXP => $this->uriRegexp,
      HttpClientFactoryStub::OPTION_LOG_STORED_RESPONSES_USAGE_FILE => $this->stubGetConfig(self::SETTING_KEY_LOG_STORED_RESPONSES_USAGE_FILE),
      HttpClientFactoryStub::OPTION_CONTEXT => $this->stubGetConfig(self::SETTING_KEY_CONTEXT),
    ];

    parent::__construct(
      $stack,
      $this->requestMockMode,
      $this->responsesStorageDirectory,
      $this->testName,
      $options,
    );
  }

  /**
   * {@inheritdoc}
   */
  public static function getSubscribedEvents(): array {
    // Respond to the kernel.response event and call onRespond().
    $events[KernelEvents::RESPONSE][] = 'onRespond';
    return $events;
  }

  /**
   * Adds a custom header to the response with requests hashes.
   *
   * @param \Symfony\Component\HttpKernel\Event\ResponseEvent $event
   *   A response event.
   */
  public function onRespond(ResponseEvent $event) {
    if ($hashes = $this->stubGetHandledRequests()) {
      $response = $event->getResponse();
      $response->headers->set(self::HTTP_HEADER_NAME, json_encode($hashes));
    }
  }

  /**
   * Stores the request has to the state, to retrieve a list of last requests.
   *
   * @param string $hash
   *   A hash value.
   */
  protected function stubStoreRequestHashUsage(string $hash): void {
    parent::stubStoreRequestHashUsage($hash);
    $this->acquireLockWithWait(self::LOCK_KEY_LAST_REQUESTS_HASHES_UPDATE);
    // The State service has a static cache, so we have to reset it to receive
    // the fresh value if a parallel request has updated the State.
    $this->stateService->resetCache();
    $lastHashes = $this->stateService->get(self::STATE_KEY_LAST_REQUESTS_HASHES, []);
    array_unshift($lastHashes, $hash);
    $lastHashes = array_slice($lastHashes, 0, self::LAST_REQUESTS_HASHES_STORE_LIMIT);
    $this->stateService->set(self::STATE_KEY_LAST_REQUESTS_HASHES, $lastHashes);
    $this->lock->release(self::LOCK_KEY_LAST_REQUESTS_HASHES_UPDATE);
  }

  /**
   * Retrieves a configuration value by key.
   *
   * @param string $key
   *   The configuration key.
   *
   * @return mixed
   *   The configuration value.
   */
  public function stubGetConfig(string $key) {
    return $this->configFactory->get(self::SETTINGS_CONFIG_KEY)->get($key);
  }

  /**
   * Retrieves a configuration object.
   *
   * @return \Drupal\Core\Config\ImmutableConfig
   *   The configuration object.
   */
  public function stubGetConfiguration(): ImmutableConfig {
    return $this->configFactory->get(self::SETTINGS_CONFIG_KEY);
  }

  /**
   * Sets a configuration value by key.
   *
   * @param string $key
   *   The configuration key.
   * @param mixed $value
   *   The configuration value.
   */
  public function stubSetConfig(string $key, mixed $value): void {
    $this->configFactory->getEditable(self::SETTINGS_CONFIG_KEY)
      ->set($key, $value)
      ->save();
  }

  /**
   * Tries to acquire a lock for a given key with a wait time.
   */
  public function acquireLockWithWait(string $key): void {
    // The Drupal API doesn't provide a reliable way to wait for a lock
    // acquisition. See the comment on the $lock->wait():
    // ```
    // You still need to acquire the lock manually and it may fail again.
    // ```
    // So we have to implement our own waiting mechanism.
    $tries = 100;
    for ($i = 0; $i < $tries; $i++) {
      if ($this->lock->acquire($key)) {
        return;
      }
      // Sleep for 0.1 second.
      usleep(100000);
    }
    throw new \RuntimeException('Could not acquire the lock.');
  }

}

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

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