Commit 626b2a9b authored by Alexey Korepov's avatar Alexey Korepov
Browse files

Issue #3313778 by Murz: 3313778-database-stub

parent 52131406
Loading
Loading
Loading
Loading

src/ConnectionStub.php

0 → 100644
+69 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\test_helpers;

use Drupal\sqlite\Driver\Database\sqlite\Connection;
use Drupal\Tests\Core\Database\Stub\StubPDO;

/**
 * The ConnectionStubFactory class.
 *
 * A stub for class Drupal\Driver\Database\fake\Connection.
 */
class ConnectionStub extends Connection {
  /**
   * The UnitsTestHelpers.
   *
   * @var Drupal\test_helpers\UnitTestHelpers
   */
  protected $unitTestHelpers;

  /**
   * The static storage for execute functions.
   *
   * @var array
   */
  protected $stubExecuteHandlers;

  /**
   * Constructs a new object.
   */
  public function __construct() {
    $this->unitTestHelpers = UnitTestHelpers::getInstance();
    $this->pdoMock = $this->unitTestHelpers->createMock(StubPDO::class);
    $this->connectionOptions = [
      'namespace' => 'Drupal\sqlite\Driver\Database\sqlite',
    ];
    parent::__construct($this->pdoMock, $this->connectionOptions);
  }

  private function mockExecuteForMethod($method, $arguments, $executeFunction) {
    $originalMethod = parent::$method(...$arguments);
    $class = \get_class($originalMethod);
    $mockedMethod = $this->unitTestHelpers->createPartialMockWithCostructor($class, [
      'execute',
    ], [$this, ...$arguments]);

    $executeFunction = $this->stubExecuteHandlers[$method]
      ?? $this->stubExecuteHandlers['all']
      ?? function () {
        return 'default';
      };
    UnitTestHelpers::bindClosureToClassMethod($executeFunction, $mockedMethod, 'execute');
    return $mockedMethod;
  }

  public function select($table, $alias = NULL, array $options = []) {
    $arguments = \func_get_args();
    $executeFunction = function () {
      return 123;
    };
    $select = $this->mockExecuteForMethod('select', $arguments, $executeFunction);
    return $select;
  }

  public function stubAddExecuteHandler(\Closure $executeFunction, string $method = 'all') {
    $this->stubExecuteHandlers[$method] = $executeFunction;
  }

}
+56 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\test_helpers;

use Drupal\sqlite\Driver\Database\sqlite\Connection;
use Drupal\Tests\Core\Database\Stub\StubPDO;

/**
 * The ConnectionStubFactory class.
 *
 * A stub for class Drupal\Driver\Database\fake\Connection.
 */
class DatabaseStubFactory {
  /**
   * The UnitsTestHelpers.
   *
   * @var Drupal\test_helpers\UnitTestHelpers
   */
  protected $unitTestHelpers;

  /**
   * Constructs a new object.
   */
  public function __construct() {
    $this->unitTestHelpers = UnitTestHelpers::getInstance();
    $this->pdoMock = $this->unitTestHelpers->createMock(StubPDO::class);
    $this->connectionSettings = [];
  }

  public function get() {
    /**
     * @todo Replace Drupal\sqlite\Driver\Database\sqlite\Connection to
     * Drupal\Driver\Database\fake\Connection.
     * require_once DRUPAL_ROOT . '/core/tests/fixtures/database_drivers/core/corefake/Connection.php';
     * does not work because of namespaces, check the answers
     * https://stackoverflow.com/questions/57322376/cannot-load-class-with-require-once-when-using-namespace
     * for possible solutions.
     */
    $connection = $this->unitTestHelpers->createPartialMockWithCostructor(
      Connection::class,
      ['select'],
      [$this->pdoMock, $this->connectionSettings]
    );
    return $connection;
  }

  /**
   * Registers the class as the 'database' service.
   */
  public function registerService() {
    $connection = new ConnectionStub();
    UnitTestHelpers::addToContainer('database', $connection);
    return $connection;
  }

}
+13 −21
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@

namespace Drupal\test_helpers;

use Drupal\Core\Database\Query\ConditionInterface;
use Drupal\Core\Entity\Query\QueryBase;
use Drupal\Core\Entity\Query\QueryFactoryInterface;

@@ -25,15 +26,12 @@ class EntityQueryServiceStub implements QueryFactoryInterface {
   * Gets the query for entity type.
   */
  public function get($entityType, $conjunction) {
    $entityTypeId = $entityType->id();
    if (isset($this->executeFunctions[$entityTypeId][$conjunction])) {
      $executeFunction = $this->executeFunctions[$entityTypeId][$conjunction];
    }
    else {
      $executeFunction = function () {
    $executeFunction =
      $this->executeFunctions[$entityType->id()]
      ?? $this->executeFunctions['all']
      ?? function () {
        return [];
      };
    }
    $query = $this->queryStubFactory->get($entityType, $conjunction, $executeFunction);
    return $query;
  }
@@ -42,25 +40,19 @@ class EntityQueryServiceStub implements QueryFactoryInterface {
   * Gets the aggregate query for entity type.
   */
  public function getAggregate($entityType, $conjunction) {
    $entityTypeId = $entityType->id();
    if (isset($this->executeFunctions[$entityTypeId][$conjunction])) {
      $executeFunction = $this->executeFunctions[$entityTypeId][$conjunction];
    }
    else {
      $executeFunction = function () {
        return [];
      };
    }
    // @todo Implement a getAggregate call.
    $query = $this->queryStubFactory->get($entityType, $conjunction, $executeFunction);
    return $query;
    // @todo Implement a custom getAggregate call.
    return $this->get($entityType, $conjunction);
  }

  /**
   * Adds an execute callback function to the particular entity type.
   */
  public function stubAddExecuteFunction(string $entityTypeId, string $conjunction, callable $function) {
    $this->executeFunctions[$entityTypeId][$conjunction] = $function;
  public function stubAddExecuteHandler(callable $function, string $entityTypeId = 'all') {
    $this->executeFunctions[$entityTypeId] = $function;
  }

  public function stubCheckConditionsMatch(ConditionInterface $conditionsExpected, $onlyListed = FALSE) {
    return UnitTestHelpers::matchConditions($conditionsExpected, $this->condition, $onlyListed);
  }

}
+1 −52
Original line number Diff line number Diff line
@@ -60,61 +60,10 @@ class EntityQueryStubFactory {

    UnitTestHelpers::bindClosureToClassMethod($executeFunction, $queryStub, 'execute');
    UnitTestHelpers::bindClosureToClassMethod(function (Condition $conditionsExpected, $onlyListed = FALSE) {
      return EntityQueryStubFactory::matchConditions($conditionsExpected, $this->condition, $onlyListed);
      return UnitTestHelpers::matchConditions($conditionsExpected, $this->condition, $onlyListed);
    }, $queryStub, 'stubCheckConditionsMatch');

    return $queryStub;
  }

  /**
   * Performs matching of passed conditions with the query.
   */
  public static function matchConditions(Condition $conditionsExpectedObject, Condition $conditionsObject, $onlyListed = FALSE): bool {
    if (strcasecmp($conditionsObject->getConjunction(), $conditionsExpectedObject->getConjunction()) != 0) {
      return FALSE;
    }
    $conditions = $conditionsObject->conditions();
    $conditionsExpected = $conditionsExpectedObject->conditions();
    $conditionsFound = [];
    foreach ($conditions as $condition) {
      foreach ($conditionsExpected as $delta => $conditionExpected) {
        if (EntityQueryStubFactory::matchCondition($conditionExpected, $condition, $onlyListed)) {
          $conditionsFound[$delta] = TRUE;
        }
      }
    }
    if (count($conditionsFound) != count($conditionsExpected)) {
      return FALSE;
    }
    if ($onlyListed && (count($conditions) != count($conditionsExpected))) {
      return FALSE;
    }
    return TRUE;
  }

  /**
   * Performs matching of a single condition with expected.
   */
  public static function matchCondition(array $conditionExpected, array $conditionExists, $onlyListed = FALSE): bool {
    if (is_object($conditionExists['field'] ?? NULL)) {
      if (!is_object($conditionExpected['field'] ?? NULL)) {
        return FALSE;
      }
      return self::matchConditions($conditionExpected['field'], $conditionExists['field'], $onlyListed);
    }
    if (($conditionExpected['field'] ?? NULL) != ($conditionExists['field'] ?? NULL)) {
      return FALSE;
    }
    if (($conditionExpected['value'] ?? NULL) != ($conditionExists['value'] ?? NULL)) {
      return FALSE;
    }
    if (($conditionExpected['operator'] ?? NULL) != ($conditionExists['operator'] ?? NULL)) {
      return FALSE;
    }
    if (($conditionExpected['langcode'] ?? NULL) != ($conditionExists['langcode'] ?? NULL)) {
      return FALSE;
    }
    return TRUE;
  }

}
+29 −0
Original line number Diff line number Diff line
<?php

namespace Drupal\test_helpers;

use Drupal\Core\Entity\EntityTypeManager;

/**
 * The EntityTypeManagerStub class for internal usage only.
 *
 * This is an utility class for creating a partial mock with required interface.
 */
class EntityTypeManagerStub extends EntityTypeManager implements EntityTypeManagerStubInterface {

  public function stubAddDefinition(string $pluginId, object $definition = NULL, $forceOverride = FALSE) {
  }

  public function stubGetOrCreateHandler(string $handlerType, string $entityTypeId, object $handler = NULL, $forceOverride = FALSE) {
  }

  public function stubGetOrCreateStorage(string $entityClass, object $storage = NULL, $forceOverride = FALSE) {
  }

  public function stubInit() {
  }

  public function stubReset() {
  }

}
Loading