0
0
Fork 0
mirror of https://github.com/salesagility/SuiteCRM.git synced 2025-03-17 06:42:43 +00:00

adding unit test for elastic pagination

This commit is contained in:
Gyula Madarasz 2019-01-10 17:45:10 +00:00
parent 055e8bc35e
commit d60df7660d
11 changed files with 227 additions and 34 deletions

View file

@ -5236,7 +5236,7 @@ class SugarBean
* @param string $id
*/
public function mark_deleted($id)
{
{
global $current_user;
$date_modified = $GLOBALS['timedate']->nowDb();
$id = $this->db->quote($id);

View file

@ -42,4 +42,8 @@ if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
class SuiteException extends Exception {}
class SuiteException extends Exception {
const NO_ID = 1;
}

View file

@ -51,6 +51,8 @@ if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
include_once __DIR__ . '/StateCheckerDirectoryIterator.php';
/**
* StateChecker
*
@ -325,7 +327,7 @@ class StateChecker
throw new StateCheckerException('Real path can not resolved for: ' . $path);
}
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($realpath), RecursiveIteratorIterator::SELF_FIRST);
$objects = new RecursiveIteratorIterator(new StateCheckerDirectoryIterator($realpath), RecursiveIteratorIterator::SELF_FIRST);
$files = [];
foreach ($objects as $name => $object) {
if (!$object->isDir() && !$this->isExcludedFile($name)) {

View file

@ -0,0 +1,71 @@
<?php
/**
*
* SugarCRM Community Edition is a customer relationship management program developed by
* SugarCRM, Inc. Copyright (C) 2004-2013 SugarCRM Inc.
*
* SuiteCRM is an extension to SugarCRM Community Edition developed by SalesAgility Ltd.
* Copyright (C) 2011 - 2018 SalesAgility Ltd.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by the
* Free Software Foundation with the addition of the following permission added
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
* IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
* OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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 http://www.gnu.org/licenses or write to the Free
* Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*
* You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
* SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
*
* 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 "Powered by
* SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
* reasonably feasible for technical reasons, the Appropriate Legal Notices must
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
namespace SuiteCRM;
use InvalidArgumentException;
use RecursiveDirectoryIterator;
use RecursiveFilterIterator;
/**
* StateCheckerDirectoryIterator
*
* Readable directory iterator
*
* @author gyula
*/
class StateCheckerDirectoryIterator extends RecursiveFilterIterator
{
public function __construct($path)
{
if (!$path instanceof RecursiveDirectoryIterator) {
if (! is_readable($path) || ! is_dir($path)) {
throw new InvalidArgumentException("$path is not a valid directory or not readable");
}
$path = new RecursiveDirectoryIterator($path, RecursiveDirectoryIterator::SKIP_DOTS);
}
parent::__construct($path);
}
public function accept()
{
return $this->current()->isReadable() && $this->current()->isDir();
}
}

View file

@ -118,12 +118,7 @@ class ElasticSearchCommands extends \Robo\Tasks
{
$this->bootstrap();
$indexer = new ElasticSearchIndexer();
$indexer->setDifferentialIndexing($differential);
if ($searchdefs) {
$indexer->setDocumentifier(new SearchDefsDocumentifier());
}
$indexer->index();
ElasticSearchIndexer::repairElasticsearchIndex($differential, $searchdefs);
}
/**

View file

@ -44,7 +44,9 @@ if (!defined('sugarEntry') || !sugarEntry) {
}
use InvalidArgumentException;
use LoggerManager;
use SugarBean;
use SuiteCRM\Search\Exceptions\SearchException;
use SuiteCRM\Utility\SuiteLogger;
use Throwable;
@ -99,6 +101,8 @@ class ElasticSearchHooks
{
try {
$this->reIndex($bean);
} catch (SearchException $exception) {
$this->handleError($exception);
} catch (\Exception $exception) {
$this->handleError($exception);
} catch (\Throwable $throwable) {
@ -114,20 +118,25 @@ class ElasticSearchHooks
private function reIndex(SugarBean $bean)
{
if (ElasticSearchIndexer::isEnabled() === false) {
return;
throw new SearchException(
'Elasticsearch trying to re-indexing a bean but indexer is disabled in configuration.',
SearchException::ES_DISABLED
);
}
$this->bean = $bean;
$this->getIndexer();
if ($this->isBlacklisted()) {
return;
if (!$this->isBlacklisted()) {
$this->correctAction();
$this->performAction($bean);
} else {
LoggerManager::getLogger()->warn(
'Elasticsearch trying to re-indexing a bean but this module is blacklisted: ' .
$bean->module_name
);
}
$this->correctAction();
$this->performAction($bean);
}
/**
@ -192,4 +201,4 @@ class ElasticSearchHooks
$logger->error($message);
$logger->error($exception);
}
}
}

View file

@ -566,4 +566,18 @@ class ElasticSearchIndexer extends AbstractIndexer
return $meta['last_index'];
}
/**
*
* @param bool $differential
* @param int $searchdefs
*/
public static function repairElasticsearchIndex($differential = true, $searchdefs = 0) {
$indexer = new ElasticSearchIndexer();
$indexer->setDifferentialIndexing($differential);
if ($searchdefs) {
$indexer->setDocumentifier(new SearchDefsDocumentifier());
}
$indexer->index();
}
}

View file

@ -50,5 +50,7 @@ class SearchException extends \RuntimeException
{
const ZERO_SIZE = 100;
const ES_DISABLED = 101;
const ES_MODULE_BLACKLISTED = 102;
}

View file

@ -118,6 +118,15 @@ class SearchResults
foreach ($hits as $module => $beans) {
foreach ((array)$beans as $bean) {
$obj = BeanFactory::getBean($module, $bean);
// if a search found a bean but suitecrm does not, it could happens
// maybe the bean is deleted but elsasticsearch is not re-indexing yet.
// so at this point we trying to rebuild the index and try again to get bean:
if (!$obj) {
ElasticSearch\ElasticSearchIndexer::repairElasticsearchIndex();
$obj = BeanFactory::getBean($module, $bean);
}
if (!$obj) {
throw new Exception('Error retrieveing bean: ' . $module . ' [' . $bean . ']');
}

View file

@ -38,6 +38,8 @@
* display the words "Powered by SugarCRM" and "Supercharged by SuiteCRM".
*/
use SuiteCRM\Search\ElasticSearch\ElasticSearchIndexer;
if (!defined('sugarEntry') || !sugarEntry) {
die('Not A Valid Entry Point');
}
@ -95,6 +97,7 @@ if (is_admin($current_user) || isset($from_sync_client) || is_admin_for_any_modu
}
echo "<h3>{$mod_strings['LBL_REPAIR_DATABASE_SYNCED']}</h3>";
ElasticSearchIndexer::repairElasticsearchIndex();
}
} else {
if (!$export && empty($_REQUEST['repair_silent'])) {
@ -173,6 +176,7 @@ if (is_admin($current_user) || isset($from_sync_client) || is_admin_for_any_modu
echo $ss->fetch('modules/Administration/templates/RepairDatabase.tpl');
} else {
echo "<h3>{$mod_strings['LBL_REPAIR_DATABASE_SYNCED']}</h3>";
ElasticSearchIndexer::repairElasticsearchIndex();
}
}
}

View file

@ -54,41 +54,124 @@ if (!defined('sugarEntry') || !sugarEntry) {
*/
class SearchResultsControllerTest extends StateCheckerPHPUnitTestCaseAbstract {
public function testDisplayFoundOne() {
$state = new SuiteCRM\StateSaver();
public function testDisplayFoundOnePage() {
$state = new SuiteCRM\StateSaver();
$state->pushTable('accounts');
$state->pushTable('accounts_cstm');
$account = BeanFactory::getBean('Accounts');
$account->name = 'test account 1';
$ok = $account->save();
$this->assertTrue((bool)$ok);
$searchHooks = new \SuiteCRM\Search\ElasticSearch\ElasticSearchHooks();
$searchHooks->beanSaved($account, null, null);
$state->pushGlobals();
$ids = [];
for ($i=0; $i<15; $i++) {
$account = BeanFactory::getBean('Accounts');
$account->name = 'test account ' . $i;
$ok = $account->save();
$this->assertTrue((bool)$ok);
$ids[] = $account->id;
}
$this->assertEquals(15, count($ids));
$request = [
'search-query-string' => 'test account',
//'query_string' => '',
'query_string' => 'test account',
'search-query-size' => 10,
'search-query-from' => 0,
'search-engine' => 0,
];
$query = SearchQuery::fromRequestArray($request);
$hits = [];
$hits = [
'Accounts' => $ids,
];
$groupedByModule = true;
$searchTime = null;
$total = null;
$searchTime = 0.05;
$total = 15;
$scores = null;
$options = null;
$results = new SearchResults($hits, $groupedByModule, $searchTime, $total, $scores, $options);
$searchResultsController = new SearchResultsController($query, $results);
ob_start();
$searchResultsController->display();
$contents = ob_get_contents();
$content = ob_get_contents();
ob_end_clean();
$this->assertContains('test account 1', $contents);
$this->assertContains('Total result(s): 15', $content);
$this->assertContains('Page 1 of 2', $content);
// add 5 more..
for ($i=15; $i<20; $i++) {
$account = BeanFactory::getBean('Accounts');
$account->name = 'test account ' . $i;
$ok = $account->save();
$this->assertTrue((bool)$ok);
$ids[] = $account->id;
}
$this->assertEquals(20, count($ids));
$request = [
'search-query-string' => 'test account',
'query_string' => 'test account',
'search-query-size' => 10,
'search-query-from' => 10,
'search-engine' => 0,
];
$query = SearchQuery::fromRequestArray($request);
$hits = [
'Accounts' => $ids,
];
$groupedByModule = true;
$searchTime = 0.05;
$total = 20;
$scores = null;
$options = null;
$results = new SearchResults($hits, $groupedByModule, $searchTime, $total, $scores, $options);
$searchResultsController = new SearchResultsController($query, $results);
ob_start();
$searchResultsController->display();
$content = ob_get_contents();
ob_end_clean();
$this->assertContains('Total result(s): 20', $content);
$this->assertContains('Page 2 of 2', $content);
$state->popGlobals();
$state->popTable('accounts_cstm');
$state->popTable('accounts');
}
public function testDisplayFoundOne() {
$state = new SuiteCRM\StateSaver();
$state->pushTable('accounts');
$state->pushTable('accounts_cstm');
$state->pushGlobals();
$account = BeanFactory::getBean('Accounts');
$account->name = 'test account 1';
$ok = $account->save();
$this->assertTrue((bool)$ok);
$request = [
'search-query-string' => 'test account',
'query_string' => 'test account',
'search-query-size' => 10,
'search-query-from' => 0,
'search-engine' => 0,
];
$query = SearchQuery::fromRequestArray($request);
$hits = [
'Accounts' => [$account->id],
];
$groupedByModule = true;
$searchTime = 0.05;
$total = 1;
$scores = null;
$options = null;
$results = new SearchResults($hits, $groupedByModule, $searchTime, $total, $scores, $options);
$searchResultsController = new SearchResultsController($query, $results);
ob_start();
$searchResultsController->display();
$content = ob_get_contents();
ob_end_clean();
$this->assertContains('test account 1', $content);
$state->popGlobals();
$state->popTable('accounts_cstm');
$state->popTable('accounts');
}