silverstripe /
silverstripe-fulltextsearch
| 1 | <?php |
||
| 2 | |||
| 3 | namespace SilverStripe\FullTextSearch\Tests; |
||
| 4 | |||
| 5 | use SilverStripe\Core\Config\Config; |
||
| 6 | use SilverStripe\Core\Injector\Injector; |
||
| 7 | use SilverStripe\Dev\SapphireTest; |
||
| 8 | use SilverStripe\FullTextSearch\Search\FullTextSearch; |
||
| 9 | use SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexHandler; |
||
| 10 | use SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexQueuedHandler; |
||
| 11 | use SilverStripe\FullTextSearch\Solr\Reindex\Jobs\SolrReindexGroupQueuedJob; |
||
| 12 | use SilverStripe\FullTextSearch\Solr\Reindex\Jobs\SolrReindexQueuedJob; |
||
| 13 | use SilverStripe\FullTextSearch\Solr\Services\Solr4Service; |
||
| 14 | use SilverStripe\FullTextSearch\Solr\Services\SolrService; |
||
| 15 | use SilverStripe\FullTextSearch\Tests\SolrReindexQueuedTest\SolrReindexQueuedTest_Service; |
||
| 16 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Index; |
||
| 17 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Item; |
||
| 18 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_RecordingLogger; |
||
| 19 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Variant; |
||
| 20 | use Symbiote\QueuedJobs\Services\QueuedJob; |
||
| 21 | |||
| 22 | /** |
||
| 23 | * Additional tests of solr reindexing processes when run with queuedjobs |
||
| 24 | */ |
||
| 25 | class SolrReindexQueuedTest extends SapphireTest |
||
| 26 | { |
||
| 27 | protected $usesDatabase = true; |
||
| 28 | |||
| 29 | protected static $extra_dataobjects = array( |
||
| 30 | SolrReindexTest_Item::class |
||
| 31 | ); |
||
| 32 | |||
| 33 | /** |
||
| 34 | * Forced index for testing |
||
| 35 | * |
||
| 36 | * @var SolrReindexTest_Index |
||
| 37 | */ |
||
| 38 | protected $index = null; |
||
| 39 | |||
| 40 | /** |
||
| 41 | * Mock service |
||
| 42 | * |
||
| 43 | * @var SolrService |
||
| 44 | */ |
||
| 45 | protected $service = null; |
||
| 46 | |||
| 47 | protected function setUp() |
||
| 48 | { |
||
| 49 | parent::setUp(); |
||
| 50 | |||
| 51 | if (!interface_exists(QueuedJob::class)) { |
||
| 52 | $this->skipTest = true; |
||
| 53 | return $this->markTestSkipped("These tests need the QueuedJobs module installed to run"); |
||
| 54 | } |
||
| 55 | |||
| 56 | // Set queued handler for reindex |
||
| 57 | Config::modify()->set(Injector::class, SolrReindexHandler::class, array( |
||
| 58 | 'class' => SolrReindexQueuedHandler::class |
||
| 59 | )); |
||
| 60 | Injector::inst()->registerService(new SolrReindexQueuedHandler(), SolrReindexHandler::class); |
||
| 61 | |||
| 62 | // Set test variant |
||
| 63 | SolrReindexTest_Variant::enable(); |
||
| 64 | |||
| 65 | // Set index list |
||
| 66 | $this->service = $this->serviceMock(); |
||
| 67 | $this->index = singleton(SolrReindexTest_Index::class); |
||
| 68 | $this->index->setService($this->service); |
||
| 69 | FullTextSearch::force_index_list($this->index); |
||
| 70 | } |
||
| 71 | |||
| 72 | /** |
||
| 73 | * Populate database with dummy dataset |
||
| 74 | * |
||
| 75 | * @param int $number Number of records to create in each variant |
||
| 76 | */ |
||
| 77 | protected function createDummyData($number) |
||
| 78 | { |
||
| 79 | // Note that we don't create any records in variant = 2, to represent a variant |
||
| 80 | // that should be cleared without any re-indexes performed |
||
| 81 | foreach ([0, 1] as $variant) { |
||
| 82 | for ($i = 1; $i <= $number; $i++) { |
||
| 83 | $item = new SolrReindexTest_Item(); |
||
| 84 | $item->Variant = $variant; |
||
| 85 | $item->Title = "Item $variant / $i"; |
||
| 86 | $item->write(); |
||
| 87 | } |
||
| 88 | } |
||
| 89 | } |
||
| 90 | |||
| 91 | /** |
||
| 92 | * Mock service |
||
| 93 | * |
||
| 94 | * @return SolrService |
||
| 95 | */ |
||
| 96 | protected function serviceMock() |
||
| 97 | { |
||
| 98 | // Setup mock |
||
| 99 | /** @var Solr4Service $serviceMock */ |
||
| 100 | $serviceMock = $this->getMockBuilder(Solr4Service::class) |
||
| 101 | ->setMethods(['deleteByQuery', 'addDocument']) |
||
| 102 | ->getMock(); |
||
| 103 | |||
| 104 | return $serviceMock; |
||
| 105 | } |
||
| 106 | |||
| 107 | protected function tearDown() |
||
| 108 | { |
||
| 109 | FullTextSearch::force_index_list(); |
||
| 110 | SolrReindexTest_Variant::disable(); |
||
| 111 | parent::tearDown(); |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * Get the reindex handler |
||
| 116 | * |
||
| 117 | * @return SolrReindexHandler |
||
| 118 | */ |
||
| 119 | protected function getHandler() |
||
| 120 | { |
||
| 121 | return Injector::inst()->get(SolrReindexHandler::class); |
||
| 122 | } |
||
| 123 | |||
| 124 | /** |
||
| 125 | * @return SolrReindexQueuedTest_Service |
||
| 126 | */ |
||
| 127 | protected function getQueuedJobService() |
||
| 128 | { |
||
| 129 | return Injector::inst()->get(SolrReindexQueuedTest_Service::class); |
||
| 130 | } |
||
| 131 | |||
| 132 | /** |
||
| 133 | * Test that reindex will generate a top top level queued job, and executing this will perform |
||
| 134 | * the necessary initialisation of the grouped queued jobs |
||
| 135 | */ |
||
| 136 | public function testReindexSegmentsGroups() |
||
| 137 | { |
||
| 138 | $this->createDummyData(18); |
||
| 139 | |||
| 140 | // Deletes are performed in the main task prior to individual groups being processed |
||
| 141 | // 18 records means 3 groups of 6 in each variant (6 total) |
||
| 142 | // Ensure correct call is made to Solr |
||
| 143 | $this->service->expects($this->exactly(2)) |
||
| 144 | ->method('deleteByQuery') |
||
| 145 | ->withConsecutive( |
||
| 146 | [ |
||
| 147 | $this->equalTo('-(ClassHierarchy:' . SolrReindexTest_Item::class . ')') |
||
| 148 | ], |
||
| 149 | [ |
||
| 150 | $this->equalTo('+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +(_testvariant:"2")') |
||
| 151 | ] |
||
| 152 | ); |
||
| 153 | |||
| 154 | // Create pre-existing jobs |
||
| 155 | $this->getQueuedJobService()->queueJob(new SolrReindexQueuedJob()); |
||
| 156 | $this->getQueuedJobService()->queueJob(new SolrReindexGroupQueuedJob()); |
||
| 157 | $this->getQueuedJobService()->queueJob(new SolrReindexGroupQueuedJob()); |
||
| 158 | |||
| 159 | // Initiate re-index |
||
| 160 | $logger = new SolrReindexTest_RecordingLogger(); |
||
| 161 | $this->getHandler()->triggerReindex($logger, 6, 'Solr_Reindex'); |
||
| 162 | |||
| 163 | // Old jobs should be cancelled |
||
| 164 | $this->assertEquals(1, $logger->countMessages('Cancelled 1 re-index tasks and 2 re-index groups')); |
||
| 165 | $this->assertEquals(1, $logger->countMessages('Queued Solr Reindex Job')); |
||
| 166 | |||
| 167 | // Next job should be queue job |
||
| 168 | $job = $this->getQueuedJobService()->getNextJob(); |
||
| 169 | $this->assertInstanceOf(SolrReindexQueuedJob::class, $job); |
||
| 170 | $this->assertEquals(6, $job->getBatchSize()); |
||
| 171 | |||
| 172 | // Test that necessary items are created |
||
| 173 | $logger->clear(); |
||
| 174 | $job->setLogger($logger); |
||
| 175 | $job->process(); |
||
| 176 | |||
| 177 | $this->assertEquals(1, $logger->countMessages('Beginning init of reindex')); |
||
| 178 | $this->assertEquals(6, $logger->countMessages('Queued Solr Reindex Group ')); |
||
| 179 | $this->assertEquals(3, $logger->countMessages(' of ' . SolrReindexTest_Item::class . ' in {' . json_encode(SolrReindexTest_Variant::class) . ':"0"}')); |
||
| 180 | $this->assertEquals(3, $logger->countMessages(' of ' . SolrReindexTest_Item::class . ' in {' . json_encode(SolrReindexTest_Variant::class) . ':"1"}')); |
||
| 181 | $this->assertEquals(1, $logger->countMessages('Completed init of reindex')); |
||
| 182 | |||
| 183 | // Test that invalid classes are removed |
||
| 184 | $this->assertNotEmpty($logger->getMessages('Clearing obsolete classes from ' . SolrReindexTest_Index::class)); |
||
|
0 ignored issues
–
show
|
|||
| 185 | |||
| 186 | // Test that valid classes in invalid variants are removed |
||
| 187 | $this->assertNotEmpty($logger->getMessages( |
||
| 188 | 'Clearing all records of type ' . SolrReindexTest_Item::class . ' in the current state: {"' . SolrReindexTest_Variant::class . '":"2"}' |
||
| 189 | )); |
||
| 190 | } |
||
| 191 | |||
| 192 | /** |
||
| 193 | * Test index processing on individual groups |
||
| 194 | */ |
||
| 195 | public function testRunGroup() |
||
| 196 | { |
||
| 197 | $this->createDummyData(18); |
||
| 198 | |||
| 199 | // Just do what the SolrReindexQueuedJob would do to create each sub |
||
| 200 | $logger = new SolrReindexTest_RecordingLogger(); |
||
| 201 | $this->getHandler()->runReindex($logger, 6, 'Solr_Reindex'); |
||
| 202 | |||
| 203 | // Assert jobs are created |
||
| 204 | $this->assertEquals(6, $logger->countMessages('Queued Solr Reindex Group')); |
||
| 205 | |||
| 206 | // Check next job is a group queued job |
||
| 207 | /** @var SolrReindexGroupQueuedJob $job */ |
||
| 208 | $job = $this->getQueuedJobService()->getNextJob(); |
||
| 209 | $this->assertInstanceOf(SolrReindexGroupQueuedJob::class, $job); |
||
| 210 | $this->assertEquals( |
||
| 211 | 'Solr Reindex Group (1/3) of ' . SolrReindexTest_Item::class . ' in {' . json_encode(SolrReindexTest_Variant::class) . ':"0"}', |
||
| 212 | $job->getTitle() |
||
| 213 | ); |
||
| 214 | |||
| 215 | // Running this job performs the necessary reindex |
||
| 216 | $logger->clear(); |
||
| 217 | $job->setLogger($logger); |
||
| 218 | $job->process(); |
||
| 219 | |||
| 220 | // Check tasks completed (as per non-queuedjob version) |
||
| 221 | $this->assertEquals(1, $logger->countMessages('Beginning reindex group')); |
||
| 222 | $this->assertEquals(1, $logger->countMessages('Adding ' . SolrReindexTest_Item::class . '')); |
||
| 223 | $this->assertEquals(1, $logger->countMessages('Queuing commit on all changes')); |
||
| 224 | $this->assertEquals(1, $logger->countMessages('Completed reindex group')); |
||
| 225 | |||
| 226 | // Check IDs |
||
| 227 | $idMessage = $logger->filterMessages('Updated '); |
||
| 228 | $this->assertNotEmpty(preg_match('/^Updated (?<ids>[,\d]+)/i', $idMessage[0], $matches)); |
||
| 229 | $ids = array_unique(explode(',', $matches['ids'])); |
||
| 230 | $this->assertEquals(6, count($ids)); |
||
| 231 | foreach ($ids as $id) { |
||
| 232 | // Each id should be % 3 == 0 |
||
| 233 | $this->assertEquals(0, $id % 3, "ID $id Should match pattern ID % 3 = 0"); |
||
| 234 | } |
||
| 235 | } |
||
| 236 | } |
||
| 237 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.