search_api-8.x-1.15/tests/src/Unit/Processor/FieldsProcessorPluginBaseTest.php

tests/src/Unit/Processor/FieldsProcessorPluginBaseTest.php
<?php

namespace Drupal\Tests\search_api\Unit\Processor;

use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\Field;
use Drupal\search_api\Plugin\search_api\data_type\value\TextToken;
use Drupal\search_api\Plugin\search_api\data_type\value\TextValue;
use Drupal\search_api\Query\Condition;
use Drupal\Tests\UnitTestCase;

/**
 * Tests the base class for fields-based processors.
 *
 * @coversDefaultClass \Drupal\search_api\Processor\FieldsProcessorPluginBase
 *
 * @group search_api
 */
class FieldsProcessorPluginBaseTest extends UnitTestCase {

  use TestItemsTrait;

  /**
   * A search index mock to use in this test case.
   *
   * @var \Drupal\search_api\IndexInterface|\PHPUnit_Framework_MockObject_MockObject
   */
  protected $index;

  /**
   * The class under test.
   *
   * @var \Drupal\Tests\search_api\Unit\Processor\TestFieldsProcessorPlugin
   */
  protected $processor;

  /**
   * Creates a new processor object for use in the tests.
   */
  public function setUp() {
    parent::setUp();

    $this->setUpMockContainer();
    $this->index = $this->createMock(IndexInterface::class);
    $this->index->expects($this->any())
      ->method('status')
      ->will($this->returnValue(TRUE));
    $items = $this->getTestItem();
    $fields = $items[$this->itemIds[0]]->getFields();
    $this->index->expects($this->any())
      ->method('getFields')
      ->will($this->returnValue($fields));

    $this->processor = new TestFieldsProcessorPlugin(['#index' => $this->index], '', []);
  }

  /**
   * Tests whether the processor handles field changes correctly.
   */
  public function testFieldRenaming() {
    $configuration['fields'] = [
      'float_field',
      'float_field_2',
      'string_field',
      'text_field',
      'text_field_2',
    ];
    $this->processor->setConfiguration($configuration);

    $override = function ($type) {
      return in_array($type, ['string', 'text']);
    };
    $this->processor->setMethodOverride('testType', $override);

    $this->index->method('getFieldRenames')
      ->willReturn([
        'float_field' => 'foo',
        'text_field' => 'bar',
      ]);
    $this->index->method('getField')
      ->willReturnMap([
        ['float_field', (new Field($this->index, ''))->setType('float')],
        ['float_field_2', NULL],
        ['string_field', (new Field($this->index, ''))->setType('string')],
        ['bar', (new Field($this->index, ''))->setType('text')],
        ['text_field_2', (new Field($this->index, ''))->setType('text')],
      ]);

    $this->processor->preIndexSave();

    $fields = $this->processor->getConfiguration()['fields'];
    sort($fields);
    $expected = [
      'bar',
      'string_field',
      'text_field_2',
    ];
    $this->assertEquals($expected, $fields);
  }

  /**
   * Tests whether the "Enable on all supported fields" option works correctly.
   *
   * We want the option to make sure that all supported fields are automatically
   * added to the processor, without adding unsupported ones or keeping old ones
   * that were removed.
   */
  public function testAllFields() {
    $configuration['all_fields'] = TRUE;
    $configuration['fields'] = [
      'float_field',
      'string_field',
    ];
    $this->processor->setConfiguration($configuration);

    $override = function ($type) {
      return in_array($type, ['string', 'text']);
    };
    $this->processor->setMethodOverride('testType', $override);

    // Since it's not possible to override an already specified method on a mock
    // object, we need to create a new mock object for the index in this test.
    /** @var \Drupal\search_api\IndexInterface|\PHPUnit_Framework_MockObject_MockObject $index */
    $index = $this->createMock(IndexInterface::class);
    $index->method('getFields')->willReturn([
      'float_field' => (new Field($this->index, ''))->setType('float'),
      'float_field_2' => (new Field($this->index, ''))->setType('float'),
      'string_field' => (new Field($this->index, ''))->setType('string'),
      'text_field' => (new Field($this->index, ''))->setType('text'),
      'text_field_2' => (new Field($this->index, ''))->setType('text'),
    ]);
    $this->processor->setIndex($index);

    $this->processor->preIndexSave();

    $fields = $this->processor->getConfiguration()['fields'];
    sort($fields);
    $expected = [
      'string_field',
      'text_field',
      'text_field_2',
    ];
    $this->assertEquals($expected, $fields);
  }

  /**
   * Tests whether the default implementation of testType() works correctly.
   */
  public function testTestTypeDefault() {
    $items = $this->getTestItem();
    $this->processor->preprocessIndexItems($items);
    $this->assertFieldsProcessed($items, ['text_field', 'string_field']);
  }

  /**
   * Tests whether overriding of testType() works correctly.
   */
  public function testTestTypeOverride() {
    $override = function ($type) {
      return \Drupal::getContainer()
        ->get('search_api.data_type_helper')
        ->isTextType($type, ['string', 'integer']);
    };
    $this->processor->setMethodOverride('testType', $override);

    $items = $this->getTestItem();
    $this->processor->preprocessIndexItems($items);
    $this->assertFieldsProcessed($items, ['string_field', 'integer_field']);
  }

  /**
   * Tests whether selecting fields works correctly.
   */
  public function testTestField() {
    // testType() shouldn't have any effect anymore when fields are configured.
    $override = function () {
      return FALSE;
    };
    $this->processor->setMethodOverride('testType', $override);
    $configuration['fields'] = ['text_field', 'float_field'];
    $this->processor->setConfiguration($configuration);

    $items = $this->getTestItem();
    $this->processor->preprocessIndexItems($items);
    $this->assertFieldsProcessed($items, ['text_field', 'float_field']);
  }

  /**
   * Tests whether overriding of processFieldValue() works correctly.
   */
  public function testProcessFieldValueOverride() {
    $override = function (&$value, &$type) {
      // Check whether the passed $type matches the one included in the value.
      if (strpos($value, "{$type}_field") !== FALSE) {
        $value = "&$value";
      }
      else {
        $value = "/$value";
      }
    };
    $this->processor->setMethodOverride('processFieldValue', $override);

    $items = $this->getTestItem();
    $this->processor->preprocessIndexItems($items);
    $this->assertFieldsProcessed($items, ['text_field', 'string_field'], '&');
  }

  /**
   * Tests whether removing values in processFieldValue() works correctly.
   */
  public function testProcessFieldRemoveValue() {
    $override = function (&$value) {
      if ($value != 'bar') {
        $value = "*$value";
      }
      else {
        $value = '';
      }
    };
    $this->processor->setMethodOverride('processFieldValue', $override);

    $fields = [
      'field1' => [
        'type' => 'string',
        'values' => [
          'foo',
          'bar',
        ],
      ],
    ];
    $items = $this->createItems($this->index, 1, $fields);

    $this->processor->preprocessIndexItems($items);

    $item_fields = $items[$this->itemIds[0]]->getFields();
    $this->assertEquals(['*foo'], $item_fields['field1']->getValues(), 'Field value was correctly removed.');
  }

  /**
   * Tests whether the processField() method operates correctly.
   */
  public function testProcessFieldsTokenized() {
    $override = function (&$value, $type) {
      switch ($type) {
        case 'integer':
          ++$value;
          return;

        case 'string':
          $value = "++$value";
          return;
      }

      if (strpos($value, ' ')) {
        $value = TestFieldsProcessorPlugin::createTokenizedText($value, 4)->getTokens();
      }
      elseif ($value == 'bar') {
        $value = TestFieldsProcessorPlugin::createTokenizedText('*bar', 2)->getTokens();
      }
      elseif ($value == 'baz') {
        $value = '';
      }
      else {
        $value = "*$value";
      }
    };
    $this->processor->setMethodOverride('processFieldValue', $override);

    $value = TestFieldsProcessorPlugin::createTokenizedText('foobar baz', 3);
    $tokens = $value->getTokens();
    $tokens[] = new TextToken('foo bar', 2);
    $value->setTokens($tokens);
    $fields = [
      'field1' => [
        'type' => 'text',
        'values' => [
          TestFieldsProcessorPlugin::createTokenizedText('foo bar baz', 3),
          $value,
          new TextValue('foo'),
          new TextValue('foo bar'),
          new TextValue('bar'),
          new TextValue('baz'),
        ],
      ],
      'field2' => [
        'type' => 'integer',
        'values' => [
          1,
          3,
        ],
      ],
      'field3' => [
        'type' => 'string',
        'values' => [
          'foo',
          'foo bar baz',
        ],
      ],
    ];
    $items = $this->createItems($this->index, 1, $fields);

    $this->processor->setConfiguration([
      'fields' => ['field1', 'field2', 'field3'],
    ]);

    $this->processor->preprocessIndexItems($items);

    $fields = $items[$this->itemIds[0]]->getFields();

    /** @var \Drupal\search_api\Plugin\search_api\data_type\value\TextValueInterface[] $values */
    $values = $fields['field1']->getValues();
    $summary = [];
    foreach ($values as $i => $value) {
      $summary[$i]['text'] = $value->toText();
      $tokens = $value->getTokens();
      if ($tokens !== NULL) {
        $summary[$i]['tokens'] = [];
        foreach ($tokens as $token) {
          $summary[$i]['tokens'][] = [
            'text' => $token->getText(),
            'boost' => $token->getBoost(),
          ];
        }
      }
    }
    $expected = [
      [
        'text' => '*foo *bar',
        'tokens' => [
          [
            'text' => '*foo',
            'boost' => 3,
          ],
          [
            'text' => '*bar',
            'boost' => 6,
          ],
        ],
      ],
      [
        'text' => '*foobar foo bar',
        'tokens' => [
          [
            'text' => '*foobar',
            'boost' => 3,
          ],
          [
            'text' => 'foo',
            'boost' => 8,
          ],
          [
            'text' => 'bar',
            'boost' => 8,
          ],
        ],
      ],
      [
        'text' => '*foo',
      ],
      [
        'text' => 'foo bar',
        'tokens' => [
          [
            'text' => 'foo',
            'boost' => 4,
          ],
          [
            'text' => 'bar',
            'boost' => 4,
          ],
        ],
      ],
      [
        'text' => '*bar',
        'tokens' => [
          [
            'text' => '*bar',
            'boost' => 2,
          ],
        ],
      ],
    ];
    $this->assertEquals($expected, $summary);

    $expected = [2, 4];
    $this->assertEquals('integer', $fields['field2']->getType());
    $this->assertEquals($expected, $fields['field2']->getValues());

    $expected = ['++foo', '++foo bar baz'];
    $this->assertEquals('string', $fields['field3']->getType());
    $this->assertEquals($expected, $fields['field3']->getValues());
  }

  /**
   * Tests whether preprocessing of queries without search keys works correctly.
   */
  public function testProcessKeysNoKeys() {
    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);

    $this->processor->preprocessSearchQuery($query);

    $this->assertNull($query->getKeys(), 'Query without keys was correctly ignored.');
  }

  /**
   * Tests whether preprocessing of simple search keys works correctly.
   */
  public function testProcessKeysSimple() {
    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $keys = &$query->getKeys();
    $keys = 'foo';

    $this->processor->preprocessSearchQuery($query);

    $this->assertEquals('*foo', $query->getKeys(), 'Search keys were correctly preprocessed.');
  }

  /**
   * Tests whether preprocessing of complex search keys works correctly.
   */
  public function testProcessKeysComplex() {
    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $keys = &$query->getKeys();
    $keys = [
      '#conjunction' => 'OR',
      'foo',
      [
        '#conjunction' => 'AND',
        'bar',
        'baz',
        '#negation' => TRUE,
      ],
    ];

    $this->processor->preprocessSearchQuery($query);

    $expected = [
      '#conjunction' => 'OR',
      '*foo',
      [
        '#conjunction' => 'AND',
        '*bar',
        '*baz',
        '#negation' => TRUE,
      ],
    ];
    $this->assertEquals($expected, $query->getKeys(), 'Search keys were correctly preprocessed.');
  }

  /**
   * Tests whether overriding of processKey() works correctly.
   */
  public function testProcessKeyOverride() {
    $override = function (&$value) {
      if ($value != 'baz') {
        $value = "&$value";
      }
      else {
        $value = '';
      }
    };
    $this->processor->setMethodOverride('processKey', $override);

    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $keys = &$query->getKeys();
    $keys = [
      '#conjunction' => 'OR',
      'foo',
      [
        '#conjunction' => 'AND',
        'bar',
        'baz',
        '#negation' => TRUE,
      ],
    ];

    $this->processor->preprocessSearchQuery($query);

    $expected = [
      '#conjunction' => 'OR',
      '&foo',
      [
        '#conjunction' => 'AND',
        '&bar',
        '#negation' => TRUE,
      ],
    ];
    $this->assertEquals($expected, $query->getKeys(), 'Search keys were correctly preprocessed.');
  }

  /**
   * Tests whether preprocessing search conditions works correctly.
   */
  public function testProcessConditions() {
    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $query->addCondition('text_field', 'foo');
    $query->addCondition('text_field', ['foo', 'bar'], 'IN');
    $query->addCondition('string_field', NULL, '<>');
    $query->addCondition('integer_field', 'bar');

    $this->processor->preprocessSearchQuery($query);

    $expected = [
      new Condition('text_field', '*foo'),
      new Condition('text_field', ['*foo', '*bar'], 'IN'),
      new Condition('string_field', 'undefined', '<>'),
      new Condition('integer_field', 'bar'),
    ];
    $this->assertEquals($expected, $query->getConditionGroup()->getConditions(), 'Conditions were preprocessed correctly.');
  }

  /**
   * Tests whether preprocessing nested search conditions works correctly.
   */
  public function testProcessConditionsNestedConditions() {
    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $conditions = $query->createConditionGroup();
    $conditions->addCondition('text_field', 'foo');
    $conditions->addCondition('text_field', ['foo', 'bar'], 'IN');
    $conditions->addCondition('string_field', NULL, '<>');
    $conditions->addCondition('integer_field', 'bar');
    $query->addConditionGroup($conditions);

    $this->processor->preprocessSearchQuery($query);

    $expected = [
      new Condition('text_field', '*foo'),
      new Condition('text_field', ['*foo', '*bar'], 'IN'),
      new Condition('string_field', 'undefined', '<>'),
      new Condition('integer_field', 'bar'),
    ];
    $this->assertEquals($expected, $query->getConditionGroup()->getConditions()[0]->getConditions(), 'Conditions were preprocessed correctly.');
  }

  /**
   * Tests whether overriding processConditionValue() works correctly.
   */
  public function testProcessConditionValueOverride() {
    $override = function (&$value) {
      if (isset($value)) {
        $value = '';
      }
    };
    $this->processor->setMethodOverride('processConditionValue', $override);

    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $query->addCondition('text_field', 'foo');
    $query->addCondition('string_field', NULL, '<>');
    $query->addCondition('integer_field', 'bar');

    $this->processor->preprocessSearchQuery($query);

    $expected = [
      new Condition('string_field', NULL, '<>'),
      new Condition('integer_field', 'bar'),
    ];
    $this->assertEquals($expected, array_merge($query->getConditionGroup()->getConditions()), 'Conditions were preprocessed correctly.');
  }

  /**
   * Tests whether overriding processConditionValue() works correctly.
   */
  public function testProcessConditionValueArrayHandling() {
    $override = function (&$value) {
      $length = strlen($value);
      if ($length == 2) {
        $value = '';
      }
      elseif ($length == 3) {
        $value .= '*';
      }
    };
    $this->processor->setMethodOverride('process', $override);

    $query = \Drupal::getContainer()
      ->get('search_api.query_helper')
      ->createQuery($this->index);
    $query->addCondition('text_field', ['a', 'b'], 'NOT IN');
    $query->addCondition('text_field', ['a', 'bo'], 'IN');
    $query->addCondition('text_field', ['ab', 'bo'], 'NOT IN');
    $query->addCondition('text_field', ['a', 'bo'], 'BETWEEN');
    $query->addCondition('text_field', ['ab', 'bo'], 'NOT BETWEEN');
    $query->addCondition('text_field', ['a', 'bar'], 'IN');
    $query->addCondition('text_field', ['abo', 'baz'], 'BETWEEN');

    $this->processor->preprocessSearchQuery($query);

    $expected = [
      new Condition('text_field', ['a', 'b'], 'NOT IN'),
      new Condition('text_field', ['a'], 'IN'),
      new Condition('text_field', ['a', 'bo'], 'BETWEEN'),
      new Condition('text_field', ['ab', 'bo'], 'NOT BETWEEN'),
      new Condition('text_field', ['a', 'bar*'], 'IN'),
      new Condition('text_field', ['abo*', 'baz*'], 'BETWEEN'),
    ];
    $this->assertEquals($expected, array_merge($query->getConditionGroup()->getConditions()), 'Conditions were preprocessed correctly.');
  }

  /**
   * Returns an array with one test item suitable for this test case.
   *
   * @param string[]|null $types
   *   (optional) The types of fields to create. Defaults to using "text",
   *   "string", "integer" and "float".
   *
   * @return \Drupal\search_api\Item\ItemInterface[]
   *   An array containing one item.
   */
  protected function getTestItem($types = NULL) {
    if ($types === NULL) {
      $types = ['text', 'string', 'integer', 'float'];
    }

    $fields = [];
    foreach ($types as $type) {
      $field_id = "{$type}_field";
      $fields[$field_id] = [
        'type' => $type,
        'values' => [
          "$field_id value 1",
          "$field_id value 2",
        ],
      ];
    }
    return $this->createItems($this->index, 1, $fields);
  }

  /**
   * Asserts that the given fields have been correctly processed.
   *
   * @param \Drupal\search_api\Item\ItemInterface[] $items
   *   An array containing one item.
   * @param string[] $processed_fields
   *   The fields which should be processed.
   * @param string $prefix
   *   (optional) The prefix that processed fields receive.
   */
  protected function assertFieldsProcessed(array $items, array $processed_fields, $prefix = "*") {
    $processed_fields = array_fill_keys($processed_fields, TRUE);
    foreach ($items as $item) {
      foreach ($item->getFields() as $field_id => $field) {
        if (!empty($processed_fields[$field_id])) {
          $expected = [
            "$prefix$field_id value 1",
            "$prefix$field_id value 2",
          ];
        }
        else {
          $expected = [
            "$field_id value 1",
            "$field_id value 2",
          ];
        }
        $this->assertEquals($expected, $field->getValues(), "Field $field_id is correct.");
      }
    }
  }

}

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

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