Completed
Push — master ( 187618...8c43e0 )
by Will
12s
created

tests/CommentingControllerTest.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace SilverStripe\Comments\Tests;
4
5
use SilverStripe\Comments\Controllers\CommentingController;
6
use SilverStripe\Comments\Model\Comment;
7
use SilverStripe\Comments\Model\Comment\SecurityToken as CommentSecurityToken;
8
use SilverStripe\Comments\Tests\Stubs\CommentableItem;
9
use SilverStripe\Comments\Tests\CommentTestHelper;
10
use SilverStripe\Control\Controller;
11
use SilverStripe\Core\Config\Config;
12
use SilverStripe\Core\Email\Email;
13
use SilverStripe\Dev\FunctionalTest;
14
use SilverStripe\ORM\DataObject;
15
use SilverStripe\Security\Member;
16
use SilverStripe\Security\SecurityToken;
17
18
/**
19
 * @package comments
20
 * @subpackage tests
21
 */
22
class CommentingControllerTest extends FunctionalTest
23
{
24
    /**
25
     * {@inheritDoc}
26
     */
27
    protected static $fixture_file = 'CommentsTest.yml';
28
29
    /**
30
     * {@inheritDoc}
31
     */
32
    protected $extraDataObjects = array(
33
        CommentableItem::class
34
    );
35
36
    protected $securityEnabled;
37
38
    public function tearDown()
39
    {
40
        if ($this->securityEnabled) {
41
            SecurityToken::inst()->enable();
42
        } else {
43
            SecurityToken::inst()->disable();
44
        }
45
        parent::tearDown();
46
    }
47
48
    public function setUp()
49
    {
50
        parent::setUp();
51
        $this->securityEnabled = SecurityToken::inst()->is_enabled();
52
53
        // We will assert against explicit responses, unless handed otherwise in a test for redirects
54
        $this->autoFollowRedirection = false;
55
    }
56
57
    public function testApproveUnmoderatedComment()
58
    {
59
        SecurityToken::inst()->disable();
60
61
        // mark a comment as spam then approve it
62
        $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
63
        $comment = $this->objFromFixture(Comment::class, 'testModeratedComment1');
64
        $st = new CommentSecurityToken($comment);
65
        $url = 'comments/approve/' . $comment->ID;
66
        $url = $st->addToUrl($url, Member::currentUser());
67
        $response = $this->get($url, null, ['Referer' => '/']);
68
        $this->assertEquals(302, $response->getStatusCode());
69
        $comment = DataObject::get_by_id(Comment::class, $comment->ID);
70
71
        // Need to use 0,1 here instead of false, true for SQLite
72
        $this->assertEquals(0, $comment->IsSpam);
73
        $this->assertEquals(1, $comment->Moderated);
74
75
        // try and approve a non existent comment
76
        $response = $this->get('comments/approve/100000');
77
        $this->assertEquals(404, $response->getStatusCode());
78
    }
79
80
    public function testSetGetOwnerController()
81
    {
82
        $commController = new CommentingController();
83
        $commController->setOwnerController(Controller::curr());
84
        $this->assertEquals(Controller::curr(), $commController->getOwnerController());
85
        $commController->setOwnerController(null);
86
        $this->assertNull($commController->getOwnerController());
87
    }
88
89 View Code Duplication
    public function testHam()
90
    {
91
        SecurityToken::inst()->disable();
92
93
        // mark a comment as spam then ham it
94
        $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
95
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
96
        $comment->markSpam();
97
        $st = new CommentSecurityToken($comment);
98
        $url = 'comments/ham/' . $comment->ID;
99
        $url = $st->addToUrl($url, Member::currentUser());
100
        $response = $this->get($url);
101
        $this->assertEquals(302, $response->getStatusCode());
102
        $comment = DataObject::get_by_id(Comment::class, $comment->ID);
103
104
        // Need to use 0,1 here instead of false, true for SQLite
105
        $this->assertEquals(0, $comment->IsSpam);
106
        $this->assertEquals(1, $comment->Moderated);
107
108
        // try and ham a non existent comment
109
        $response = $this->get('comments/ham/100000');
110
        $this->assertEquals(404, $response->getStatusCode());
111
    }
112
113 View Code Duplication
    public function testSpam()
114
    {
115
        // mark a comment as approved then spam it
116
        $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
117
        $comment = $this->objFromFixture(Comment::class, 'firstComA');
118
        $comment->markApproved();
119
        $st = new CommentSecurityToken($comment);
120
        $url = 'comments/spam/' . $comment->ID;
121
        $url = $st->addToUrl($url, Member::currentUser());
122
        $response = $this->get($url);
123
        $this->assertEquals(302, $response->getStatusCode());
124
        $comment = DataObject::get_by_id(Comment::class, $comment->ID);
125
126
        // Need to use 0,1 here instead of false, true for SQLite
127
        $this->assertEquals(1, $comment->IsSpam);
128
        $this->assertEquals(1, $comment->Moderated);
129
130
        // try and spam a non existent comment
131
        $response = $this->get('comments/spam/100000');
132
        $this->assertEquals(404, $response->getStatusCode());
133
    }
134
135
    public function testRSS()
136
    {
137
        // Delete the newly added children of firstComA so as not to have to recalculate values below
138
        $this->objFromFixture(Comment::class, 'firstComAChild1')->delete();
139
        $this->objFromFixture(Comment::class, 'firstComAChild2')->delete();
140
        $this->objFromFixture(Comment::class, 'firstComAChild3')->delete();
141
142
        $item = $this->objFromFixture(CommentableItem::class, 'first');
143
144
        // comments sitewide
145
        $response = $this->get('comments/rss');
146
        $this->assertEquals(10, substr_count($response->getBody(), "<item>"), "10 approved, non spam comments on page 1");
147
148
        $response = $this->get('comments/rss?start=10');
149
        $this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
150
151
        // all comments on a type
152
        $response = $this->get('comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem');
153
        $this->assertEquals(10, substr_count($response->getBody(), "<item>"));
154
155
        $response = $this->get('comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem?start=10');
156
        $this->assertEquals(4, substr_count($response->getBody(), "<item>"), "3 approved, non spam comments on page 2");
157
158
        // specific page
159
        $response = $this->get('comments/rss/SilverStripe-Comments-Tests-Stubs-CommentableItem/'.$item->ID);
160
        $this->assertEquals(1, substr_count($response->getBody(), "<item>"));
161
        $this->assertContains('<dc:creator>FA</dc:creator>', $response->getBody());
162
163
        // test accessing comments on a type that doesn't exist
164
        $response = $this->get('comments/rss/Fake');
165
        $this->assertEquals(404, $response->getStatusCode());
166
    }
167
168
    // This is returning a 404 which looks logical code wise but also a bit weird.
169
    // Test module on a clean install and check what the actual URL is first
170
/*    public function testReply() {
171
        $this->logInWithPermission('CMS_ACCESS_CommentAdmin');
172
        $comment = $this->objFromFixture('Comment', 'firstComA');
173
        $item = $this->objFromFixture('CommentableItem', 'first');
174
175
        $st = new CommentSecurityToken($comment);
176
        $url = 'comments/reply/' . $item->ID.'?ParentCommentID=' . $comment->ID;
177
        error_log($url);
178
        $response = $this->get($url);
179
        error_log(print_r($response,1));
180
181
        $this->assertEquals(200, $response->getStatusCode());
182
183
    }
184
*/
185
/*
186
    public function testCommentsFormLoadMemberData() {
187
        Config::inst()->update('CommentableItem', 'comments', array(
188
            'use_preview' => false
189
        ));
190
        $this->logInAs('visitor');
191
        SecurityToken::inst()->disable();
192
        $parent = $this->objFromFixture('CommentableItem', 'first');
193
        $parent->CommentsRequireLogin = true;
194
        $parent->PostingRequiredPermission = true;
195
        //$parent->write();
196
        $commController = new CommentingController();
197
        $commController->setOwnerRecord($parent);
198
199
        $form = $commController->CommentsForm();
200
        $commentsFields = $form->Fields()->first()->FieldList();
201
        $expected = array('Name', 'Email', 'URL', 'Comment', 'PreviewComment');
202
        CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
203
    }
204
*/
205
206
    public function testCommentsFormUsePreview()
207
    {
208
        // test with preview on
209
        Config::inst()->update(CommentableItem::class, 'comments', array(
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
210
            'use_preview' => true
211
        ));
212
213
        $this->objFromFixture(Comment::class, 'firstComAChild1')->delete();
214
        $this->objFromFixture(Comment::class, 'firstComAChild2')->delete();
215
        $this->objFromFixture(Comment::class, 'firstComAChild3')->delete();
216
217
        SecurityToken::inst()->disable();
218
        $this->autoFollowRedirection = false;
219
        $parent = $this->objFromFixture(CommentableItem::class, 'first');
220
        $commController = new CommentingController();
221
        $commController->setOwnerRecord($parent);
222
223
        $form = $commController->CommentsForm();
224
        $commentsFields = $form->Fields()->first()->FieldList();
225
        $expected = array('Name', 'Email', 'URL', 'Comment', 'PreviewComment');
226
        CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
227
228
        // Turn off preview.  Assert lack of preview field
229
        Config::inst()->update(CommentableItem::class, 'comments', array(
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface SilverStripe\Config\Coll...nfigCollectionInterface as the method update() does only exist in the following implementations of said interface: SilverStripe\Config\Coll...\MemoryConfigCollection.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
230
            'use_preview' => false
231
        ));
232
        $form = $commController->CommentsForm();
233
        $commentsFields = $form->Fields()->first()->FieldList();
234
        $expected = array('Name', 'Email', 'URL', 'Comment');
235
        CommentTestHelper::assertFieldNames($this, $expected, $commentsFields);
236
    }
237
238
    public function testCommentsForm()
239
    {
240
        $this->autoFollowRedirection = true;
241
242
        // Delete the newly added children of firstComA so as not to change this test
243
        $this->objFromFixture(Comment::class, 'firstComAChild1')->delete();
244
        $this->objFromFixture(Comment::class, 'firstComAChild2')->delete();
245
        $this->objFromFixture(Comment::class, 'firstComAChild3')->delete();
246
247
        SecurityToken::inst()->disable();
248
        $this->autoFollowRedirection = false;
249
        $parent = $this->objFromFixture(CommentableItem::class, 'first');
250
251
        // Test posting to base comment
252
        $response = $this->post(
253
            'comments/CommentsForm',
254
            array(
255
                'Name' => 'Poster',
256
                'Email' => '[email protected]',
257
                'Comment' => 'My Comment',
258
                'ParentID' => $parent->ID,
259
                'ParentClassName' => CommentableItem::class,
260
                'action_doPostComment' => 'Post'
261
            )
262
        );
263
        $this->assertEquals(302, $response->getStatusCode());
264
        // $this->assertStringStartsWith('CommentableItemController#comment-', $response->getHeader('Location'));
265
        $this->assertDOSEquals(
266
            array(
267
                array(
268
                    'Name' => 'Poster',
269
                    'Email' => '[email protected]',
270
                    'Comment' => 'My Comment',
271
                    'ParentID' => $parent->ID,
272
                    'ParentClass' => CommentableItem::class,
273
                )
274
            ),
275
            Comment::get()->filter('Email', '[email protected]')
276
        );
277
278
        // Test posting to parent comment
279
        $parentComment = $this->objFromFixture(Comment::class, 'firstComA');
280
        $this->assertEquals(0, $parentComment->ChildComments()->count());
281
282
        $response = $this->post(
283
            'comments/reply/' . $parentComment->ID,
284
            array(
285
                'Name' => 'Test Author',
286
                'Email' => '[email protected]',
287
                'Comment' => 'Making a reply to firstComA',
288
                'ParentID' => $parent->ID,
289
                'ParentClassName' => CommentableItem::class,
290
                'ParentCommentID' => $parentComment->ID,
291
                'action_doPostComment' => 'Post'
292
            )
293
        );
294
        $this->assertEquals(302, $response->getStatusCode());
295
        // $this->assertStringStartsWith('CommentableItemController#comment-', $response->getHeader('Location'));
296
        $this->assertDOSEquals(
297
            array(
298
                array(
299
                    'Name' => 'Test Author',
300
                    'Email' => '[email protected]',
301
                    'Comment' => 'Making a reply to firstComA',
302
                    'ParentID' => $parent->ID,
303
                    'ParentClass' => CommentableItem::class,
304
                    'ParentCommentID' => $parentComment->ID
305
                )
306
            ),
307
            $parentComment->ChildComments()
308
        );
309
    }
310
311
    /**
312
     * SS4 introduces namespaces. They don't work in URLs, so we encode and decode them here.
313
     */
314
    public function testEncodeClassName()
315
    {
316
        $controller = new CommentingController;
317
        $this->assertSame('SilverStripe-Comments-Model-Comment', $controller->encodeClassName(Comment::class));
318
    }
319
320
    public function testDecodeClassName()
321
    {
322
        $controller = new CommentingController;
323
        $this->assertSame(Comment::class, $controller->decodeClassName('SilverStripe-Comments-Model-Comment'));
324
    }
325
}
326