snapshot of https://github.com/espocrm/espocrm.git (master, 2026-09-18; blobs > 131072B omitted)
Some checks failed
Test / Test on PHP 8.3 (push) Failing after 5s
Test / Test on PHP 8.4 (push) Failing after 4s
Test / Test on PHP 8.5 (push) Failing after 3s
TypeScript static analysis / TypeScript check (push) Failing after 14s
Test Integration on PostgreSQL / Test on PHP 8.4 fix (push) Failing after 1s
Test Integration on PostgreSQL / Test on PHP 8.4 master (push) Failing after 1s
Test Integration on MySQL / Test on PHP 8.3 (push) Failing after 2s
Test Integration on MySQL / Test on PHP 8.5 (push) Failing after 1s

This commit is contained in:
upstream-mirror
2026-09-18 04:19:15 +05:00
commit 7cc7e6b4f3
10077 changed files with 900756 additions and 0 deletions

View File

@@ -0,0 +1,147 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Select\AccessControl\Applier as AccessControlFilterApplier;
use Espo\Core\Select\AccessControl\Filter as AccessControlFilter;
use Espo\Core\Select\AccessControl\FilterFactory as AccessControlFilterFactory;
use Espo\Core\Select\AccessControl\FilterResolver;
use Espo\Core\Select\AccessControl\FilterResolverFactory as AccessControlFilterResolverFactory;
use Espo\Entities\User;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class AccessControlFilterApplierTest extends TestCase
{
private $filterFactory;
private $filterResolverFactory;
private $user;
private $queryBuilder;
private $filterResolver;
private $filter;
private $mandatoryFilter;
private $entityType;
private $applier;
protected function setUp() : void
{
$this->filterFactory = $this->createMock(AccessControlFilterFactory::class);
$this->filterResolverFactory = $this->createMock(AccessControlFilterResolverFactory::class);
$this->user = $this->createMock(User::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->filterResolver = $this->createMock(FilterResolver::class);
$this->filter = $this->createMock(AccessControlFilter::class);
$this->mandatoryFilter = $this->createMock(AccessControlFilter::class);
$this->entityType = 'Test';
$this->applier = new AccessControlFilterApplier(
entityType: $this->entityType,
user: $this->user,
accessControlFilterFactory: $this->filterFactory,
accessControlFilterResolverFactory: $this->filterResolverFactory,
);
}
public function testApply1()
{
$this->initApplierTest(true, true);
$this->applier->apply($this->queryBuilder);
}
public function testApply2()
{
$this->initApplierTest(true, false);
$this->applier->apply($this->queryBuilder);
}
public function testApply3()
{
$this->initApplierTest(false, false);
$this->applier->apply($this->queryBuilder);
}
protected function initApplierTest(bool $resolve, bool $hasFilter)
{
$this->filterResolverFactory
->expects($this->once())
->method('create')
->with($this->entityType, $this->user)
->willReturn($this->filterResolver);
$filterName = null;
if ($resolve) {
$filterName = 'test';
}
$this->filterResolver
->expects($this->once())
->method('resolve')
->willReturn($filterName);
if (!$resolve) {
$this->filterFactory
->expects($this->never())
->method('has');
return;
}
$this->filterFactory
->expects($this->once())
->method('has')
->with($this->entityType, $filterName)
->willReturn($hasFilter);
if (!$hasFilter) {
$this->expectException(\RuntimeException::class);
return;
}
$this->filterFactory
->expects($this->exactly(2))
->method('create')
->willReturnMap([
[$this->entityType, $this->user, 'mandatory', $this->mandatoryFilter],
[$this->entityType, $this->user, $filterName, $this->filter],
]);
$this->filter
->expects($this->once())
->method('apply')
->with($this->queryBuilder);
}
}

View File

@@ -0,0 +1,152 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Select\Bool\Applier as BoolFilterListApplier;
use Espo\Core\Select\Bool\Filter as BoolFilter;
use Espo\Core\Select\Bool\FilterFactory as BoolFilterFactory;
use Espo\Entities\User;
use Espo\ORM\Query\Part\Where\OrGroupBuilder;
use Espo\ORM\Query\Part\WhereClause;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class BoolFilterListApplierTest extends TestCase
{
private $boolFilterFactory;
private $user;
private $queryBuilder;
private $entityType;
private $applier;
protected function setUp(): void
{
$this->boolFilterFactory = $this->createMock(BoolFilterFactory::class);
$this->user = $this->createMock(User::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->entityType = 'Test';
$this->applier = new BoolFilterListApplier(
entityType: $this->entityType,
user: $this->user,
boolFilterFactory: $this->boolFilterFactory,
);
}
public function testApply1()
{
$boolFilterList = ['test1', 'test2'];
$filter1 = $this->createFilterMock(['test' => '1']);
$filter2 = $this->createFilterMock(['test' => '2']);
$this->initApplierTest($boolFilterList, [$filter1, $filter2], [true, true]);
$this->queryBuilder
->expects($this->once())
->method('where');
$this->applier->apply($this->queryBuilder, $boolFilterList);
}
public function testApply2()
{
$boolFilterList = ['test1'];
$filter1 = $this->createFilterMock(['test' => '1']);
$this->initApplierTest($boolFilterList, [$filter1], [true]);
$this->queryBuilder
->expects($this->once())
->method('where');
$this->applier->apply($this->queryBuilder, $boolFilterList);
}
public function testApply3()
{
$boolFilterList = ['test1'];
$this->initApplierTest($boolFilterList, [null], [false]);
$this->expectException(BadRequest::class);
$this->applier->apply($this->queryBuilder, $boolFilterList);
}
protected function initApplierTest(array $filterNameList, array $filterList, array $hasList)
{
$hasMap = [];
$createMap = [];
foreach ($filterNameList as $i => $filterName) {
$hasMap[] = [$this->entityType, $filterName, $hasList[$i]];
if (!$hasList[$i]) {
continue;
}
$createMap[] = [$this->entityType, $this->user, $filterName, $filterList[$i]];
}
$this->boolFilterFactory
->expects($this->any())
->method('has')
->willReturnMap($hasMap);
$this->boolFilterFactory
->expects($this->any())
->method('create')
->willReturnMap($createMap);
}
protected function createFilterMock(array $rawWhereClause): BoolFilter
{
$filter = $this->createMock(BoolFilter::class);
$whereClause = $this->createMock(WhereClause::class);
$whereClause
->expects($this->any())
->method('getRawValue')
->willReturn($rawWhereClause);
$filter
->expects($this->any())
->method('apply')
->with($this->queryBuilder, $this->isInstanceOf(OrGroupBuilder::class));
return $filter;
}
}

View File

@@ -0,0 +1,339 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Acl\SystemRestriction;
use Espo\Core\AclManager;
use Espo\Core\Exceptions\BadRequest;
use Espo\Entities\User;
use Espo\ORM\Query\Part\OrderList;
use Espo\ORM\Query\Part\Order;
use Espo\Core\Select\Order\Applier as OrderApplier;
use Espo\Core\Select\Order\Item;
use Espo\Core\Select\Order\ItemConverter;
use Espo\Core\Select\Order\ItemConverterFactory;
use Espo\Core\Select\Order\MetadataProvider;
use Espo\Core\Select\Order\Orderer;
use Espo\Core\Select\Order\OrdererFactory;
use Espo\Core\Select\Order\Params as OrderParams;
use Espo\Core\Select\SearchParams;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class OrderApplierTest extends TestCase
{
private ?OrdererFactory $ordererFactory = null;
private $metadataProvider;
private $itemConverterFactory;
private $queryBuilder;
private $params;
private $entityType;
private $applier;
protected function setUp(): void
{
$this->metadataProvider = $this->createMock(MetadataProvider::class);
$this->itemConverterFactory = $this->createMock(ItemConverterFactory::class);
$this->ordererFactory = $this->createMock(OrdererFactory::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->params = $this->createMock(OrderParams::class);
$aclManager = $this->createMock(AclManager::class);
$user = $this->createMock(User::class);
$restriction = $this->createMock(SystemRestriction::class);
$restriction
->method('checkFieldRead')
->willReturn(true);
$this->entityType = 'Test';
$this->applier = new OrderApplier(
entityType: $this->entityType,
metadataProvider: $this->metadataProvider,
itemConverterFactory: $this->itemConverterFactory,
ordererFactory: $this->ordererFactory,
aclManager: $aclManager,
user: $user,
systemRestriction: $restriction,
);
}
public function testApplyDefault()
{
$order = SearchParams::ORDER_DESC;
$this->params
->expects($this->any())
->method('forceDefault')
->willReturn(true);
$this->params
->expects($this->any())
->method('getOrder')
->willReturn($order);
$this->metadataProvider
->expects($this->any())
->method('getDefaultOrderBy')
->with($this->entityType)
->willReturn('testField');
$this->initApplyOrderTest('testField', $order, 'varchar');
$this->applier->apply($this->queryBuilder, $this->params);
}
public function testApply1()
{
$order = SearchParams::ORDER_DESC;
$orderBy = 'testField';
$this->params
->expects($this->any())
->method('forceDefault')
->willReturn(false);
$this->params
->expects($this->any())
->method('getOrder')
->willReturn($order);
$this->params
->expects($this->any())
->method('getOrderBy')
->willReturn($orderBy);
$this->metadataProvider
->expects($this->any())
->method('getDefaultOrderBy')
->with($this->entityType)
->willReturn($orderBy);
$this->initApplyOrderTest($orderBy, $order, 'varchar');
$this->applier->apply($this->queryBuilder, $this->params);
}
public function testApplyWithConverter()
{
$order = SearchParams::ORDER_DESC;
$orderBy = 'testField';
$this->params
->expects($this->any())
->method('forceDefault')
->willReturn(false);
$this->params
->expects($this->any())
->method('getOrder')
->willReturn($order);
$this->params
->expects($this->any())
->method('getOrderBy')
->willReturn($orderBy);
$this->metadataProvider
->expects($this->any())
->method('getDefaultOrderBy')
->with($this->entityType)
->willReturn($orderBy);
$converterResult = OrderList::create([
Order::fromString('hello')->withDesc(),
]);
$this->initApplyOrderTest($orderBy, $order, 'varchar', $converterResult);
$this->applier->apply($this->queryBuilder, $this->params);
}
public function testApplyNotExisting()
{
$order = SearchParams::ORDER_DESC;
$orderBy = 'testField';
$this->params
->expects($this->any())
->method('forceDefault')
->willReturn(false);
$this->params
->expects($this->any())
->method('getOrder')
->willReturn($order);
$this->params
->expects($this->any())
->method('getOrderBy')
->willReturn($orderBy);
$this->metadataProvider
->expects($this->any())
->method('getDefaultOrderBy')
->with($this->entityType)
->willReturn($orderBy);
$this->initApplyOrderTest($orderBy, $order, 'varchar', null, true);
$this->applier->apply($this->queryBuilder, $this->params);
}
public function testApplyWithOrderer()
{
$order = SearchParams::ORDER_DESC;
$orderBy = 'testField';
$this->params
->expects($this->any())
->method('forceDefault')
->willReturn(false);
$this->params
->expects($this->any())
->method('getOrder')
->willReturn($order);
$this->params
->expects($this->any())
->method('getOrderBy')
->willReturn($orderBy);
$this->ordererFactory
->expects($this->once())
->method('has')
->with($this->entityType, $orderBy)
->willReturn(true);
$orderer = $this->createMock(Orderer::class);
$this->ordererFactory
->expects($this->once())
->method('create')
->with($this->entityType, $orderBy)
->willReturn($orderer);
$orderer
->expects($this->once())
->method('apply')
->with($this->queryBuilder, Item::create($orderBy, $order));
$this->applier->apply($this->queryBuilder, $this->params);
}
protected function initApplyOrderTest(
string $orderBy,
string $order,
string $fieldType,
?OrderList $converterResult = null,
bool $notExisting = false
) {
$this->metadataProvider
->expects($this->any())
->method('getFieldType')
->with($this->entityType, $orderBy)
->willReturn($fieldType);
$this->itemConverterFactory
->expects($this->once())
->method('has')
->with($this->entityType, $orderBy)
->willReturn(
(bool) $converterResult
);
$this->metadataProvider
->expects($this->any())
->method('hasAttribute')
->willReturnMap(
[
[$this->entityType, $orderBy, !$notExisting],
[$this->entityType, 'id', true],
]
);
if ($converterResult) {
$converter = $this->createMock(ItemConverter::class);
$this->itemConverterFactory
->expects($this->once())
->method('create')
->with($this->entityType, $orderBy)
->willReturn($converter);
$item = Item::create($orderBy, $order);
$converter
->expects($this->once())
->method('convert')
->with($item)
->willReturn($converterResult);
} else {
if ($notExisting) {
$this->expectException(BadRequest::class);
return;
}
$this->metadataProvider
->expects($this->once())
->method('isAttributeParamUniqueTrue')
->with($this->entityType, $orderBy)
->willReturn(false);
}
$expectedOrderBy = ($converterResult ? $this->orderListToArray($converterResult): null)
?? [[$orderBy, $order]];
$expectedOrderBy[] = ['id', $order];
$this->queryBuilder
->expects($this->once())
->method('order')
->with($expectedOrderBy);
}
private function orderListToArray(OrderList $orderList): array
{
$list = [];
foreach ($orderList as $order) {
$list[] = [
$order->getExpression()->getValue(),
$order->getDirection(),
];
}
return $list;
}
}

View File

@@ -0,0 +1,108 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Exceptions\BadRequest;
use Espo\Core\Select\Primary\Applier as PrimaryFilterApplier;
use Espo\Core\Select\Primary\Filter as PrimaryFilter;
use Espo\Core\Select\Primary\FilterFactory as PrimaryFilterFactory;
use Espo\Entities\User;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class PrimaryFilterApplierTest extends TestCase
{
private $filterFactory;
private $user;
private $queryBuilder;
private $entityType;
private $applier;
protected function setUp(): void
{
$this->filterFactory = $this->createMock(PrimaryFilterFactory::class);
$this->user = $this->createMock(User::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->entityType = 'Test';
$this->applier = new PrimaryFilterApplier(
entityType: $this->entityType,
user: $this->user,
primaryFilterFactory: $this->filterFactory,
);
}
public function testApply1()
{
$filterName = 'test';
$filter = $this->createMock(PrimaryFilter::class);
$filter
->expects($this->once())
->method('apply')
->with($this->queryBuilder);
$this->filterFactory
->expects($this->once())
->method('has')
->with($this->entityType, $filterName)
->willReturn(true);
$this->filterFactory
->expects($this->once())
->method('create')
->with($this->entityType, $this->user, $filterName)
->willReturn($filter);
$this->applier->apply($this->queryBuilder, $filterName);
}
public function testApply2()
{
$filterName = 'test';
$this->filterFactory
->expects($this->once())
->method('has')
->with($this->entityType, $filterName)
->willReturn(false);
$this->filterFactory
->expects($this->never())
->method('create');
$this->expectException(BadRequest::class);
$this->applier->apply($this->queryBuilder, $filterName);
}
}

View File

@@ -0,0 +1,196 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Select\SearchParams;
use Espo\Core\Select\Select\Applier as SelectApplier;
use Espo\Core\Select\Select\MetadataProvider;
use Espo\Core\Utils\FieldUtil;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class SelectApplierTest extends TestCase
{
private $metadataProvider;
private $queryBuilder;
private $applier;
private $entityType;
private $fieldUtil;
private $searchParams;
private $user;
protected function setUp(): void
{
$this->user = $this->createMock(User::class);
$this->metadataProvider = $this->createMock(MetadataProvider::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->searchParams = $this->createMock(SearchParams::class);
$this->fieldUtil = $this->createMock(FieldUtil::class);
$this->entityType = 'Test';
$this->applier = new SelectApplier(
$this->entityType,
$this->user,
$this->fieldUtil,
$this->metadataProvider
);
}
public function testApply1()
{
$this->initTestApply();
}
public function testApplyPortal()
{
$this->initTestApply(true);
}
protected function initTestApply(bool $isPortal = false)
{
$select = ['testSelect', 'testNotExisting', 'testText'];
$orderBy = 'testOrder';
$aclAttributeList = ['testAcl'];
$aclPortalAttributeList = ['testAclPortal'];
$dependencyMap = [
'testSelect' => [
'testDependency',
],
];
$aclAttribute = $aclAttributeList[0];
if ($isPortal) {
$aclAttribute = $aclPortalAttributeList[0];
}
$this->user
->expects($this->any())
->method('isPortal')
->willReturn($isPortal);
$this->searchParams
->expects($this->any())
->method('getMaxTextAttributeLength')
->willReturn(100);
$this->searchParams
->expects($this->any())
->method('getSelect')
->willReturn($select);
$this->searchParams
->expects($this->any())
->method('getOrderBy')
->willReturn($orderBy);
$this->metadataProvider
->expects($this->any())
->method('getAclAttributeList')
->willReturn($aclAttributeList);
$this->metadataProvider
->expects($this->any())
->method('getAclPortalAttributeList')
->willReturn($aclPortalAttributeList);
$this->metadataProvider
->expects($this->any())
->method('getSelectAttributesDependencyMap')
->with($this->entityType)
->willReturn($dependencyMap);
$this->fieldUtil
->expects($this->once())
->method('getAttributeList')
->with($this->entityType, $orderBy)
->willReturn([$orderBy]);
$this->metadataProvider
->expects($this->any())
->method('hasAttribute')
->willReturnMap(
[
[$this->entityType, 'id', true],
[$this->entityType, 'testSelect', true],
[$this->entityType, 'testNotExisting', false],
[$this->entityType, 'testOrder', true],
[$this->entityType, 'testAcl', true],
[$this->entityType, 'testAclPortal', true],
[$this->entityType, 'testDependency', true],
[$this->entityType, 'testText', true],
]
);
$this->metadataProvider
->expects($this->any())
->method('isAttributeNotStorable')
->willReturn(false);
$this->metadataProvider
->expects($this->any())
->method('getAttributeType')
->willReturnMap(
[
[$this->entityType, 'id', Entity::ID],
[$this->entityType, 'testSelect', Entity::VARCHAR],
[$this->entityType, 'testOrder', Entity::VARCHAR],
[$this->entityType, 'testAcl', Entity::VARCHAR],
[$this->entityType, 'testAclPortal', Entity::VARCHAR],
[$this->entityType, 'testDependency', Entity::VARCHAR],
[$this->entityType, 'testText', Entity::TEXT],
]
);
$expected = [
'id',
$aclAttribute,
'testSelect',
['LEFT:(testText, 100)', 'testText'],
'testOrder',
'testDependency',
];
$this->queryBuilder
->expects($this->once())
->method('select')
->with($expected);
$this->applier->apply($this->queryBuilder, $this->searchParams);
}
}

View File

@@ -0,0 +1,269 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Select\Text\Applier as TextFilterApplier;
use Espo\Core\Select\Text\ConfigProvider;
use Espo\Core\Select\Text\DefaultFilter;
use Espo\Core\Select\Text\FilterFactory;
use Espo\Core\Select\Text\FilterParams;
use Espo\Core\Select\Text\FullTextSearch\Data as FullTextSearchData;
use Espo\Core\Select\Text\FullTextSearch\DataComposer\Params as FullTextSearchDataComposerParams;
use Espo\Core\Select\Text\FullTextSearch\DataComposerFactory as FullTextSearchDataComposerFactory;
use Espo\Core\Select\Text\FullTextSearch\DefaultDataComposer as FullTextSearchDataComposer;
use Espo\Core\Select\Text\MetadataProvider;
use Espo\Core\Utils\Config;
use Espo\Entities\User;
use Espo\ORM\Entity;
use Espo\ORM\Query\Part\Expression as Expr;
use Espo\ORM\Query\Part\Where\OrGroup;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class TextFilterApplierTest extends TestCase
{
private $metadataProvider;
private $queryBuilder;
private $filterParams;
private $config;
private $fullTextSearchDataComposerFactory;
private $filterFactory;
private $applier;
private $entityType;
protected function setUp(): void
{
$user = $this->createMock(User::class);
$this->metadataProvider = $this->createMock(MetadataProvider::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->filterParams = $this->createMock(FilterParams::class);
$this->config = $this->createMock(Config::class);
$this->fullTextSearchDataComposerFactory = $this->createMock(FullTextSearchDataComposerFactory::class);
$this->filterFactory = $this->createMock(FilterFactory::class);
$configProvider = $this->createMock(ConfigProvider::class);
$this->entityType = 'Test';
$this->applier = new TextFilterApplier(
$this->entityType,
$user,
$this->metadataProvider,
$this->fullTextSearchDataComposerFactory,
$this->filterFactory,
$configProvider
);
}
public function testApply1()
{
$this->initTest(false);
}
public function testApply2()
{
$this->initTest(true);
}
public function testApply3()
{
$this->initTest(false, '1000');
}
protected function initTest(bool $noFullTextSearch = false, ?string $filter = null)
{
$filter = $filter ?? 'test';
$filterParams = $this->createMock(FilterParams::class);
$filterParams
->expects($this->any())
->method('noFullTextSearch')
->willReturn($noFullTextSearch);
$this->config
->expects($this->any())
->method('get')
->willReturnMap(
[
['textFilterUseContainsForVarchar', false],
['textFilterContainsMinLength', 4],
]
);
$configProvider = new ConfigProvider($this->config);
$defaultFilter = new DefaultFilter(
$this->entityType,
$this->metadataProvider,
$configProvider
);
$this->filterFactory
->expects($this->once())
->method('create')
->willReturn(
$defaultFilter
);
$this->metadataProvider
->expects($this->any())
->method('getFullTextSearchOrderType')
->with($this->entityType)
->willReturn(null);
$this->metadataProvider
->expects($this->any())
->method('getAttributeType')
->willReturnMap(
[
[$this->entityType, 'fieldVarchar', Entity::VARCHAR],
[$this->entityType, 'fieldText', Entity::TEXT],
[$this->entityType, 'fieldFullText', Entity::TEXT],
[$this->entityType, 'fieldInt', Entity::INT],
[$this->entityType, 'fieldForeign', Entity::FOREIGN],
['ForeignEntityType', 'field', Entity::VARCHAR],
]
);
$this->metadataProvider
->expects($this->any())
->method('getAttributeRelationParam')
->with($this->entityType, 'fieldForeign')
->willReturn('link1');
$this->metadataProvider
->expects($this->any())
->method('getRelationType')
->with($this->entityType, 'link2')
->willReturn(Entity::HAS_MANY);
$this->metadataProvider
->expects($this->any())
->method('getRelationEntityType')
->with($this->entityType, 'link2')
->willReturn('ForeignEntityType');
$this->metadataProvider
->expects($this->any())
->method('getTextFilterAttributeList')
->with($this->entityType)
->willReturn(
['fieldVarchar', 'fieldText', 'fieldFullText', 'fieldInt', 'fieldForeign', 'link2.field']
);
if (!$noFullTextSearch) {
$this->initFullTextSearchData($filter, ['fieldFullText'], 'TEST:(test)');
}
$expectedWhere = [
['fieldVarchar*' => $filter . '%'],
['fieldText*' => '%' . $filter . '%']
];
if (is_numeric($filter)) {
$expectedWhere[] = ['fieldInt=' => intval($filter)];
}
if ($noFullTextSearch) {
$expectedWhere[] = ['fieldFullText*' => '%' . $filter . '%'];
}
$expectedWhere[] = ['fieldForeign*' => $filter . '%'];
$expectedWhere[] = ['link2.field*' => $filter . '%'];
if (!$noFullTextSearch) {
$expectedWhere[] = ['NOT_EQUAL:(TEST:(test), 0):' => null];
}
$this->queryBuilder
->expects($this->once())
->method('where')
->with(OrGroup::fromRaw($expectedWhere));
$c = $this->exactly(2);
$this->queryBuilder
->expects($c)
->method('leftJoin')
->willReturnCallback(function ($link) use ($c) {
if ($c->numberOfInvocations() === 1) {
$this->assertEquals('link1', $link);
}
if ($c->numberOfInvocations() === 2) {
$this->assertEquals('link2', $link);
}
return $this->queryBuilder;
});
$this->queryBuilder
->expects($this->once())
->method('distinct');
$this->applier->apply($this->queryBuilder, $filter, $this->filterParams);
}
protected function initFullTextSearchData(string $filter, array $fieldList, string $expression)
{
$fullTextSearchData = $this->createMock(FullTextSearchData::class);
$fullTextSearchDataComposer = $this->createMock(FullTextSearchDataComposer::class);
$fullTextSearchDataComposerParams = FullTextSearchDataComposerParams::create();
$this->fullTextSearchDataComposerFactory
->expects($this->once())
->method('create')
->with($this->entityType)
->willReturn($fullTextSearchDataComposer);
$fullTextSearchData
->expects($this->any())
->method('getFieldList')
->willReturn($fieldList);
$fullTextSearchData
->expects($this->any())
->method('getExpression')
->willReturn(Expr::create($expression));
$fullTextSearchDataComposer
->expects($this->any())
->method('compose')
->with($filter, $fullTextSearchDataComposerParams)
->willReturn($fullTextSearchData);
}
}

View File

@@ -0,0 +1,131 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier\Appliers;
use Espo\Core\Select\Where\Applier as WhereApplier;
use Espo\Core\Select\Where\Checker;
use Espo\Core\Select\Where\CheckerFactory;
use Espo\Core\Select\Where\Converter;
use Espo\Core\Select\Where\ConverterFactory;
use Espo\Core\Select\Where\Item as WhereItem;
use Espo\Core\Select\Where\Params;
use Espo\Entities\User;
use Espo\ORM\Query\SelectBuilder as QueryBuilder;
use PHPUnit\Framework\TestCase;
class WhereApplierTest extends TestCase
{
private $user;
private $queryBuilder;
private $whereItem;
private $converterFactory;
private $converter;
private $checkerFactory;
private $checker;
private $params;
private $entityType;
private $applier;
protected function setUp(): void
{
$this->user = $this->createMock(User::class);
$this->queryBuilder = $this->createMock(QueryBuilder::class);
$this->whereItem = $this->createMock(WhereItem::class);
$this->converterFactory = $this->createMock(ConverterFactory::class);
$this->converter = $this->createMock(Converter::class);
$this->checkerFactory = $this->createMock(CheckerFactory::class);
$this->checker = $this->createMock(Checker::class);
$this->params = $this->createMock(Params::class);
$this->entityType = 'Test';
$this->applier = new WhereApplier(
$this->entityType,
$this->user,
$this->converterFactory,
$this->checkerFactory
);
}
public function testApply1()
{
$this->checker
->expects($this->once())
->method('check')
->with($this->whereItem, $this->params);
$this->checkerFactory
->expects($this->once())
->method('create')
->with($this->entityType, $this->user)
->willReturn($this->checker);
$this->converter
->expects($this->once())
->method('convert')
->with($this->queryBuilder, $this->whereItem);
$this->converterFactory
->expects($this->once())
->method('create')
->with($this->entityType, $this->user)
->willReturn($this->converter);
$this->applier->apply($this->queryBuilder, $this->whereItem, $this->params);
}
public function testApply2()
{
$this->checker
->expects($this->once())
->method('check')
->with($this->whereItem, $this->params);
$this->checkerFactory
->expects($this->once())
->method('create')
->with($this->entityType, $this->user)
->willReturn($this->checker);
$this->converter
->expects($this->once())
->method('convert')
->with($this->queryBuilder, $this->whereItem);
$this->converterFactory
->expects($this->once())
->method('create')
->with($this->entityType, $this->user)
->willReturn($this->converter);
$this->applier->apply($this->queryBuilder, $this->whereItem, $this->params);
}
}

View File

@@ -0,0 +1,171 @@
<?php
/************************************************************************
* This file is part of EspoCRM.
*
* EspoCRM – Open Source CRM application.
* Copyright (C) 2014-2026 EspoCRM, Inc.
* Website: https://www.espocrm.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License version 3,
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
************************************************************************/
namespace tests\unit\Espo\Core\Select\Applier;
use Espo\Core\Acl;
use Espo\Core\AclManager;
use Espo\Core\Binding\Binder;
use Espo\Core\Binding\BindingContainer;
use Espo\Core\Binding\BindingData;
use Espo\Core\InjectableFactory;
use Espo\Core\Select\AccessControl\Applier as AccessControlFilterApplier;
use Espo\Core\Select\Applier\Appliers\Additional as AdditionalApplier;
use Espo\Core\Select\Applier\Appliers\Limit as LimitApplier;
use Espo\Core\Select\Applier\Factory as ApplierFactory;
use Espo\Core\Select\Bool\Applier as BoolFilterListApplier;
use Espo\Core\Select\Order\Applier as OrderApplier;
use Espo\Core\Select\Primary\Applier as PrimaryFilterApplier;
use Espo\Core\Select\Select\Applier as SelectApplier;
use Espo\Core\Select\Text\Applier as TextFilterApplier;
use Espo\Core\Select\Where\Applier as WhereApplier;
use Espo\Core\Utils\Acl\UserAclManagerProvider;
use Espo\Entities\User;
use PHPUnit\Framework\TestCase;
class FactoryTest extends TestCase
{
private $aclManager;
private $acl;
private $injectableFactory;
private $user;
private $factory;
protected function setUp(): void
{
$this->injectableFactory = $this->createMock(InjectableFactory::class);
$this->user = $this->createMock(User::class);
$userAclManagerProvider = $this->createMock(UserAclManagerProvider::class);
$this->aclManager = $this->createMock(AclManager::class);
$userAclManagerProvider
->expects($this->any())
->method('get')
->willReturn($this->aclManager);
$this->factory = new ApplierFactory(
$this->injectableFactory,
$userAclManagerProvider,
);
$this->acl = $this->createMock(Acl::class);
$this->aclManager
->expects($this->any())
->method('createUserAcl')
->willReturn($this->acl);
}
public function testCreate1()
{
$this->prepareFactoryTest(SelectApplier::class, ApplierFactory::SELECT, 'createSelect');
}
public function testCreate2()
{
$this->prepareFactoryTest(
BoolFilterListApplier::class, ApplierFactory::BOOL_FILTER_LIST, 'createBoolFilterList');
}
public function testCreate3()
{
$this->prepareFactoryTest(TextFilterApplier::class, ApplierFactory::TEXT_FILTER, 'createTextFilter');
}
public function testCreate4()
{
$this->prepareFactoryTest(WhereApplier::class, ApplierFactory::WHERE, 'createWhere');
}
public function testCreate5()
{
$this->prepareFactoryTest(OrderApplier::class, ApplierFactory::ORDER, 'createOrder');
}
public function testCreate6()
{
$this->prepareFactoryTest(LimitApplier::class, ApplierFactory::LIMIT, 'createLimit');
}
public function testCreate7()
{
$this->prepareFactoryTest(AdditionalApplier::class, ApplierFactory::ADDITIONAL, 'createAdditional');
}
public function testCreate8()
{
$this->prepareFactoryTest(
PrimaryFilterApplier::class, ApplierFactory::PRIMARY_FILTER, 'createPrimaryFilter');
}
public function testCreate9()
{
$this->prepareFactoryTest(
AccessControlFilterApplier::class, ApplierFactory::ACCESS_CONTROL_FILTER, 'createAccessControlFilter');
}
protected function prepareFactoryTest(string $defaultClassName, string $type, string $method)
{
$entityType = 'Test';
$applierClassName = $className ?? $defaultClassName;
$applier = $this->createMock($defaultClassName);
$bindingData = new BindingData();
$binder = new Binder($bindingData);
$binder
->bindInstance(User::class, $this->user)
->bindInstance(AclManager::class, $this->aclManager)
->bindInstance(Acl::class, $this->acl)
->for($applierClassName)
->bindValue('$entityType', $entityType);
$bindingContainer = new BindingContainer($bindingData);
$this->injectableFactory
->expects($this->once())
->method('createWithBinding')
->with($applierClassName, $bindingContainer)
->willReturn($applier);
$resultApplier = $this->factory->$method(
$entityType,
$this->user,
$type
);
$this->assertEquals($applier, $resultApplier);
}
}