View source
<?php
namespace Drupal\Tests\search_api\Kernel;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Transaction;
use Drupal\KernelTests\KernelTestBase;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\Utility\Utility;
use Psr\Log\LoggerInterface;
class BasicTrackerTest extends KernelTestBase {
public static $modules = [
'user',
'search_api',
'system',
];
protected $tracker;
protected $index;
protected $timeService;
public function setUp() {
parent::setUp();
$this
->installSchema('search_api', [
'search_api_item',
]);
$this
->installConfig('search_api');
if (!Utility::isRunningInCli()) {
\Drupal::state()
->set('search_api_use_tracking_batch', FALSE);
}
$this->index = Index::create([
'id' => 'index',
'tracker_settings' => [
'default' => [],
],
]);
$this->tracker = $this->index
->getTrackerInstance();
$this->timeService = new TestTimeService();
$this->tracker
->setTimeService($this->timeService);
}
public function testTracking($indexing_order) {
$logger = $this
->createMock(LoggerInterface::class);
$logger
->method('log')
->willReturnCallback(function ($level, $message, array $variables) {
$error = 'Tracking operation threw ';
$error .= strtr($message, $variables);
throw new \Exception($error);
});
$this->tracker
->setLogger($logger);
$this->tracker
->setConfiguration([
'indexing_order' => $indexing_order,
]);
$datasource_1 = 'test1';
$datasource_2 = 'test2';
$ids = [];
foreach ([
$datasource_1,
$datasource_2,
] as $num => $datasource_id) {
foreach ([
1,
2,
3,
] as $raw_id) {
$ids[$num][] = Utility::createCombinedId($datasource_id, $raw_id);
}
}
$this
->assertIndexingStatus(0, 0);
$this
->assertIndexingStatus(0, 0, $datasource_1);
$this
->assertIndexingStatus(0, 0, $datasource_2);
$this->tracker
->trackItemsDeleted([
$ids[0][0],
]);
$this
->assertIndexingStatus(0, 0);
$this->tracker
->trackItemsUpdated([
$ids[0][0],
]);
$this
->assertIndexingStatus(0, 0);
$this->tracker
->trackItemsIndexed([
$ids[0][0],
]);
$this
->assertIndexingStatus(0, 0);
$this->tracker
->trackItemsInserted([
$ids[0][0],
]);
$this->tracker
->trackItemsInserted([
$ids[1][1],
]);
$this->timeService
->advanceTime();
$this->tracker
->trackItemsInserted([
$ids[0][2],
$ids[1][0],
]);
$this->timeService
->advanceTime();
$this->tracker
->trackItemsInserted([
$ids[0][1],
$ids[1][2],
$ids[0][0],
]);
$this->timeService
->advanceTime();
$this
->assertIndexingStatus(0, 6);
$this
->assertIndexingStatus(0, 3, $datasource_1);
$this
->assertIndexingStatus(0, 3, $datasource_2);
$fifo = $indexing_order === 'fifo';
$to_index = $this->tracker
->getRemainingItems(4);
sort($to_index);
if ($fifo) {
$expected = [
$ids[0][0],
$ids[0][2],
$ids[1][0],
$ids[1][1],
];
}
else {
$expected = [
$ids[0][1],
$ids[0][2],
$ids[1][0],
$ids[1][2],
];
}
$this
->assertEquals($expected, $to_index);
$to_index = $this->tracker
->getRemainingItems(1, $datasource_1);
if ($fifo) {
$expected = [
$ids[0][0],
];
}
else {
$expected = [
$ids[0][1],
];
}
$this
->assertEquals($expected, $to_index);
$to_index = $this->tracker
->getRemainingItems(-1);
sort($to_index);
$expected = array_merge($ids[0], $ids[1]);
$this
->assertEquals($expected, $to_index);
$to_index = $this->tracker
->getRemainingItems(-1, $datasource_2);
sort($to_index);
$this
->assertEquals($ids[1], $to_index);
$this->tracker
->trackItemsUpdated([
$ids[0][0],
]);
$this->timeService
->advanceTime();
$to_index = $this->tracker
->getRemainingItems(1, $datasource_1);
$this
->assertEquals([
$ids[0][0],
], $to_index);
$this->tracker
->trackItemsInserted([]);
$this->tracker
->trackItemsUpdated([]);
$this->tracker
->trackItemsIndexed([]);
$this->tracker
->trackItemsDeleted([]);
$this
->assertIndexingStatus(0, 6);
$this
->assertIndexingStatus(0, 3, $datasource_1);
$this
->assertIndexingStatus(0, 3, $datasource_2);
$this->tracker
->trackItemsIndexed([
$ids[0][0],
$ids[0][1],
$ids[1][0],
]);
$this
->assertIndexingStatus(3, 6);
$this
->assertIndexingStatus(2, 3, $datasource_1);
$this
->assertIndexingStatus(1, 3, $datasource_2);
$to_index = $this->tracker
->getRemainingItems(-1);
sort($to_index);
$expected = [
$ids[0][2],
$ids[1][1],
$ids[1][2],
];
$this
->assertEquals($expected, $to_index);
$this->tracker
->trackItemsUpdated([
$ids[0][0],
$ids[0][2],
]);
$this->timeService
->advanceTime();
$this
->assertIndexingStatus(2, 6);
$this
->assertIndexingStatus(1, 3, $datasource_1);
$this
->assertIndexingStatus(1, 3, $datasource_2);
$to_index = $this->tracker
->getRemainingItems(-1);
sort($to_index);
array_unshift($expected, $ids[0][0]);
$this
->assertEquals($expected, $to_index);
$this->tracker
->trackItemsDeleted([
$ids[1][0],
$ids[1][2],
]);
$this
->assertIndexingStatus(1, 4);
$this
->assertIndexingStatus(1, 3, $datasource_1);
$this
->assertIndexingStatus(0, 1, $datasource_2);
$to_index = $this->tracker
->getRemainingItems(-1);
sort($to_index);
unset($expected[3]);
$this
->assertEquals($expected, $to_index);
if ($fifo) {
$expected = [
$ids[0][2],
$ids[1][1],
];
}
else {
$expected = [
$ids[0][0],
$ids[0][2],
];
}
$to_index = $this->tracker
->getRemainingItems(2);
sort($to_index);
$this
->assertEquals($expected, $to_index);
$this->tracker
->trackItemsInserted([
$ids[1][2],
]);
$this->timeService
->advanceTime();
$this
->assertIndexingStatus(1, 5);
$this
->assertIndexingStatus(1, 3, $datasource_1);
$this
->assertIndexingStatus(0, 2, $datasource_2);
$this->tracker
->trackItemsIndexed(array_merge($ids[0], $ids[1]));
$this
->assertIndexingStatus(5, 5);
$this
->assertIndexingStatus(3, 3, $datasource_1);
$this
->assertIndexingStatus(2, 2, $datasource_2);
$this->tracker
->trackAllItemsUpdated($datasource_1);
$this->timeService
->advanceTime();
$this
->assertIndexingStatus(2, 5);
$this
->assertIndexingStatus(0, 3, $datasource_1);
$this
->assertIndexingStatus(2, 2, $datasource_2);
$this->tracker
->trackItemsIndexed([
$ids[0][0],
]);
$this
->assertIndexingStatus(3, 5);
$this
->assertIndexingStatus(1, 3, $datasource_1);
$this
->assertIndexingStatus(2, 2, $datasource_2);
$this->tracker
->trackAllItemsUpdated();
$this->timeService
->advanceTime();
$this
->assertIndexingStatus(0, 5);
$this
->assertIndexingStatus(0, 3, $datasource_1);
$this
->assertIndexingStatus(0, 2, $datasource_2);
$this->tracker
->trackAllItemsDeleted($datasource_2);
$this
->assertIndexingStatus(0, 3);
$this
->assertIndexingStatus(0, 3, $datasource_1);
$this
->assertIndexingStatus(0, 0, $datasource_2);
$this->tracker
->trackAllItemsDeleted();
$this
->assertIndexingStatus(0, 0);
$this
->assertIndexingStatus(0, 0, $datasource_1);
$this
->assertIndexingStatus(0, 0, $datasource_2);
}
public function trackingDataProvider() {
return [
'FIFO' => [
'fifo',
],
'LIFO' => [
'lifo',
],
];
}
public function testExceptionHandling($tracker_method, array $args = []) {
$connection = $this
->getMockBuilder(Connection::class)
->disableOriginalConstructor()
->getMock();
foreach ([
'select',
'insert',
'update',
'delete',
] as $method) {
$connection
->method($method)
->willThrowException(new \Exception());
}
$transaction = $this
->getMockBuilder(Transaction::class)
->disableOriginalConstructor()
->getMock();
$rolled_back = FALSE;
$rollback = function () use (&$rolled_back) {
$rolled_back = TRUE;
};
$transaction
->method('rollback')
->willReturnCallback($rollback);
$connection
->method('startTransaction')
->willReturn($transaction);
$this->tracker
->setDatabaseConnection($connection);
$logger = $this
->createMock(LoggerInterface::class);
$log = [];
$logger
->method('log')
->willReturnCallback(function () use (&$log) {
$log[] = func_get_args();
});
$this->tracker
->setLogger($logger);
call_user_func_array([
$this->tracker,
$tracker_method,
], $args);
$this
->assertCount(1, $log);
$this
->assertStringStartsWith('%type', $log[0][1]);
$this
->assertFalse($rolled_back);
}
public function exceptionHandlingDataProvider() {
return [
'trackItemsInserted()' => [
'trackItemsInserted',
[
[
'',
],
],
],
'trackItemsUpdated()' => [
'trackItemsUpdated',
[
[
'',
],
],
],
'trackAllItemsUpdated()' => [
'trackAllItemsUpdated',
],
'trackItemsIndexed()' => [
'trackItemsIndexed',
[
[
'',
],
],
],
'trackItemsDeleted()' => [
'trackItemsDeleted',
],
'trackAllItemsDeleted()' => [
'trackAllItemsDeleted',
],
'getRemainingItems()' => [
'getRemainingItems',
],
'getTotalItemsCount()' => [
'getTotalItemsCount',
],
'getIndexedItemsCount()' => [
'getIndexedItemsCount',
],
'getRemainingItemsCount()' => [
'getRemainingItemsCount',
],
];
}
protected function assertIndexingStatus($indexed, $total, $datasource_id = NULL) {
$datasource = $datasource_id ? " for datasource {$datasource_id}" : '';
$actual_indexed = $this->tracker
->getIndexedItemsCount($datasource_id);
$this
->assertEquals($indexed, $actual_indexed, "{$actual_indexed} items indexed{$datasource}, {$indexed} expected.");
$actual_total = $this->tracker
->getTotalItemsCount($datasource_id);
$this
->assertEquals($total, $actual_total, "{$actual_total} items tracked in total{$datasource}, {$total} expected.");
$remaining = $total - $indexed;
$actual_remaining = $this->tracker
->getRemainingItemsCount($datasource_id);
$this
->assertEquals($remaining, $actual_remaining, "{$actual_remaining} items remaining to be indexed{$datasource}, {$remaining} expected.");
}
}