!!!|Fix unit tests for CMS 9 + PHP 7.3

* Add some more checks to remove PHP warnings / notices.
* Adjust code to match CMS 9 API / interfaces.
* Do no longer use "query" but only searchTerm, as query is internal
  API.
This commit is contained in:
Daniel Siepmann 2019-05-05 12:00:40 +02:00
parent 4d976f6294
commit b88869ecaa
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
12 changed files with 293 additions and 191 deletions

View file

@ -23,9 +23,9 @@ namespace Codappix\SearchCore\Connection;
use Codappix\SearchCore\Connection\ConnectionInterface;
use Codappix\SearchCore\Connection\FacetRequestInterface;
use Codappix\SearchCore\Domain\Search\SearchService;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use TYPO3\CMS\Extbase\Persistence\QueryResultInterface;
interface SearchRequestInterface extends QueryInterface
interface SearchRequestInterface extends QueryResultInterface
{
/**
* Returns the actual string the user searched for.
@ -48,6 +48,14 @@ interface SearchRequestInterface extends QueryInterface
*/
public function getFacets() : array;
public function setLimit(int $limit);
public function setOffset(int $offset);
public function getLimit() : int;
public function getOffset() : int;
/**
* Workaround for paginate widget support which will
* use the request to build another search.

View file

@ -41,6 +41,9 @@ class GeoPointProcessor implements ProcessorInterface
protected function isApplyable(array $record, array $configuration) : bool
{
if (!isset($configuration['lat']) || !isset($configuration['lon'])) {
return false;
}
if (!isset($record[$configuration['lat']])
|| !is_numeric($record[$configuration['lat']])
|| trim($record[$configuration['lat']]) === ''

View file

@ -0,0 +1,198 @@
<?php
namespace Codappix\SearchCore\Domain\Model;
/*
* Copyright (C) 2019 Daniel Siepmann <coding@daniel-siepmann.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
/**
* Extbase QueryInterface
* Current implementation covers only paginate widget support.
*/
class Query implements QueryInterface
{
/**
* @var int
*/
private $offset = 0;
/**
* @var int
*/
private $limit = 10;
public function execute($returnRawQueryResult = false)
{
if (! ($this->connection instanceof ConnectionInterface)) {
throw new \InvalidArgumentException(
'Connection was not set before, therefore execute can not work. Use `setConnection` before.',
1502197732
);
}
if (! ($this->searchService instanceof SearchService)) {
throw new \InvalidArgumentException(
'SearchService was not set before, therefore execute can not work. Use `setSearchService` before.',
1520325175
);
}
return $this->searchService->processResult($this->connection->search($this));
}
public function setLimit($limit)
{
$this->limit = (int) $limit;
return $this;
}
public function setOffset($offset)
{
$this->offset = (int) $offset;
return $this;
}
public function getLimit()
{
return $this->limit;
}
public function getOffset()
{
return $this->offset;
}
public function getSource()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196146);
}
public function setOrderings(array $orderings)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196163);
}
public function matching($constraint)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196197);
}
public function logicalAnd($constraint1)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
}
public function logicalOr($constraint1)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196198);
}
public function logicalNot(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
}
public function equals($propertyName, $operand, $caseSensitive = true)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196199);
}
public function like($propertyName, $operand, $caseSensitive = true)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196199);
}
public function contains($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196200);
}
public function in($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196167);
}
public function lessThan($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196201);
}
public function lessThanOrEqual($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
public function greaterThan($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196202);
}
public function greaterThanOrEqual($propertyName, $operand)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
public function getType()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196203);
}
public function setQuerySettings(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
public function getQuerySettings()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196205);
}
public function count()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196169);
}
public function getOrderings()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196206);
}
public function getConstraint()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196171);
}
public function isEmpty($propertyName)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196207);
}
public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196172);
}
public function getStatement()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196208);
}
}

View file

@ -23,7 +23,9 @@ namespace Codappix\SearchCore\Domain\Model;
use Codappix\SearchCore\Connection\ConnectionInterface;
use Codappix\SearchCore\Connection\FacetRequestInterface;
use Codappix\SearchCore\Connection\SearchRequestInterface;
use Codappix\SearchCore\Domain\Model\Query;
use Codappix\SearchCore\Domain\Search\SearchService;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
/**
* Represents a search request used to process an actual search.
@ -35,7 +37,7 @@ class SearchRequest implements SearchRequestInterface
*
* @var string
*/
protected $query = '';
protected $queryString = '';
/**
* @var array
@ -48,14 +50,9 @@ class SearchRequest implements SearchRequestInterface
protected $facets = [];
/**
* @var int
* @var Query
*/
protected $offset = 0;
/**
* @var int
*/
protected $limit = 10;
private $query;
/**
* Used for QueryInterface implementation to allow execute method to work.
@ -72,19 +69,15 @@ class SearchRequest implements SearchRequestInterface
/**
* @param string $query
*/
public function __construct(string $query = '')
public function __construct(string $queryString = '')
{
$this->query = $query;
}
public function getQuery() : string
{
return $this->query;
$this->queryString = $queryString;
$this->query = new Query();
}
public function getSearchTerm() : string
{
return $this->query;
return $this->queryString;
}
/**
@ -94,7 +87,8 @@ class SearchRequest implements SearchRequestInterface
{
$filter = \TYPO3\CMS\Core\Utility\ArrayUtility::removeArrayEntryByValue($filter, '');
$this->filter = \TYPO3\CMS\Core\Utility\ArrayUtility::filterRecursive($filter, function ($value) {
return !empty($value);
return (!is_array($value) && trim($value) !== '')
|| is_array($value) && count($value) !== 0;
});
}
@ -138,157 +132,94 @@ class SearchRequest implements SearchRequestInterface
$this->searchService = $searchService;
}
// Extbase QueryInterface
// Current implementation covers only paginate widget support.
public function execute($returnRawQueryResult = false)
public function setLimit(int $limit)
{
if (! ($this->connection instanceof ConnectionInterface)) {
throw new \InvalidArgumentException(
'Connection was not set before, therefore execute can not work. Use `setConnection` before.',
1502197732
);
}
if (! ($this->searchService instanceof SearchService)) {
throw new \InvalidArgumentException(
'SearchService was not set before, therefore execute can not work. Use `setSearchService` before.',
1520325175
);
}
return $this->searchService->processResult($this->connection->search($this));
}
public function setLimit($limit)
{
$this->limit = (int) $limit;
$this->query->setLimit($limit);
return $this;
}
public function setOffset($offset)
public function setOffset(int $offset)
{
$this->offset = (int) $offset;
$this->query->setOffset($offset);
return $this;
}
public function getLimit()
public function getLimit() : int
{
return $this->limit;
return $this->query->getLimit();
}
public function getOffset()
public function getOffset() : int
{
return $this->offset;
return $this->query->getOffset();
}
public function getSource()
// Implementation of QueryResultInterface
public function getQuery(): QueryInterface
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196146);
return $this->query;
}
public function setOrderings(array $orderings)
/**
* Returns the first object in the result set
*
* @return object
*/
public function getFirst()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196163);
}
public function matching($constraint)
/**
* Returns an array with the objects in the result set
*
* @return array
*/
public function toArray()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196197);
}
public function logicalAnd($constraint1)
public function count(): int
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
}
public function logicalOr($constraint1)
public function current(): mixed
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196198);
}
public function logicalNot(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\ConstraintInterface $constraint)
public function key()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196166);
}
public function equals($propertyName, $operand, $caseSensitive = true)
public function next(): void
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196199);
}
public function contains($propertyName, $operand)
public function rewind(): void
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196200);
}
public function in($propertyName, $operand)
public function valid(): bool
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196167);
}
public function lessThan($propertyName, $operand)
public function offsetExists($offset): bool
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196201);
// TODO: Implement
return false;
}
public function lessThanOrEqual($propertyName, $operand)
public function offsetGet($offset): mixed
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
public function greaterThan($propertyName, $operand)
public function offsetSet($offset, $value) : void
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196202);
}
public function greaterThanOrEqual($propertyName, $operand)
public function offsetUnset($offset): void
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
public function getType()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196203);
}
public function setQuerySettings(\TYPO3\CMS\Extbase\Persistence\Generic\QuerySettingsInterface $querySettings)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196168);
}
public function getQuerySettings()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196205);
}
public function count()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196169);
}
public function getOrderings()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196206);
}
public function getConstraint()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196171);
}
public function isEmpty($propertyName)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196207);
}
public function setSource(\TYPO3\CMS\Extbase\Persistence\Generic\Qom\SourceInterface $source)
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196172);
}
public function getStatement()
{
throw new \BadMethodCallException('Method is not implemented yet.', 1502196208);
}
}

View file

@ -103,8 +103,6 @@ class SearchResult implements SearchResultInterface
public function next()
{
++$this->position;
return $this->current();
}
public function key()

View file

@ -85,7 +85,7 @@ class QueryFactory
protected function addSize(SearchRequestInterface $searchRequest, array &$query)
{
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'from' => $searchRequest->getOffset(),
'size' => $searchRequest->getLimit(),
]);
@ -108,7 +108,7 @@ class QueryFactory
$matchExpression['minimum_should_match'] = $minimumShouldMatch;
}
$query = ArrayUtility::setValueByPath($query, 'query.bool.must.0.multi_match', $matchExpression);
$query = ArrayUtility::setValueByPath($query, 'query.bool.must.0.multi_match', $matchExpression, '.');
}
protected function addBoosts(SearchRequestInterface $searchRequest, array &$query)
@ -137,7 +137,7 @@ class QueryFactory
}
if (!empty($boostQueryParts)) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'query' => [
'bool' => [
'should' => $boostQueryParts,
@ -149,6 +149,10 @@ class QueryFactory
protected function addFactorBoost(array &$query)
{
if (!isset($query['query'])) {
return;
}
try {
$query['query'] = [
'function_score' => [
@ -164,7 +168,7 @@ class QueryFactory
protected function addFields(SearchRequestInterface $searchRequest, array &$query)
{
try {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'stored_fields' => GeneralUtility::trimExplode(
',',
$this->configuration->get('searching.fields.stored_fields'),
@ -183,7 +187,7 @@ class QueryFactory
);
$scriptFields = $this->configurationUtility->filterByCondition($scriptFields);
if ($scriptFields !== []) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, ['script_fields' => $scriptFields]);
ArrayUtility::mergeRecursiveWithOverrule($query, ['script_fields' => $scriptFields]);
}
} catch (InvalidArgumentException $e) {
// Nothing configured
@ -196,7 +200,7 @@ class QueryFactory
$sorting = $this->configurationUtility->replaceArrayValuesWithRequestContent($searchRequest, $sorting);
$sorting = $this->configurationUtility->filterByCondition($sorting);
if ($sorting !== []) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, ['sort' => $sorting]);
ArrayUtility::mergeRecursiveWithOverrule($query, ['sort' => $sorting]);
}
}
@ -215,7 +219,7 @@ class QueryFactory
);
}
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'query' => [
'bool' => [
'filter' => $filter,
@ -260,7 +264,7 @@ class QueryFactory
protected function addFacets(SearchRequestInterface $searchRequest, array &$query)
{
foreach ($searchRequest->getFacets() as $facet) {
$query = ArrayUtility::arrayMergeRecursiveOverrule($query, [
ArrayUtility::mergeRecursiveWithOverrule($query, [
'aggs' => [
$facet->getIdentifier() => $facet->getConfig(),
],

View file

@ -20,10 +20,10 @@ namespace Codappix\SearchCore\Tests\Unit;
* 02110-1301, USA.
*/
use TYPO3\CMS\Core\Tests\UnitTestCase as CoreTestCase;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Form\Service\TranslationService;
use TYPO3\TestingFramework\Core\Unit\UnitTestCase as CoreTestCase;
abstract class AbstractUnitTestCase extends CoreTestCase
{

View file

@ -29,6 +29,8 @@ use TYPO3\CMS\Extbase\Object\ObjectManager;
class SearchControllerTest extends AbstractUnitTestCase
{
protected $resetSingletonInstances = true;
/**
* @var SearchController
*/

View file

@ -23,6 +23,7 @@ namespace Codappix\SearchCore\Tests\Unit\DataProcessing;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\DataProcessing\TcaRelationResolvingProcessor;
use Codappix\SearchCore\Tests\Unit\AbstractUnitTestCase;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use \TYPO3\CMS\Core\Utility\GeneralUtility;
@ -51,6 +52,14 @@ class TcaRelationResolvingProcessorTest extends AbstractUnitTestCase
$this->subject = GeneralUtility::makeInstance(ObjectManager::class)
->get(TcaRelationResolvingProcessor::class);
$GLOBALS['LANG'] = $this->getMockBuilder(LanguageService::class)->getMock();
}
public function tearDown()
{
unset($GLOBALS['LANG']);
parent::tearDown();
}
/**
@ -118,6 +127,7 @@ class TcaRelationResolvingProcessorTest extends AbstractUnitTestCase
public function renderTypeInputDateTimeIsHandled()
{
$originalRecord = [
'uid' => 10,
'endtime' => 99999999999,
'starttime' => 1523010960,
];
@ -159,6 +169,7 @@ class TcaRelationResolvingProcessorTest extends AbstractUnitTestCase
$record = $this->subject->processData($originalRecord, $configuration);
$this->assertSame(
[
'uid' => '10',
'endtime' => '16-11-38 09:46',
'starttime' => 1523010960,
],

View file

@ -58,7 +58,8 @@ class SearchRequestTest extends AbstractUnitTestCase
'Single filter with empty recursive values' => [
'filter' => [
'someFilter' => [
'someKey' => '',
'firstEmptyKey' => '',
'secondEmptyKey' => '',
],
],
],
@ -80,58 +81,4 @@ class SearchRequestTest extends AbstractUnitTestCase
'Filter was not set.'
);
}
/**
* @test
*/
public function exceptionIsThrownIfSearchServiceWasNotSet()
{
$subject = new SearchRequest();
$subject->setConnection($this->getMockBuilder(ConnectionInterface::class)->getMock());
$this->expectException(\InvalidArgumentException::class);
$subject->execute();
}
/**
* @test
*/
public function exceptionIsThrownIfConnectionWasNotSet()
{
$subject = new SearchRequest();
$subject->setSearchService(
$this->getMockBuilder(SearchService::class)
->disableOriginalConstructor()
->getMock()
);
$this->expectException(\InvalidArgumentException::class);
$subject->execute();
}
/**
* @test
*/
public function executionMakesUseOfProvidedConnectionAndSearchService()
{
$searchServiceMock = $this->getMockBuilder(SearchService::class)
->disableOriginalConstructor()
->getMock();
$connectionMock = $this->getMockBuilder(ConnectionInterface::class)
->getMock();
$searchResultMock = $this->getMockBuilder(SearchResultInterface::class)
->getMock();
$subject = new SearchRequest();
$subject->setSearchService($searchServiceMock);
$subject->setConnection($connectionMock);
$connectionMock->expects($this->once())
->method('search')
->with($subject)
->willReturn($searchResultMock);
$searchServiceMock->expects($this->once())
->method('processResult')
->with($searchResultMock);
$subject->execute();
}
}

View file

@ -156,9 +156,8 @@ class QueryFactoryTest extends AbstractUnitTestCase
);
$query = $this->subject->create($searchRequest);
$this->assertSame(
null,
$query->toArray()['query']['bool']['filter'],
$this->assertTrue(
!isset($query->toArray()['query']['bool']['filter']),
'Filter was added to query, even if no filter exists.'
);
}
@ -405,7 +404,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
$query = $this->subject->create($searchRequest);
$this->assertInstanceOf(
stdClass,
\stdClass::class,
$query->toArray()['query']['match_all'],
'Empty search request does not create expected query.'
);
@ -543,7 +542,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
'config' => 'something',
],
'field2' => [
'config' => '{request.query}',
'config' => '{request.searchTerm}',
],
],
$this->throwException(new InvalidArgumentException)
@ -613,7 +612,7 @@ class QueryFactoryTest extends AbstractUnitTestCase
'config' => 'something',
],
'field2' => [
'config' => '{request.query}',
'config' => '{request.searchTerm}',
],
]
));

View file

@ -23,6 +23,7 @@ namespace Codappix\SearchCore\Tests\Unit\Domain\Search;
use Codappix\SearchCore\Configuration\ConfigurationContainerInterface;
use Codappix\SearchCore\Configuration\InvalidArgumentException;
use Codappix\SearchCore\Connection\ConnectionInterface;
use Codappix\SearchCore\Connection\SearchRequestInterface;
use Codappix\SearchCore\Connection\SearchResultInterface;
use Codappix\SearchCore\DataProcessing\Service as DataProcessorService;
use Codappix\SearchCore\Domain\Model\SearchRequest;
@ -182,7 +183,7 @@ class SearchServiceTest extends AbstractUnitTestCase
$this->connection->expects($this->once())
->method('search')
->with($this->callback(function ($searchRequest) {
->with($this->callback(function (SearchRequestInterface $searchRequest) {
return $searchRequest->getFilter() === ['property' => '0'];
}))
->willReturn($this->getMockBuilder(SearchResultInterface::class)->getMock());