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

tests/SolrReindexQueuedTest.php (2 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\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))
0 ignored issues
show
The method expects() 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

143
        $this->service->/** @scrutinizer ignore-call */ 
144
                        expects($this->exactly(2))

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...
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
The call to SilverStripe\FullTextSea...ngLogger::getMessages() has too many arguments starting with 'Clearing obsolete class...eindexTest_Index::class. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

184
        $this->assertNotEmpty($logger->/** @scrutinizer ignore-call */ getMessages('Clearing obsolete classes from ' . SolrReindexTest_Index::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...
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