Passed
Push — master ( b4e3f0...d6a119 )
by Robbie
04:16 queued 02:31
created

tests/SolrReindexTest.php (3 issues)

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
The expression return $serviceMock->getMock() returns the type PHPUnit_Framework_MockObject_MockObject which is incompatible with the documented return type SilverStripe\FullTextSea...lr\Services\SolrService.
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 ignore-call  annotation

165
        $this->service->/** @scrutinizer ignore-call */ 
166
                        method('deleteByQuery')

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 ignore-call  annotation

237
        $this->assertNotEmpty($logger->/** @scrutinizer ignore-call */ getMessages('Adding ' . SolrReindexTest_Item::class));

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