search_api_meilisearch-1.0.x-dev/tests/src/Kernel/Parser/StringExpressionFilterParserTest.php

tests/src/Kernel/Parser/StringExpressionFilterParserTest.php
<?php

namespace Drupal\Tests\search_api_meilisearch\Kernel\Parser;

use Drupal\KernelTests\KernelTestBase;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\Query\ConditionGroup;
use Drupal\search_api\Query\ConditionGroupInterface;
use Drupal\search_api_meilisearch\Parser\FilterParserInterface;
use PHPUnit\Framework\Attributes\Group;

/**
 * Provides tests for string expression filter parser.
 *
 * @group search_api_meilisearch
 */
#[Group('search_api_meilisearch')]
class StringExpressionFilterParserTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'search_api',
    'search_api_meilisearch',
  ];

  /**
   * The parser service.
   *
   * @var \Drupal\search_api_meilisearch\Parser\FilterParserInterface
   */
  protected FilterParserInterface $parser;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->parser = $this->container->get('search_api_meilisearch.filter_parser');
  }

  /**
   * Tests parsing string expression filters from condition groups.
   */
  public function testParsingConditionGroups(): void {
    // AND group without conditions.
    $this->assertConditionGroupParsed(new ConditionGroup(), '');

    // AND group with sub condition group without conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addConditionGroup(new ConditionGroup()),
      ''
    );

    // AND group with one condition.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addCondition('name', 'test'),
      'name = "test"'
    );

    // AND group with one condition value with spaces.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addCondition('name', 'a test value'),
      'name = "a test value"'
    );

    // AND group with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addCondition('name', 'test')
        ->addCondition('body', 'test'),
      'name = "test" AND body = "test"'
    );

    // OR group with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup('OR'))
        ->addCondition('name', 'test')
        ->addCondition('body', 'test'),
      'name = "test" OR body = "test"'
    );

    // AND group with AND subgroup with one condition.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addConditionGroup(
        (new ConditionGroup())->addCondition('name', 'test')
      ),
      'name = "test"'
    );

    // AND group with OR subgroup with one condition.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addConditionGroup(
        (new ConditionGroup('OR'))->addCondition('name', 'test')
      ),
      'name = "test"'
    );

    // AND group with AND subgroup with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addConditionGroup(
        (new ConditionGroup())
          ->addCondition('name', 'test')
          ->addCondition('body', 'test')
      ),
      'name = "test" AND body = "test"'
    );

    // AND group with OR subgroup with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())->addConditionGroup(
        (new ConditionGroup('OR'))
          ->addCondition('name', 'test')
          ->addCondition('body', 'test')
      ),
      'name = "test" OR body = "test"'
    );

    // AND group with two AND subgroups each with one condition.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addConditionGroup((new ConditionGroup())->addCondition('name', 'test'))
        ->addConditionGroup((new ConditionGroup())->addCondition('body', 'test')),
      'name = "test" AND body = "test"'
    );

    // OR group with two AND subgroups each with one condition.
    $this->assertConditionGroupParsed(
      (new ConditionGroup('OR'))
        ->addConditionGroup((new ConditionGroup())->addCondition('name', 'test'))
        ->addConditionGroup((new ConditionGroup())->addCondition('body', 'test')),
      'name = "test" OR body = "test"'
    );

    // AND group with two AND subgroups each with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test')
            ->addCondition('body', 'test')
        )
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test1')
            ->addCondition('body', 'test1')
        ),
      'name = "test" AND body = "test" AND name = "test1" AND body = "test1"'
    );

    // AND group with two OR subgroups each with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addConditionGroup(
          (new ConditionGroup('OR'))
            ->addCondition('name', 'test')
            ->addCondition('body', 'test')
        )
        ->addConditionGroup(
          (new ConditionGroup('OR'))
            ->addCondition('name', 'test1')
            ->addCondition('body', 'test1')
        ),
      '(name = "test" OR body = "test") AND (name = "test1" OR body = "test1")'
    );

    // OR group with two AND subgroups each with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup('OR'))
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test')
            ->addCondition('body', 'test')
        )
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test1')
            ->addCondition('body', 'test1')
        ),
      'name = "test" AND body = "test" OR name = "test1" AND body = "test1"'
    );

    // AND group with one condition and two AND subgroups each with two
    // conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addCondition('name', 'test_')
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test')
            ->addCondition('body', 'test')
        )
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test1')
            ->addCondition('body', 'test1')
        ),
      'name = "test_" AND name = "test" AND body = "test" AND name = "test1" AND body = "test1"'
    );

    // Complex deeply nested group.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addCondition('name', 'test test')
        ->addConditionGroup(
          (new ConditionGroup('OR'))
            ->addCondition('name', 'test')
            ->addConditionGroup(
              (new ConditionGroup())
                ->addCondition('count', 5, '>=')
                ->addConditionGroup(
                  (new ConditionGroup('OR'))
                    ->addCondition('name', 'test1')
                    ->addCondition('body', 'test1')
                )
            )
        )
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test2')
            ->addCondition('body', 'test2')
            ->addCondition('id', [1, 3], 'IN')
        ),
      'name = "test test" AND (name = "test" OR count >= 5 AND (name = "test1" OR body = "test1")) AND name = "test2" AND body = "test2" AND (id = 1 OR id = 3)'
    );

    // Deeply nested group with single condition.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addConditionGroup(
          (new ConditionGroup())
            ->addConditionGroup(
              (new ConditionGroup())
                ->addConditionGroup(
                  (new ConditionGroup())
                    ->addCondition('name', 'test')
                )
            )
        ),
      'name = "test"'
    );

    // Condition group mixed with condition and subgroup.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addCondition('name', 'test')
        ->addConditionGroup(
          (new ConditionGroup('OR'))
            ->addCondition('name', 'test1')
            ->addCondition('name', 'test2')
        ),
      'name = "test" AND (name = "test1" OR name = "test2")'
    );

    // OR group with a condition with AND subgroup with two conditions.
    $this->assertConditionGroupParsed(
      (new ConditionGroup('OR'))
        ->addCondition('name', 'test')
        ->addConditionGroup(
          (new ConditionGroup())
            ->addCondition('name', 'test1')
            ->addCondition('name', 'test2')
        ),
      'name = "test" OR name = "test1" AND name = "test2"'
    );

    // AND group with two conditions, one condition with IN operator.
    $this->assertConditionGroupParsed(
      (new ConditionGroup())
        ->addCondition('name', ['test1', 'test2', 'test3'], 'IN')
        ->addCondition('body', 'test body'),
      '(name = "test1" OR name = "test2" OR name = "test3") AND body = "test body"'
    );
  }

  /**
   * Tests parsing filters with various operators in condition group.
   */
  public function testParsingOperators(): void {
    // Greater than operator.
    $this->assertOperatorParsed('count', '>', '0', 'count > 0');

    // Greater than equal operator.
    $this->assertOperatorParsed('count', '>=', 0, 'count >= 0');

    // Less than operator.
    $this->assertOperatorParsed('count', '<', '0', 'count < 0');

    // Less than equal operator.
    $this->assertOperatorParsed('count', '<=', '0', 'count <= 0');

    // Not equal operator.
    $this->assertOperatorParsed('count', '<>', '0', 'count != 0');

    // BETWEEN operator.
    $this->assertOperatorParsed('count', 'BETWEEN', [0, 10], 'count 0 TO 10');

    // NOT BETWEEN operator.
    $this->assertOperatorParsed('count', 'NOT BETWEEN', [0, 10], '(count < 0 OR count > 10)');

    // IN operator.
    $this->assertOperatorParsed('id', 'IN', [1, 3, 4], '(id = 1 OR id = 3 OR id = 4)');

    // IN operator multi word.
    $this->assertOperatorParsed(
      'category',
      'IN',
      ['world today', 'politics', 'sport'],
      '(category = "world today" OR category = "politics" OR category = "sport")'
    );

    // NOT IN operator.
    $this->assertOperatorParsed('id', 'NOT IN', [1, 3, 4], '(id != 1 AND id != 3 AND id != 4)');

    // Is null operator.
    $this->assertOperatorParsed('body', '=', NULL, "body NOT EXISTS");

    // Is not null operator.
    $this->assertOperatorParsed('body', '<>', NULL, "body EXISTS");
  }

  /**
   * Tests parsing filters with boolean field operator.
   */
  public function testBooleanOperator(): void {
    $index = Index::create([
      'id' => 'test',
      'field_settings' => [
        'field_boolean' => [
          'type' => 'boolean',
        ],
      ],
    ]);

    $conditionGroup = new ConditionGroup();
    $conditionGroup->addCondition('field_boolean', TRUE, '=');
    $conditionGroup->addCondition('field_boolean', '1', '=');
    $conditionGroup->addCondition('field_boolean', 1, '=');
    $result = $this->parser->parseExpression($conditionGroup, $index);

    $expected = 'field_boolean = "true" AND field_boolean = "true" AND field_boolean = "true"';
    $this->assertEquals($expected, $result);

    $conditionGroup = new ConditionGroup();
    $conditionGroup->addCondition('field_boolean', FALSE, '=');
    $conditionGroup->addCondition('field_boolean', '0', '=');
    $conditionGroup->addCondition('field_boolean', 0, '=');
    $result = $this->parser->parseExpression($conditionGroup, $index);

    $expected = 'field_boolean = "false" AND field_boolean = "false" AND field_boolean = "false"';
    $this->assertEquals($expected, $result);
  }

  /**
   * Parses the condition group and asserts the result.
   *
   * @param \Drupal\search_api\Query\ConditionGroupInterface $conditionGroup
   *   The condition group to parse.
   * @param string $expected
   *   The expected result.
   */
  protected function assertConditionGroupParsed(ConditionGroupInterface $conditionGroup, string $expected): void {
    $result = $this->parser->parseExpression($conditionGroup, Index::create(['id' => 'test']));
    $this->assertEquals($expected, $result);
  }

  /**
   * Parses operator and asserts the result.
   *
   * @param string $field
   *   The field name.
   * @param string $operator
   *   The condition operator.
   * @param string|array $value
   *   The field value.
   * @param string $expected
   *   Expected filter expression.
   */
  protected function assertOperatorParsed(string $field, string $operator, $value, string $expected) {
    $conditionGroup = new ConditionGroup();
    $conditionGroup->addCondition($field, $value, $operator);
    $result = $this->parser->parseExpression($conditionGroup, Index::create(['id' => 'test']));

    $this->assertEquals($expected, $result);
  }

}

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

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