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\Search\Variants\SearchVariant; |
||||||
10 | use SilverStripe\FullTextSearch\Solr\Reindex\Handlers\SolrReindexHandler; |
||||||
11 | use SilverStripe\FullTextSearch\Solr\Services\Solr4Service; |
||||||
12 | use SilverStripe\FullTextSearch\Solr\Services\SolrService; |
||||||
13 | use SilverStripe\FullTextSearch\Solr\Tasks\Solr_Reindex; |
||||||
14 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Index; |
||||||
15 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Item; |
||||||
16 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_RecordingLogger; |
||||||
17 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_TestHandler; |
||||||
18 | use SilverStripe\FullTextSearch\Tests\SolrReindexTest\SolrReindexTest_Variant; |
||||||
19 | |||||||
20 | class SolrReindexTest extends SapphireTest |
||||||
21 | { |
||||||
22 | protected $usesDatabase = true; |
||||||
23 | |||||||
24 | protected static $extra_dataobjects = array( |
||||||
25 | SolrReindexTest_Item::class |
||||||
26 | ); |
||||||
27 | |||||||
28 | /** |
||||||
29 | * Forced index for testing |
||||||
30 | * |
||||||
31 | * @var SolrReindexTest_Index |
||||||
32 | */ |
||||||
33 | protected $index = null; |
||||||
34 | |||||||
35 | /** |
||||||
36 | * Mock service |
||||||
37 | * |
||||||
38 | * @var SolrService |
||||||
39 | */ |
||||||
40 | protected $service = null; |
||||||
41 | |||||||
42 | protected function setUp() |
||||||
43 | { |
||||||
44 | parent::setUp(); |
||||||
45 | |||||||
46 | // Set test handler for reindex |
||||||
47 | Config::modify()->set(Injector::class, SolrReindexHandler::class, array( |
||||||
48 | 'class' => SolrReindexTest_TestHandler::class |
||||||
49 | )); |
||||||
50 | |||||||
51 | Injector::inst()->registerService(new SolrReindexTest_TestHandler(), SolrReindexHandler::class); |
||||||
52 | |||||||
53 | // Set test variant |
||||||
54 | SolrReindexTest_Variant::enable(); |
||||||
55 | |||||||
56 | // Set index list |
||||||
57 | $this->service = $this->getServiceMock(); |
||||||
58 | $this->index = singleton(SolrReindexTest_Index::class); |
||||||
59 | $this->index->setService($this->service); |
||||||
60 | |||||||
61 | FullTextSearch::force_index_list($this->index); |
||||||
62 | } |
||||||
63 | |||||||
64 | /** |
||||||
65 | * Populate database with dummy dataset |
||||||
66 | * |
||||||
67 | * @param int $number Number of records to create in each variant |
||||||
68 | */ |
||||||
69 | protected function createDummyData($number) |
||||||
70 | { |
||||||
71 | // Note that we don't create any records in variant = 2, to represent a variant |
||||||
72 | // that should be cleared without any re-indexes performed |
||||||
73 | foreach ([0, 1] as $variant) { |
||||||
74 | for ($i = 1; $i <= $number; $i++) { |
||||||
75 | $item = new SolrReindexTest_Item(); |
||||||
76 | $item->Variant = $variant; |
||||||
77 | $item->Title = "Item $variant / $i"; |
||||||
78 | $item->write(); |
||||||
79 | } |
||||||
80 | } |
||||||
81 | } |
||||||
82 | |||||||
83 | /** |
||||||
84 | * Mock service |
||||||
85 | * |
||||||
86 | * @return SolrService |
||||||
87 | */ |
||||||
88 | protected function getServiceMock() |
||||||
89 | { |
||||||
90 | $serviceMock = $this->getMockBuilder(Solr4Service::class) |
||||||
91 | ->setMethods(['deleteByQuery', 'addDocument']); |
||||||
92 | |||||||
93 | return $serviceMock->getMock(); |
||||||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||||||
94 | } |
||||||
95 | |||||||
96 | protected function tearDown() |
||||||
97 | { |
||||||
98 | FullTextSearch::force_index_list(); |
||||||
99 | SolrReindexTest_Variant::disable(); |
||||||
100 | parent::tearDown(); |
||||||
101 | } |
||||||
102 | |||||||
103 | /** |
||||||
104 | * Get the reindex handler |
||||||
105 | * |
||||||
106 | * @return SolrReindexHandler |
||||||
107 | */ |
||||||
108 | protected function getHandler() |
||||||
109 | { |
||||||
110 | return Injector::inst()->get(SolrReindexHandler::class); |
||||||
111 | } |
||||||
112 | |||||||
113 | /** |
||||||
114 | * Ensure the test variant is up and running properly |
||||||
115 | */ |
||||||
116 | public function testVariant() |
||||||
117 | { |
||||||
118 | // State defaults to 0 |
||||||
119 | $variant = SearchVariant::current_state(); |
||||||
120 | $this->assertEquals( |
||||||
121 | array( |
||||||
122 | SolrReindexTest_Variant::class => "0" |
||||||
123 | ), |
||||||
124 | $variant |
||||||
125 | ); |
||||||
126 | |||||||
127 | // All states enumerated |
||||||
128 | $allStates = iterator_to_array(SearchVariant::reindex_states()); |
||||||
129 | $this->assertEquals( |
||||||
130 | array( |
||||||
131 | array( |
||||||
132 | SolrReindexTest_Variant::class => "0" |
||||||
133 | ), |
||||||
134 | array( |
||||||
135 | SolrReindexTest_Variant::class => "1" |
||||||
136 | ), |
||||||
137 | array( |
||||||
138 | SolrReindexTest_Variant::class => "2" |
||||||
139 | ) |
||||||
140 | ), |
||||||
141 | $allStates |
||||||
142 | ); |
||||||
143 | |||||||
144 | // Check correct items created and that filtering on variant works |
||||||
145 | $this->createDummyData(120); |
||||||
146 | SolrReindexTest_Variant::set_current(2); |
||||||
147 | $this->assertEquals(0, SolrReindexTest_Item::get()->count()); |
||||||
148 | SolrReindexTest_Variant::set_current(1); |
||||||
149 | $this->assertEquals(120, SolrReindexTest_Item::get()->count()); |
||||||
150 | SolrReindexTest_Variant::set_current(0); |
||||||
151 | $this->assertEquals(120, SolrReindexTest_Item::get()->count()); |
||||||
152 | SolrReindexTest_Variant::disable(); |
||||||
153 | $this->assertEquals(240, SolrReindexTest_Item::get()->count()); |
||||||
154 | } |
||||||
155 | |||||||
156 | |||||||
157 | /** |
||||||
158 | * Given the invocation of a new re-index with a given set of data, ensure that the necessary |
||||||
159 | * list of groups are created and segmented for each state |
||||||
160 | * |
||||||
161 | * Test should work fine with any variants (versioned, subsites, etc) specified |
||||||
162 | */ |
||||||
163 | public function testReindexSegmentsGroups() |
||||||
164 | { |
||||||
165 | $this->service->method('deleteByQuery') |
||||||
0 ignored issues
–
show
The method
method() does not exist on SilverStripe\FullTextSea...lr\Services\SolrService .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||||
166 | ->withConsecutive( |
||||||
167 | ['-(ClassHierarchy:' . SolrReindexTest_Item::class . ')'], |
||||||
168 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +(_testvariant:"2")'] |
||||||
169 | ); |
||||||
170 | |||||||
171 | $this->createDummyData(120); |
||||||
172 | |||||||
173 | // Initiate re-index |
||||||
174 | $logger = new SolrReindexTest_RecordingLogger(); |
||||||
175 | $this->getHandler()->runReindex($logger, 21, Solr_Reindex::class); |
||||||
176 | |||||||
177 | // Test that invalid classes are removed |
||||||
178 | $this->assertContains( |
||||||
179 | 'Clearing obsolete classes from ' . str_replace('\\', '-', SolrReindexTest_Index::class), |
||||||
180 | $logger->getMessages() |
||||||
181 | ); |
||||||
182 | |||||||
183 | // Test that valid classes in invalid variants are removed |
||||||
184 | $this->assertContains( |
||||||
185 | 'Clearing all records of type ' . SolrReindexTest_Item::class . ' in the current state: {' |
||||||
186 | . json_encode(SolrReindexTest_Variant::class) . ':"2"}', |
||||||
187 | $logger->getMessages() |
||||||
188 | ); |
||||||
189 | |||||||
190 | // 120x2 grouped into groups of 21 results in 12 groups |
||||||
191 | $this->assertEquals(12, $logger->countMessages('Called processGroup with ')); |
||||||
192 | $this->assertEquals(6, $logger->countMessages('{' . json_encode(SolrReindexTest_Variant::class) . ':"0"}')); |
||||||
193 | $this->assertEquals(6, $logger->countMessages('{' . json_encode(SolrReindexTest_Variant::class) . ':"1"}')); |
||||||
194 | |||||||
195 | // Given that there are two variants, there should be two group ids of each number |
||||||
196 | $this->assertEquals(2, $logger->countMessages(' ' . SolrReindexTest_Item::class . ', group 0 of 6')); |
||||||
197 | $this->assertEquals(2, $logger->countMessages(' ' . SolrReindexTest_Item::class . ', group 1 of 6')); |
||||||
198 | $this->assertEquals(2, $logger->countMessages(' ' . SolrReindexTest_Item::class . ', group 2 of 6')); |
||||||
199 | $this->assertEquals(2, $logger->countMessages(' ' . SolrReindexTest_Item::class . ', group 3 of 6')); |
||||||
200 | $this->assertEquals(2, $logger->countMessages(' ' . SolrReindexTest_Item::class . ', group 4 of 6')); |
||||||
201 | $this->assertEquals(2, $logger->countMessages(' ' . SolrReindexTest_Item::class . ', group 5 of 6')); |
||||||
202 | |||||||
203 | // Check various group sizes |
||||||
204 | $logger->clear(); |
||||||
205 | $this->getHandler()->runReindex($logger, 120, 'Solr_Reindex'); |
||||||
206 | $this->assertEquals(2, $logger->countMessages('Called processGroup with ')); |
||||||
207 | $logger->clear(); |
||||||
208 | $this->getHandler()->runReindex($logger, 119, 'Solr_Reindex'); |
||||||
209 | $this->assertEquals(4, $logger->countMessages('Called processGroup with ')); |
||||||
210 | $logger->clear(); |
||||||
211 | $this->getHandler()->runReindex($logger, 121, 'Solr_Reindex'); |
||||||
212 | $this->assertEquals(2, $logger->countMessages('Called processGroup with ')); |
||||||
213 | $logger->clear(); |
||||||
214 | $this->getHandler()->runReindex($logger, 2, 'Solr_Reindex'); |
||||||
215 | $this->assertEquals(120, $logger->countMessages('Called processGroup with ')); |
||||||
216 | } |
||||||
217 | |||||||
218 | /** |
||||||
219 | * Test index processing on individual groups |
||||||
220 | */ |
||||||
221 | public function testRunGroup() |
||||||
222 | { |
||||||
223 | $this->service->method('deleteByQuery') |
||||||
224 | ->with('+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=2 u=2}mod(ID, 6)" +(_testvariant:"1")'); |
||||||
225 | |||||||
226 | $this->createDummyData(120); |
||||||
227 | $logger = new SolrReindexTest_RecordingLogger(); |
||||||
228 | |||||||
229 | // Initiate re-index of third group (index 2 of 6) |
||||||
230 | $state = array(SolrReindexTest_Variant::class => '1'); |
||||||
231 | $this->getHandler()->runGroup($logger, $this->index, $state, SolrReindexTest_Item::class, 6, 2); |
||||||
232 | $idMessage = $logger->filterMessages('Updated '); |
||||||
233 | $this->assertNotEmpty(preg_match('/^Updated (?<ids>[,\d]+)/i', $idMessage[0], $matches)); |
||||||
234 | $ids = array_unique(explode(',', $matches['ids'])); |
||||||
235 | |||||||
236 | // Test successful |
||||||
237 | $this->assertNotEmpty($logger->getMessages('Adding ' . SolrReindexTest_Item::class)); |
||||||
0 ignored issues
–
show
The call to
SilverStripe\FullTextSea...ngLogger::getMessages() has too many arguments starting with 'Adding ' . SilverStripe...ReindexTest_Item::class .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
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.
Loading history...
|
|||||||
238 | $this->assertNotEmpty($logger->getMessages('Done')); |
||||||
239 | |||||||
240 | // Test that items in this variant / group are re-indexed |
||||||
241 | // 120 divided into 6 groups should be 20 at least (max 21) |
||||||
242 | $this->assertEquals(21, count($ids), 'Group size is about 20', 1); |
||||||
243 | foreach ($ids as $id) { |
||||||
244 | // Each id should be % 6 == 2 |
||||||
245 | $this->assertEquals(2, $id % 6, "ID $id Should match pattern ID % 6 = 2"); |
||||||
246 | } |
||||||
247 | } |
||||||
248 | |||||||
249 | /** |
||||||
250 | * Test that running all groups covers the entire range of dataobject IDs |
||||||
251 | */ |
||||||
252 | public function testRunAllGroups() |
||||||
253 | { |
||||||
254 | $this->service->method('deleteByQuery') |
||||||
255 | ->withConsecutive( |
||||||
256 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=0 u=0}mod(ID, 6)" +(_testvariant:"1")'], |
||||||
257 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=1 u=1}mod(ID, 6)" +(_testvariant:"1")'], |
||||||
258 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=2 u=2}mod(ID, 6)" +(_testvariant:"1")'], |
||||||
259 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=3 u=3}mod(ID, 6)" +(_testvariant:"1")'], |
||||||
260 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=4 u=4}mod(ID, 6)" +(_testvariant:"1")'], |
||||||
261 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=5 u=5}mod(ID, 6)" +(_testvariant:"1")'], |
||||||
262 | ['+(ClassHierarchy:' . SolrReindexTest_Item::class . ') +_query_:"{!frange l=6 u=6}mod(ID, 6)" +(_testvariant:"1")'] |
||||||
263 | ); |
||||||
264 | |||||||
265 | $this->createDummyData(120); |
||||||
266 | $logger = new SolrReindexTest_RecordingLogger(); |
||||||
267 | |||||||
268 | // Test that running all groups covers the complete set of ids |
||||||
269 | $state = array(SolrReindexTest_Variant::class => '1'); |
||||||
270 | for ($i = 0; $i < 6; $i++) { |
||||||
271 | // See testReindexSegmentsGroups for test that each of these states is invoked during a full reindex |
||||||
272 | $this |
||||||
273 | ->getHandler() |
||||||
274 | ->runGroup($logger, $this->index, $state, SolrReindexTest_Item::class, 6, $i); |
||||||
275 | } |
||||||
276 | |||||||
277 | // Count all ids updated |
||||||
278 | $ids = array(); |
||||||
279 | foreach ($logger->filterMessages('Updated ') as $message) { |
||||||
280 | $this->assertNotEmpty(preg_match('/^Updated (?<ids>[,\d]+)/', $message, $matches)); |
||||||
281 | $ids = array_unique(array_merge($ids, explode(',', $matches['ids']))); |
||||||
282 | } |
||||||
283 | |||||||
284 | // Check ids |
||||||
285 | $this->assertEquals(120, count($ids)); |
||||||
286 | } |
||||||
287 | } |
||||||
288 |