Completed
Push — master ( 44f7bc...3e4910 )
by
unknown
20s queued 14s
created

testCorrectSubsiteIDOnFileWrite()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 57
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 57
rs 9.0309
c 0
b 0
f 0
cc 4
eloc 42
nc 6
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace SilverStripe\FullTextSearch\Tests;
4
5
use Apache_Solr_Document;
6
use Page;
7
use SilverStripe\Assets\File;
8
use SilverStripe\Assets\Image;
9
use SilverStripe\CMS\Model\SiteTree;
10
use SilverStripe\Core\Config\Config;
11
use SilverStripe\Core\Injector\Injector;
12
use SilverStripe\Dev\SapphireTest;
13
use SilverStripe\FullTextSearch\Search\FullTextSearch;
14
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateImmediateProcessor;
15
use SilverStripe\FullTextSearch\Search\Processors\SearchUpdateProcessor;
16
use SilverStripe\FullTextSearch\Search\Updaters\SearchUpdater;
17
use SilverStripe\FullTextSearch\Search\Variants\SearchVariantSubsites;
18
use SilverStripe\FullTextSearch\Solr\Services\Solr4Service;
19
use SilverStripe\FullTextSearch\Tests\SolrIndexSubsitesTest\SolrIndexSubsitesTest_Index;
20
use SilverStripe\FullTextSearch\Tests\SolrIndexVersionedTest\SolrDocumentMatcher;
21
use SilverStripe\ORM\DataObject;
22
use SilverStripe\Subsites\Model\Subsite;
23
use SilverStripe\Versioned\Versioned;
24
25
/**
26
 * Subsite specific solr testing
27
 */
28
class SolrIndexSubsitesTest extends SapphireTest
29
{
30
    protected static $fixture_file = 'SolrIndexSubsitesTest/SolrIndexSubsitesTest.yml';
31
32
    /**
33
     * @var SolrIndexSubsitesTest_Index
34
     */
35
    private static $index = null;
36
37
    protected $server = null;
38
39
    protected function setUp()
0 ignored issues
show
Coding Style introduced by
setUp uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
40
    {
41
        // Prevent parent::setUp() crashing on db build
42
        if (!class_exists(Subsite::class)) {
43
            static::$fixture_file = null;
44
        }
45
46
        parent::setUp();
47
48
        if (!class_exists(Subsite::class)) {
49
            $this->markTestSkipped("These tests need the Subsite module installed to run");
50
        }
51
52
        $this->server = $_SERVER;
53
54
        if (self::$index === null) {
55
            self::$index = singleton(SolrIndexSubsitesTest_Index::class);
56
        }
57
58
        SearchUpdater::bind_manipulation_capture();
59
60
        Config::modify()->set(Injector::class, SearchUpdateProcessor::class, [
61
            'class' => SearchUpdateImmediateProcessor::class,
62
        ]);
63
64
        FullTextSearch::force_index_list(self::$index);
65
        SearchUpdater::clear_dirty_indexes();
66
    }
67
68
    protected function tearDown()
0 ignored issues
show
Coding Style introduced by
tearDown uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
69
    {
70
        if ($this->server) {
71
            $_SERVER = $this->server;
72
            $this->server = null;
73
        }
74
        parent::tearDown();
75
    }
76
77
    protected function getServiceMock()
78
    {
79
        return $this->getMockBuilder(Solr4Service::class)
80
            ->setMethods(['addDocument', 'commit'])
81
            ->getMock();
82
    }
83
84
    /**
85
     * @param DataObject $object Item being added
86
     * @param int $subsiteID
87
     * @param string $stage
88
     * @return string
89
     */
90
    protected function getExpectedDocumentId($object, $subsiteID, $stage = null)
91
    {
92
        $id = $object->ID;
93
        $class = DataObject::getSchema()->baseDataClass($object);
94
        $variants = array();
95
96
        // Check subsite
97
        if (class_exists(Subsite::class)
98
            && DataObject::getSchema()->hasOneComponent($object->getClassName(), 'Subsite')
0 ignored issues
show
Bug Best Practice introduced by
The expression \SilverStripe\ORM\DataOb...ClassName(), 'Subsite') of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
99
        ) {
100
            $variants[] = '"SearchVariantSubsites":"' . $subsiteID. '"';
101
        }
102
103
        // Check versioned
104
        if ($stage) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $stage of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
105
            $variants[] = '"SearchVariantVersioned":"' . $stage . '"';
106
        }
107
        return $id . '-' . $class . '-{' . implode(',', $variants) . '}';
108
    }
109
110
    public function testPublishing()
0 ignored issues
show
Coding Style introduced by
testPublishing uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
111
    {
112
        // Setup mocks
113
        $serviceMock = $this->getServiceMock();
114
        self::$index->setService($serviceMock);
115
116
        $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
117
118
        // Add records to first subsite
119
        Versioned::set_stage(Versioned::DRAFT);
120
        $_SERVER['HTTP_HOST'] = 'www.subsite1.com';
121
122
        $file = new File();
123
        $file->Title = 'My File';
124
        $file->SubsiteID = $subsite1->ID;
125
        $file->write();
126
127
        $page = new Page();
128
        $page->Title = 'My Page';
129
        $page->SubsiteID = $subsite1->ID;
130
        $page->write();
131
132
        $doc1 = new Apache_Solr_Document([
0 ignored issues
show
Unused Code introduced by
The call to Apache_Solr_Document::__construct() has too many arguments starting with array('_documentid' => $...site' => $subsite1->ID).

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
133
            '_documentid' => $this->getExpectedDocumentId($page, $subsite1->ID, 'Stage'),
134
            'ClassName' => 'Page',
135
            'SiteTree_Title' => 'My Page',
136
            '_versionedstage' => 'Stage',
137
            '_subsite' => $subsite1->ID,
138
        ]);
139
140
        $doc2 = new Apache_Solr_Document([
0 ignored issues
show
Unused Code introduced by
The call to Apache_Solr_Document::__construct() has too many arguments starting with array('_documentid' => $...site' => $subsite1->ID).

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.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
141
            '_documentid' => $this->getExpectedDocumentId($file, $subsite1->ID),
142
            'ClassName' => File::class,
143
            'File_Title' => 'My File',
144
            '_subsite' => $subsite1->ID,
145
        ]);
146
147
        $serviceMock
148
            ->expects($this->exactly(2))
149
            ->method('addDocument')
150
            ->withConsecutive($doc1, $doc2);
151
152
        SearchUpdater::flush_dirty_indexes();
153
    }
154
155
    public function testCorrectSubsiteIDOnPageWrite()
156
    {
157
        $mockWrites = [
158
            '3367:SiteTree:a:1:{s:22:"SearchVariantVersioned";s:4:"Live";}' => [
159
                'base' => 'SilverStripe\\CMS\\Model\\SiteTree',
160
                'class' => 'Page',
161
                'id' => 3367,
162
                'statefulids' => [
163
                    [
164
                        'id' => 3367,
165
                        'state' => [
166
                            'SearchVariantVersioned' => 'Live',
167
                        ],
168
                    ],
169
                ],
170
                'fields' => [
171
                    'SilverStripe\\CMS\\Model\\SiteTree:ClassName' => 'Page',
172
                    'SilverStripe\\CMS\\Model\\SiteTree:LastEdited' => '2016-12-08 23:55:30',
173
                    'SilverStripe\\CMS\\Model\\SiteTree:Created' => '2016-11-30 05:23:58',
174
                    'SilverStripe\\CMS\\Model\\SiteTree:URLSegment' => 'test',
175
                    'SilverStripe\\CMS\\Model\\SiteTree:Title' => 'Test Title',
176
                    'SilverStripe\\CMS\\Model\\SiteTree:Content' => '<p>test content</p>',
177
                    'SilverStripe\\CMS\\Model\\SiteTree:MetaDescription' => 'a solr test',
178
                    'SilverStripe\\CMS\\Model\\SiteTree:ShowInMenus' => 1,
179
                    'SilverStripe\\CMS\\Model\\SiteTree:ShowInSearch' => 1,
180
                    'SilverStripe\\CMS\\Model\\SiteTree:Sort' => 77,
181
                    'SilverStripe\\CMS\\Model\\SiteTree:HasBrokenFile' => 0,
182
                    'SilverStripe\\CMS\\Model\\SiteTree:HasBrokenLink' => 0,
183
                    'SilverStripe\\CMS\\Model\\SiteTree:CanViewType' => 'Inherit',
184
                    'SilverStripe\\CMS\\Model\\SiteTree:CanEditType' => 'Inherit',
185
                    'SilverStripe\\CMS\\Model\\SiteTree:Locale' => 'en_NZ',
186
                    'SilverStripe\\CMS\\Model\\SiteTree:SubsiteID' => 0,
187
                    'Page:ID' => 3367,
188
                    'Page:MetaKeywords' => null,
189
                ],
190
            ],
191
        ];
192
        $variant = new SearchVariantSubsites();
193
        $tmpMockWrites = $mockWrites;
194
        $variant->extractManipulationWriteState($tmpMockWrites);
195
196 View Code Duplication
        foreach ($tmpMockWrites as $mockWrite) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
197
            $this->assertCount(1, $mockWrite['statefulids']);
198
            $statefulIDs = array_shift($mockWrite['statefulids']);
199
200
            $this->assertArrayHasKey(SearchVariantSubsites::class, $statefulIDs['state']);
201
            $this->assertEquals(0, $statefulIDs['state'][SearchVariantSubsites::class]);
202
        }
203
204
        $subsite = $this->objFromFixture(Subsite::class, 'subsite1');
205
        $tmpMockWrites = $mockWrites;
206
        $tmpMockWrites['3367:SiteTree:a:1:{s:22:"SearchVariantVersioned";s:4:"Live";}']['fields'][SiteTree::class . ':SubsiteID'] = $subsite->ID;
207
208
        $variant->extractManipulationWriteState($tmpMockWrites);
209 View Code Duplication
        foreach ($tmpMockWrites as $mockWrite) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
210
            $this->assertCount(1, $mockWrite['statefulids']);
211
            $statefulIDs = array_shift($mockWrite['statefulids']);
212
213
            $this->assertArrayHasKey(SearchVariantSubsites::class, $statefulIDs['state']);
214
            $this->assertEquals($subsite->ID, $statefulIDs['state'][SearchVariantSubsites::class]);
215
        }
216
    }
217
218
    public function testCorrectSubsiteIDOnFileWrite()
219
    {
220
        $subsiteIDs = ['0'] + $this->allFixtureIDs(Subsite::class);
221
        $mockWrites = [
222
            '35910:File:a:0:{}' => [
223
                'base' => File::class,
224
                'class' => File::class,
225
                'id' => 35910,
226
                'statefulids' => [
227
                    [
228
                        'id' => 35910,
229
                        'state' => [],
230
                    ],
231
                ],
232
                'fields' => [
233
                    File::class . ':ClassName' => Image::class,
234
                    File::class . ':ShowInSearch' => 1,
235
                    File::class . ':ParentID' => 26470,
236
                    File::class . ':Filename' => 'assets/Uploads/pic.jpg',
237
                    File::class . ':Name' => 'pic.jpg',
238
                    File::class . ':Title' => 'pic',
239
                    File::class . ':SubsiteID' => 0,
240
                    File::class . ':OwnerID' => 661,
241
                    File::class . ':CurrentVersionID' => 22038,
242
                    File::class . ':LastEdited' => '2016-12-09 00:35:13',
243
                ],
244
            ],
245
        ];
246
        $variant = new SearchVariantSubsites();
247
        $tmpMockWrites = $mockWrites;
248
        $variant->extractManipulationWriteState($tmpMockWrites);
249
        foreach ($tmpMockWrites as $mockWrite) {
250
            $this->assertCount(count($subsiteIDs), $mockWrite['statefulids']);
251
            foreach ($mockWrite['statefulids'] as $statefulIDs) {
252
                $this->assertContains(
253
                    $statefulIDs['state'][SearchVariantSubsites::class],
254
                    $subsiteIDs,
255
                    sprintf(
256
                        'Failed to assert that %s is in list of valid subsites: %s',
257
                        $statefulIDs['state'][SearchVariantSubsites::class],
258
                        implode(', ', $subsiteIDs)
259
                    )
260
                );
261
            }
262
        }
263
264
        $subsite = $this->objFromFixture(Subsite::class, 'subsite1');
265
        $tmpMockWrites = $mockWrites;
266
        $tmpMockWrites['35910:File:a:0:{}']['fields'][File::class . ':SubsiteID'] = $subsite->ID;
267
268
        $variant->extractManipulationWriteState($tmpMockWrites);
269
        foreach ($tmpMockWrites as $mockWrite) {
270
            $this->assertCount(1, $mockWrite['statefulids']);
271
            $statefulIDs = array_shift($mockWrite['statefulids']);
272
            $this->assertEquals($subsite->ID, $statefulIDs['state'][SearchVariantSubsites::class]);
273
        }
274
    }
275
}
276