1
|
|
|
<?php // phpcs:ignore WordPress.Files.FileName.NotHyphenatedLowercase |
2
|
|
|
/** |
3
|
|
|
* Connection Manager functionality testing. |
4
|
|
|
* |
5
|
|
|
* @package automattic/jetpack-connection |
6
|
|
|
*/ |
7
|
|
|
|
8
|
|
|
namespace Automattic\Jetpack\Connection; |
9
|
|
|
|
10
|
|
|
use Automattic\Jetpack\Constants; |
11
|
|
|
use PHPUnit\Framework\TestCase; |
12
|
|
|
use WorDBless\Users as WorDBless_Users; |
13
|
|
|
use WP_Error; |
14
|
|
|
|
15
|
|
|
/** |
16
|
|
|
* Connection Manager functionality testing. |
17
|
|
|
*/ |
18
|
|
|
class ManagerTest extends TestCase { |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Temporary stack for `wp_redirect`. |
22
|
|
|
* |
23
|
|
|
* @var array |
24
|
|
|
*/ |
25
|
|
|
protected $arguments_stack = array(); |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* User ID added for the test. |
29
|
|
|
* |
30
|
|
|
* @var int |
31
|
|
|
*/ |
32
|
|
|
protected $user_id; |
33
|
|
|
|
34
|
|
|
const DEFAULT_TEST_CAPS = array( 'default_test_caps' ); |
35
|
|
|
|
36
|
|
|
/** |
37
|
|
|
* Initialize the object before running the test method. |
38
|
|
|
* |
39
|
|
|
* @before |
40
|
|
|
*/ |
41
|
|
|
public function set_up() { |
42
|
|
|
$this->manager = $this->getMockBuilder( 'Automattic\Jetpack\Connection\Manager' ) |
43
|
|
|
->setMethods( array( 'get_tokens', 'get_connection_owner_id', 'unlink_user_from_wpcom' ) ) |
44
|
|
|
->getMock(); |
45
|
|
|
|
46
|
|
|
$this->tokens = $this->getMockBuilder( 'Automattic\Jetpack\Connection\Tokens' ) |
47
|
|
|
->setMethods( array( 'get_access_token', 'disconnect_user' ) ) |
48
|
|
|
->getMock(); |
49
|
|
|
|
50
|
|
|
$this->manager->method( 'get_tokens' )->will( $this->returnValue( $this->tokens ) ); |
51
|
|
|
|
52
|
|
|
$this->user_id = wp_insert_user( |
53
|
|
|
array( |
54
|
|
|
'user_login' => 'test_is_user_connected_with_user_id_logged_in', |
55
|
|
|
'user_pass' => '123', |
56
|
|
|
) |
57
|
|
|
); |
58
|
|
|
wp_set_current_user( 0 ); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* Clean up the testing environment. |
63
|
|
|
* |
64
|
|
|
* @after |
65
|
|
|
*/ |
66
|
|
|
public function tear_down() { |
67
|
|
|
wp_set_current_user( 0 ); |
68
|
|
|
WorDBless_Users::init()->clear_all_users(); |
69
|
|
|
unset( $this->manager ); |
70
|
|
|
unset( $this->tokens ); |
71
|
|
|
Constants::clear_constants(); |
72
|
|
|
} |
73
|
|
|
|
74
|
|
|
/** |
75
|
|
|
* Test the `is_active` functionality when connected. |
76
|
|
|
* |
77
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_active |
78
|
|
|
*/ |
79
|
|
View Code Duplication |
public function test_is_active_when_connected() { |
80
|
|
|
$access_token = (object) array( |
81
|
|
|
'secret' => 'abcd1234', |
82
|
|
|
'external_user_id' => 1, |
83
|
|
|
); |
84
|
|
|
$this->tokens->expects( $this->once() ) |
85
|
|
|
->method( 'get_access_token' ) |
86
|
|
|
->will( $this->returnValue( $access_token ) ); |
87
|
|
|
|
88
|
|
|
$this->assertTrue( $this->manager->is_active() ); |
89
|
|
|
} |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* Test the `is_active` functionality when not connected. |
93
|
|
|
* |
94
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_active |
95
|
|
|
*/ |
96
|
|
|
public function test_is_active_when_not_connected() { |
97
|
|
|
$this->tokens->expects( $this->once() ) |
98
|
|
|
->method( 'get_access_token' ) |
99
|
|
|
->will( $this->returnValue( false ) ); |
100
|
|
|
|
101
|
|
|
$this->assertFalse( $this->manager->is_active() ); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Test the `api_url` generation. |
106
|
|
|
* |
107
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::api_url |
108
|
|
|
*/ |
109
|
|
|
public function test_api_url_defaults() { |
110
|
|
|
add_filter( 'jetpack_constant_default_value', array( $this, 'filter_api_constant' ), 10, 2 ); |
111
|
|
|
|
112
|
|
|
$this->assertEquals( |
113
|
|
|
'https://jetpack.wordpress.com/jetpack.something/1/', |
114
|
|
|
$this->manager->api_url( 'something' ) |
115
|
|
|
); |
116
|
|
|
$this->assertEquals( |
117
|
|
|
'https://jetpack.wordpress.com/jetpack.another_thing/1/', |
118
|
|
|
$this->manager->api_url( 'another_thing/' ) |
119
|
|
|
); |
120
|
|
|
|
121
|
|
|
remove_filter( 'jetpack_constant_default_value', array( $this, 'filter_api_constant' ), 10, 2 ); |
|
|
|
|
122
|
|
|
} |
123
|
|
|
|
124
|
|
|
/** |
125
|
|
|
* Testing the ability of the api_url method to follow set constants and filters. |
126
|
|
|
* |
127
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::api_url |
128
|
|
|
*/ |
129
|
|
|
public function test_api_url_uses_constants_and_filters() { |
130
|
|
|
Constants::set_constant( 'JETPACK__API_BASE', 'https://example.com/api/base.' ); |
131
|
|
|
Constants::set_constant( 'JETPACK__API_VERSION', '1' ); |
132
|
|
|
$this->assertEquals( |
133
|
|
|
'https://example.com/api/base.something/1/', |
134
|
|
|
$this->manager->api_url( 'something' ) |
135
|
|
|
); |
136
|
|
|
|
137
|
|
|
Constants::set_constant( 'JETPACK__API_BASE', 'https://example.com/api/another.' ); |
138
|
|
|
Constants::set_constant( 'JETPACK__API_VERSION', '99' ); |
139
|
|
|
$this->assertEquals( |
140
|
|
|
'https://example.com/api/another.something/99/', |
141
|
|
|
$this->manager->api_url( 'something' ) |
142
|
|
|
); |
143
|
|
|
|
144
|
|
|
$overwrite_filter = function () { |
145
|
|
|
$this->arguments_stack['jetpack_api_url'][] = array_merge( array( 'jetpack_api_url' ), func_get_args() ); |
146
|
|
|
return 'completely overwrite'; |
147
|
|
|
}; |
148
|
|
|
add_filter( 'jetpack_api_url', $overwrite_filter, 10, 4 ); |
149
|
|
|
|
150
|
|
|
$this->assertEquals( |
151
|
|
|
'completely overwrite', |
152
|
|
|
$this->manager->api_url( 'something' ) |
153
|
|
|
); |
154
|
|
|
|
155
|
|
|
// The jetpack_api_url argument stack should not be empty, making sure the filter was |
156
|
|
|
// called with a proper name and arguments. |
157
|
|
|
$call_arguments = array_pop( $this->arguments_stack['jetpack_api_url'] ); |
158
|
|
|
$this->assertEquals( 'something', $call_arguments[2] ); |
159
|
|
|
$this->assertEquals( |
160
|
|
|
Constants::get_constant( 'JETPACK__API_BASE' ), |
161
|
|
|
$call_arguments[3] |
162
|
|
|
); |
163
|
|
|
$this->assertEquals( |
164
|
|
|
'/' . Constants::get_constant( 'JETPACK__API_VERSION' ) . '/', |
165
|
|
|
$call_arguments[4] |
166
|
|
|
); |
167
|
|
|
|
168
|
|
|
remove_filter( 'jetpack_api_url', $overwrite_filter, 10 ); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* Test the `is_user_connected` functionality. |
173
|
|
|
* |
174
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_user_connected |
175
|
|
|
*/ |
176
|
|
|
public function test_is_user_connected_with_default_user_id_logged_out() { |
177
|
|
|
$this->assertFalse( $this->manager->is_user_connected() ); |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Test the `is_user_connected` functionality. |
182
|
|
|
* |
183
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_user_connected |
184
|
|
|
*/ |
185
|
|
|
public function test_is_user_connected_with_false_user_id_logged_out() { |
186
|
|
|
$this->assertFalse( $this->manager->is_user_connected( false ) ); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
/** |
190
|
|
|
* Test the `is_user_connected` functionality |
191
|
|
|
* |
192
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_user_connected |
193
|
|
|
*/ |
194
|
|
|
public function test_is_user_connected_with_user_id_logged_out_not_connected() { |
195
|
|
|
$this->tokens->expects( $this->once() ) |
196
|
|
|
->method( 'get_access_token' ) |
197
|
|
|
->will( $this->returnValue( false ) ); |
198
|
|
|
|
199
|
|
|
$this->assertFalse( $this->manager->is_user_connected( $this->user_id ) ); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
/** |
203
|
|
|
* Test the `is_user_connected` functionality. |
204
|
|
|
* |
205
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_user_connected |
206
|
|
|
*/ |
207
|
|
View Code Duplication |
public function test_is_user_connected_with_default_user_id_logged_in() { |
208
|
|
|
wp_set_current_user( $this->user_id ); |
209
|
|
|
|
210
|
|
|
$access_token = (object) array( |
211
|
|
|
'secret' => 'abcd1234', |
212
|
|
|
'external_user_id' => 1, |
213
|
|
|
); |
214
|
|
|
$this->tokens->expects( $this->once() ) |
215
|
|
|
->method( 'get_access_token' ) |
216
|
|
|
->will( $this->returnValue( $access_token ) ); |
217
|
|
|
|
218
|
|
|
$this->assertTrue( $this->manager->is_user_connected() ); |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Test the `is_user_connected` functionality. |
223
|
|
|
* |
224
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::is_user_connected |
225
|
|
|
*/ |
226
|
|
View Code Duplication |
public function test_is_user_connected_with_user_id_logged_in() { |
227
|
|
|
$access_token = (object) array( |
228
|
|
|
'secret' => 'abcd1234', |
229
|
|
|
'external_user_id' => 1, |
230
|
|
|
); |
231
|
|
|
$this->tokens->expects( $this->once() ) |
232
|
|
|
->method( 'get_access_token' ) |
233
|
|
|
->will( $this->returnValue( $access_token ) ); |
234
|
|
|
|
235
|
|
|
$this->assertTrue( $this->manager->is_user_connected( $this->user_id ) ); |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Unit test for the "Delete all tokens" functionality. |
240
|
|
|
* |
241
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::delete_all_connection_tokens |
242
|
|
|
*/ |
243
|
|
View Code Duplication |
public function test_delete_all_connection_tokens() { |
244
|
|
|
( new Plugin( 'plugin-slug-1' ) )->add( 'Plugin Name 1' ); |
245
|
|
|
|
246
|
|
|
( new Plugin( 'plugin-slug-2' ) )->add( 'Plugin Name 2' ); |
247
|
|
|
|
248
|
|
|
$stub = $this->createMock( Plugin::class ); |
249
|
|
|
$stub->method( 'is_only' ) |
250
|
|
|
->willReturn( false ); |
251
|
|
|
$manager = ( new Manager() )->set_plugin_instance( $stub ); |
252
|
|
|
|
253
|
|
|
$this->assertFalse( $manager->delete_all_connection_tokens() ); |
254
|
|
|
} |
255
|
|
|
|
256
|
|
|
/** |
257
|
|
|
* Unit test for the "Disconnect from WP" functionality. |
258
|
|
|
* |
259
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::disconnect_site_wpcom |
260
|
|
|
*/ |
261
|
|
View Code Duplication |
public function test_disconnect_site_wpcom() { |
262
|
|
|
( new Plugin( 'plugin-slug-1' ) )->add( 'Plugin Name 1' ); |
263
|
|
|
|
264
|
|
|
( new Plugin( 'plugin-slug-2' ) )->add( 'Plugin Name 2' ); |
265
|
|
|
|
266
|
|
|
$stub = $this->createMock( Plugin::class ); |
267
|
|
|
$stub->method( 'is_only' ) |
268
|
|
|
->willReturn( false ); |
269
|
|
|
$manager = ( new Manager() )->set_plugin_instance( $stub ); |
270
|
|
|
|
271
|
|
|
$this->assertFalse( $manager->disconnect_site_wpcom() ); |
272
|
|
|
} |
273
|
|
|
|
274
|
|
|
/** |
275
|
|
|
* Test the `jetpack_connection_custom_caps' method. |
276
|
|
|
* |
277
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::jetpack_connection_custom_caps |
278
|
|
|
* @dataProvider jetpack_connection_custom_caps_data_provider |
279
|
|
|
* |
280
|
|
|
* @param bool $in_offline_mode Whether offline mode is active. |
281
|
|
|
* @param bool $owner_exists Whether a connection owner exists. |
282
|
|
|
* @param string $custom_cap The custom capability that is being tested. |
283
|
|
|
* @param array $expected_caps The expected output. |
284
|
|
|
*/ |
285
|
|
|
public function test_jetpack_connection_custom_caps( $in_offline_mode, $owner_exists, $custom_cap, $expected_caps ) { |
286
|
|
|
// Mock the apply_filters( 'jetpack_offline_mode', ) call in Status::is_offline_mode. |
287
|
|
|
add_filter( |
288
|
|
|
'jetpack_offline_mode', |
289
|
|
|
function () use ( $in_offline_mode ) { |
290
|
|
|
return $in_offline_mode; |
291
|
|
|
} |
292
|
|
|
); |
293
|
|
|
|
294
|
|
|
$this->manager->method( 'get_connection_owner_id' ) |
295
|
|
|
->withAnyParameters() |
296
|
|
|
->willReturn( $owner_exists ); // 0 or 1 is alright for our testing purposes. |
297
|
|
|
|
298
|
|
|
$caps = $this->manager->jetpack_connection_custom_caps( self::DEFAULT_TEST_CAPS, $custom_cap, 1, array() ); |
299
|
|
|
$this->assertEquals( $expected_caps, $caps ); |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
/** |
303
|
|
|
* Data provider test_jetpack_connection_custom_caps. |
304
|
|
|
* |
305
|
|
|
* Structure of the test data arrays: |
306
|
|
|
* [0] => 'in_offline_mode' boolean Whether offline mode is active. |
307
|
|
|
* [1] => 'owner_exists' boolean Whether a connection owner exists. |
308
|
|
|
* [2] => 'custom_cap' string The custom capability that is being tested. |
309
|
|
|
* [3] => 'expected_caps' array The expected output of the call to jetpack_connection_custom_caps. |
310
|
|
|
*/ |
311
|
|
|
public function jetpack_connection_custom_caps_data_provider() { |
312
|
|
|
|
313
|
|
|
return array( |
314
|
|
|
'offline mode, owner exists, jetpack_connect' => array( true, true, 'jetpack_connect', array( 'do_not_allow' ) ), |
315
|
|
|
'offline mode, owner exists, jetpack_reconnect' => array( true, true, 'jetpack_reconnect', array( 'do_not_allow' ) ), |
316
|
|
|
'offline mode, owner exists, jetpack_disconnect' => array( true, true, 'jetpack_disconnect', array( 'manage_options' ) ), |
317
|
|
|
'offline mode, owner exists, jetpack_connect_user' => array( true, true, 'jetpack_connect_user', array( 'do_not_allow' ) ), |
318
|
|
|
'offline mode, no owner, jetpack_connect_user' => array( true, false, 'jetpack_connect_user', array( 'do_not_allow' ) ), |
319
|
|
|
'offline mode, owner exists, unknown cap' => array( true, true, 'unknown_cap', self::DEFAULT_TEST_CAPS ), |
320
|
|
|
'not offline mode, owner exists, jetpack_connect' => array( false, true, 'jetpack_connect', array( 'manage_options' ) ), |
321
|
|
|
'not offline mode, owner exists, jetpack_reconnect' => array( false, true, 'jetpack_reconnect', array( 'manage_options' ) ), |
322
|
|
|
'not offline mode, owner exists, jetpack_disconnect' => array( false, true, 'jetpack_disconnect', array( 'manage_options' ) ), |
323
|
|
|
'not offline mode, owner exists, jetpack_connect_user' => array( false, true, 'jetpack_connect_user', array( 'read' ) ), |
324
|
|
|
'not offline mode, no owner, jetpack_connect_user' => array( false, false, 'jetpack_connect_user', array( 'manage_options' ) ), |
325
|
|
|
'not offline mode, owner exists, unknown cap' => array( false, true, 'unknown_cap', self::DEFAULT_TEST_CAPS ), |
326
|
|
|
); |
327
|
|
|
} |
328
|
|
|
|
329
|
|
|
/** |
330
|
|
|
* Test the `get_signed_token` functionality. |
331
|
|
|
* |
332
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::get_signed_token |
333
|
|
|
*/ |
334
|
|
View Code Duplication |
public function test_get_signed_token() { |
335
|
|
|
$access_token = (object) array( |
336
|
|
|
'external_user_id' => 1, |
337
|
|
|
); |
338
|
|
|
|
339
|
|
|
// Missing secret. |
340
|
|
|
$invalid_token_error = new WP_Error( 'invalid_token' ); |
|
|
|
|
341
|
|
|
$this->assertEquals( $invalid_token_error, ( new Tokens() )->get_signed_token( $access_token ) ); |
342
|
|
|
// Secret is null. |
343
|
|
|
$access_token->secret = null; |
344
|
|
|
$this->assertEquals( $invalid_token_error, ( new Tokens() )->get_signed_token( $access_token ) ); |
345
|
|
|
// Secret is empty. |
346
|
|
|
$access_token->secret = ''; |
347
|
|
|
$this->assertEquals( $invalid_token_error, ( new Tokens() )->get_signed_token( $access_token ) ); |
348
|
|
|
// Valid secret. |
349
|
|
|
$access_token->secret = 'abcd.1234'; |
350
|
|
|
|
351
|
|
|
$signed_token = ( new Tokens() )->get_signed_token( $access_token ); |
352
|
|
|
$this->assertTrue( strpos( $signed_token, 'token' ) !== false ); |
353
|
|
|
$this->assertTrue( strpos( $signed_token, 'timestamp' ) !== false ); |
354
|
|
|
$this->assertTrue( strpos( $signed_token, 'nonce' ) !== false ); |
355
|
|
|
$this->assertTrue( strpos( $signed_token, 'signature' ) !== false ); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
/** |
359
|
|
|
* Test disconnecting a user from WordPress.com. |
360
|
|
|
* |
361
|
|
|
* @covers Automattic\Jetpack\Connection\Manager::disconnect_user |
362
|
|
|
* @dataProvider get_disconnect_user_scenarios |
363
|
|
|
* |
364
|
|
|
* @param bool $remote Was the remote disconnection successful. |
365
|
|
|
* @param bool $local Was the remote disconnection successful. |
366
|
|
|
* @param bool $expected Expected outcome. |
367
|
|
|
*/ |
368
|
|
|
public function test_disconnect_user( $remote, $local, $expected ) { |
369
|
|
|
$editor_id = wp_insert_user( |
370
|
|
|
array( |
371
|
|
|
'user_login' => 'editor', |
372
|
|
|
'user_pass' => 'pass', |
373
|
|
|
'user_email' => '[email protected]', |
374
|
|
|
'role' => 'editor', |
375
|
|
|
) |
376
|
|
|
); |
377
|
|
|
( new Tokens() )->update_user_token( $editor_id, sprintf( '%s.%s.%d', 'key', 'private', $editor_id ), false ); |
378
|
|
|
|
379
|
|
|
$this->manager->method( 'unlink_user_from_wpcom' ) |
380
|
|
|
->will( $this->returnValue( $remote ) ); |
381
|
|
|
|
382
|
|
|
$this->tokens->method( 'disconnect_user' ) |
383
|
|
|
->will( $this->returnValue( $local ) ); |
384
|
|
|
|
385
|
|
|
$result = $this->manager->disconnect_user( $editor_id ); |
386
|
|
|
|
387
|
|
|
$this->assertEquals( $expected, $result ); |
388
|
|
|
} |
389
|
|
|
|
390
|
|
|
/** |
391
|
|
|
* Test data for test_disconnect_user |
392
|
|
|
* |
393
|
|
|
* @return array |
394
|
|
|
*/ |
395
|
|
View Code Duplication |
public function get_disconnect_user_scenarios() { |
396
|
|
|
return array( |
397
|
|
|
'Successful remote and local disconnection' => array( |
398
|
|
|
true, |
399
|
|
|
true, |
400
|
|
|
true, |
401
|
|
|
), |
402
|
|
|
'Failed remote and successful local disconnection' => array( |
403
|
|
|
false, |
404
|
|
|
true, |
405
|
|
|
false, |
406
|
|
|
), |
407
|
|
|
'Successful remote and failed local disconnection' => array( |
408
|
|
|
true, |
409
|
|
|
false, |
410
|
|
|
false, |
411
|
|
|
), |
412
|
|
|
); |
413
|
|
|
} |
414
|
|
|
|
415
|
|
|
/** |
416
|
|
|
* Filter to set the default constant values. |
417
|
|
|
* |
418
|
|
|
* @param string $value Existing value (empty and ignored). |
419
|
|
|
* @param string $name Constant name. |
420
|
|
|
* |
421
|
|
|
* @see Utils::DEFAULT_JETPACK__API_BASE |
422
|
|
|
* @see Utils::DEFAULT_JETPACK__API_VERSION |
423
|
|
|
* |
424
|
|
|
* @return string |
425
|
|
|
*/ |
426
|
|
|
public function filter_api_constant( $value, $name ) { |
427
|
|
|
return constant( __NAMESPACE__ . "\Utils::DEFAULT_$name" ); |
428
|
|
|
} |
429
|
|
|
|
430
|
|
|
} |
431
|
|
|
|
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.