Passed
Branch develop (bae466)
by Paul
06:21
created

ValidationTest::test_review_limits_validation()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 26
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 26
rs 9.584
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Tests;
4
5
use Faker\Factory;
6
use GeminiLabs\SiteReviews\Database\OptionManager;
7
use GeminiLabs\SiteReviews\Helper;
8
use GeminiLabs\SiteReviews\Modules\Honeypot;
9
use GeminiLabs\SiteReviews\Modules\Validator\BlacklistValidator;
10
use GeminiLabs\SiteReviews\Modules\Validator\CustomValidator;
11
use GeminiLabs\SiteReviews\Modules\Validator\DefaultValidator;
12
use GeminiLabs\SiteReviews\Modules\Validator\DuplicateValidator;
13
use GeminiLabs\SiteReviews\Modules\Validator\HoneypotValidator;
14
use GeminiLabs\SiteReviews\Modules\Validator\PermissionValidator;
15
use GeminiLabs\SiteReviews\Modules\Validator\ReviewLimitsValidator;
16
use GeminiLabs\SiteReviews\Request;
17
use WPAjaxDieContinueException;
0 ignored issues
show
Bug introduced by
The type WPAjaxDieContinueException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
18
use WPAjaxDieStopException;
0 ignored issues
show
Bug introduced by
The type WPAjaxDieStopException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
use WP_Ajax_UnitTestCase;
0 ignored issues
show
Bug introduced by
The type WP_Ajax_UnitTestCase was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
20
21
class ValidationTest extends WP_Ajax_UnitTestCase
22
{
23
    use Setup;
24
25
    protected $faker;
26
    protected $ipaddress;
27
    protected $messageFailed;
28
    protected $messageFailedBlacklist;
29
    protected $messageFailedCustom;
30
    protected $messageFailedDuplicate;
31
    protected $messageFailedHoneypot;
32
    protected $messageFailedPermission;
33
    protected $messageFailedReviewLimits;
34
    protected $messageFailedValidation;
35
    protected $messageSuccess;
36
    protected $request;
37
38
    public function set_up()
39
    {
40
        parent::set_up();
41
        $this->faker = Factory::create();
42
        $this->ipaddress = Helper::getIpAddress();
43
        $this->request = new Request([
44
            '_action' => 'submit-review',
45
            '_nonce' => wp_create_nonce('submit-review'), // wp_ajax_* is used in tests (?)
46
            '_post_id' => '13',
47
            '_referer' => $this->referer,
48
            'excluded' => '[]',
49
            'form_id' => $this->faker->slug,
50
            'content' => '',
51
            'email' => '',
52
            'name' => '',
53
            'rating' => '',
54
            'terms' => '',
55
            // 'terms_exist' => 1,
56
            'title' => '',
57
        ]);
58
        $this->messageFailed = 'The review submission failed. Please notify the site administrator.';
59
        $this->messageFailedDuplicate = 'Duplicate review detected. It looks like you already said that!';
60
        $this->messageFailedHoneypot = 'This review has been flagged as possible spam and cannot be submitted.';
61
        $this->messageFailedBlacklist = 'Your review cannot be submitted at this time.';
62
        $this->messageFailedCustom = 'Bad review.';
63
        $this->messageFailedPermission = 'You must be logged in to submit a review.';
64
        $this->messageFailedReviewLimits = 'You have already submitted a review.';
65
        $this->messageFailedValidation = 'Please fix the form errors.';
66
        $this->messageSuccess = 'Your review has been submitted!';
67
    }
68
69
    public function test_blacklist_validation()
70
    {
71
        add_filter('site-reviews/validators', fn () => [
72
            BlacklistValidator::class,
73
        ]);
74
        $blacklist = "xxx\n \napple";
75
        $response = $this->assertJsonSuccess($this->request());
76
        $this->assertTrue($response->data->review->is_approved);
77
        $this->assertEquals('publish', $response->data->review->status);
78
        $this->assertEquals($response->data->message, $this->messageSuccess);
79
        glsr(OptionManager::class)->set('settings.forms.blacklist.action', 'reject');
80
        glsr(OptionManager::class)->set('settings.forms.blacklist.entries', $blacklist);
81
        glsr(OptionManager::class)->set('settings.forms.blacklist.integration', '');
82
        $this->assertJsonError($this->request(['content' => 'Give me a xxx!!']));
83
        $this->assertJsonError($this->request(['email' => '[email protected]']));
84
        $this->assertJsonSuccess($this->request(['email' => '[email protected]']));
85
        $this->assertJsonError($this->request(['name' => 'Johnxxx Doe']));
86
        $this->assertJsonError($this->request(['title' => 'This is a xxx title']));
87
        glsr(OptionManager::class)->set('settings.forms.blacklist.entries', "{$blacklist}\n{$this->ipaddress}");
88
        $this->assertJsonError($this->request());
89
        glsr(OptionManager::class)->set('settings.forms.blacklist.integration', 'comments');
90
        $this->assertJsonSuccess($this->request());
91
        update_option('disallowed_keys', $blacklist);
92
        $this->assertJsonError($this->request(['content' => 'Give me a xxx!!']));
93
        $this->assertJsonError($this->request(['email' => '[email protected]']));
94
        $this->assertJsonError($this->request(['name' => 'Johnxxx Doe']));
95
        $this->assertJsonError($this->request(['title' => 'This is a xxx title']));
96
        update_option('disallowed_keys', "{$blacklist}\n{$this->ipaddress}");
97
        $response1 = $this->assertJsonError($this->request());
98
        $this->assertEquals($response1->data->message, $this->messageFailedBlacklist);
99
        update_option('disallowed_keys', $blacklist);
100
        glsr(OptionManager::class)->set('settings.forms.blacklist.action', 'unapprove');
101
        $response2 = $this->assertJsonSuccess($this->request(['email' => '[email protected]']));
102
        $this->assertFalse($response2->data->review->is_approved);
103
        $this->assertEquals('pending', $response2->data->review->status);
104
    }
105
106
    public function test_custom_validation()
107
    {
108
        add_filter('site-reviews/validators', fn () => [
109
            CustomValidator::class,
110
        ]);
111
        $this->assertJsonSuccess($this->request());
112
        add_filter('site-reviews/validate/custom', function () {
113
            return $this->messageFailedCustom;
114
        });
115
        $response1 = $this->assertJsonError($this->request());
116
        $this->assertEquals($response1->data->message, $this->messageFailedCustom);
117
        add_filter('site-reviews/validate/custom', '__return_false', 11);
118
        $response2 = $this->assertJsonError($this->request());
119
        $this->assertEquals($response2->data->message, $this->messageFailed);
120
    }
121
122
    public function test_default_validation()
123
    {
124
        add_filter('site-reviews/validators', fn () => [
125
            DefaultValidator::class,
126
        ]);
127
        glsr(OptionManager::class)->set('settings.forms.required', ['rating', 'title', 'content', 'name', 'email', 'terms']);
128
        $response1 = $this->assertJsonError($this->request());
129
        $response2 = $this->assertJsonSuccess($this->request([
130
            'content' => $this->faker->sentence,
131
            'email' => $this->faker->email,
132
            'name' => $this->faker->name,
133
            'rating' => 5,
134
            'terms' => 1,
135
            'title' => $this->faker->sentence,
136
        ]));
137
        $this->assertCount(6, (array) $response1->data->errors);
138
        $this->assertEquals($response1->data->message, $this->messageFailedValidation);
139
        $this->assertEquals($response2->data->message, $this->messageSuccess);
140
    }
141
142
    public function test_duplicate_validation()
143
    {
144
        add_filter('site-reviews/validators', fn () => [
145
            DuplicateValidator::class,
146
        ]);
147
        $request = $this->request([
148
            'content' => $this->faker->sentence,
149
        ]);
150
        glsr(OptionManager::class)->set('settings.forms.prevent_duplicates', 'yes');
151
        $response1 = $this->assertJsonSuccess($request);
152
        $response2 = $this->assertJsonError($request);
153
        glsr(OptionManager::class)->set('settings.forms.prevent_duplicates', 'no');
154
        $response3 = $this->assertJsonSuccess($request);
155
        $this->assertEquals($response1->data->message, $this->messageSuccess);
156
        $this->assertEquals($response2->data->message, $this->messageFailedDuplicate);
157
        $this->assertEquals($response3->data->message, $this->messageSuccess);
158
    }
159
160
    public function test_multiple_validation()
161
    {
162
        $this->assertJsonError($this->request());
163
    }
164
165
    public function test_honeypot_validation()
166
    {
167
        add_filter('site-reviews/validators', fn () => [
168
            HoneypotValidator::class,
169
        ]);
170
        $formId = 'glsr-12345678';
171
        $honeypotHash = glsr(Honeypot::class)->hash($formId);
172
        $response1 = $this->assertJsonError($this->request());
173
        $response2 = $this->assertJsonError($this->request(['form_id' => $formId, $honeypotHash => 'x']));
174
        $response3 = $this->assertJsonSuccess($this->request(['form_id' => $formId, $honeypotHash => '']));
175
        $this->assertEquals($response1->data->message, $this->messageFailedHoneypot);
176
        $this->assertEquals($response2->data->message, $this->messageFailedHoneypot);
177
        $this->assertEquals($response3->data->message, $this->messageSuccess);
178
    }
179
180
    public function test_permission_validation()
181
    {
182
        add_filter('site-reviews/validators', fn () => [
183
            PermissionValidator::class,
184
        ]);
185
        $response1 = $this->assertJsonSuccess($this->request());
186
        glsr(OptionManager::class)->set('settings.general.require.login', 'yes');
187
        $response2 = $this->assertJsonError($this->request());
188
        wp_set_current_user(self::factory()->user->create([
189
            'role' => 'editor',
190
        ]));
191
        $response3 = $this->assertJsonSuccess($this->request([
192
            '_nonce' => wp_create_nonce('submit-review'),
193
        ]));
194
        $this->assertEquals($response1->data->message, $this->messageSuccess);
195
        $this->assertEquals($response2->data->message, $this->messageFailedPermission);
196
        $this->assertEquals($response3->data->message, $this->messageSuccess);
197
    }
198
199
    public function test_review_limits_validation()
200
    {
201
        add_filter('site-reviews/validators', fn () => [
202
            ReviewLimitsValidator::class,
203
        ]);
204
        $this->assertJsonSuccess($this->request());
205
        glsr(OptionManager::class)->set('settings.forms.limit', 'ip_address');
206
        $this->assertJsonError($this->request());
207
        glsr(OptionManager::class)->set('settings.forms.limit', 'email');
208
        $request = $this->request([
209
            'email' => $this->faker->email,
210
        ]);
211
        $this->assertJsonSuccess($request);
212
        $this->assertJsonError($request);
213
        glsr(OptionManager::class)->set('settings.forms.limit_whitelist.email', $request['email']);
214
        $this->assertJsonSuccess($request);
215
        glsr(OptionManager::class)->set('settings.forms.limit', 'username');
216
        $this->assertJsonSuccess($this->request());
217
        wp_set_current_user(self::factory()->user->create([
218
            'role' => 'editor',
219
            'user_login' => 'test_user',
220
        ]));
221
        $nonce = wp_create_nonce('submit-review');
222
        $this->assertJsonSuccess($this->request(['_nonce' => $nonce]));
223
        $this->assertJsonError($this->request(['_nonce' => $nonce]));
224
        $this->assertJsonError($this->request(['_nonce' => $nonce]));
225
    }
226
227
    /**
228
     * @return object
229
     */
230
    protected function assertJsonError(array $request)
231
    {
232
        $response = $this->performAjaxRequest($request);
233
        $properties = get_object_vars($response);
234
        $this->assertArrayHasKey('success', $properties);
235
        $this->assertFalse($response->success);
236
        return $response;
237
    }
238
239
    /**
240
     * @return object
241
     */
242
    protected function assertJsonSuccess(array $request)
243
    {
244
        $response = $this->performAjaxRequest($request);
245
        $properties = get_object_vars($response);
246
        $this->assertArrayHasKey('success', $properties);
247
        $this->assertTrue($response->success);
248
        return $response;
249
    }
250
251
    /**
252
     * @return object
253
     */
254
    protected function performAjaxRequest($request)
255
    {
256
        // error_log(print_r($request, 1));
257
        $action = glsr()->prefix.'public_action';
258
        $_POST['_ajax_request'] = true;
259
        $_POST[glsr()->id] = $request;
260
        try {
261
            $this->_handleAjax($action);
262
        } catch (WPAjaxDieContinueException $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
263
        } catch (WPAjaxDieStopException $e) {
264
            error_log(print_r('WPAjaxDieStopException: '.$e->getMessage(), true));
0 ignored issues
show
Bug introduced by
It seems like print_r('WPAjaxDieStopEx...$e->getMessage(), true) can also be of type true; however, parameter $message of error_log() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

264
            error_log(/** @scrutinizer ignore-type */ print_r('WPAjaxDieStopException: '.$e->getMessage(), true));
Loading history...
265
        }
266
        $response = json_decode($this->_last_response);
267
        // Empty _last_response so we can call ajax_response more than once in the same method.
268
        $this->_last_response = '';
0 ignored issues
show
Bug Best Practice introduced by
The property _last_response does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
269
        $this->assertIsObject($response);
270
        return $response;
271
    }
272
273
    /**
274
     * @return array
275
     */
276
    protected function request(array $mergeWith = []): array
277
    {
278
        $request = clone $this->request;
279
        return $request->merge($mergeWith)->toArray();
280
    }
281
}
282