Completed
Push — fix/close-button-bg-color ( da7e83...c55173 )
by
unknown
11:17
created

ManagerTest::get_disconnect_user_scenarios()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 19

Duplication

Lines 19
Ratio 100 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 19
loc 19
rs 9.6333
c 0
b 0
f 0
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 );
0 ignored issues
show
Unused Code introduced by
The call to remove_filter() has too many arguments starting with 2.

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...
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' );
0 ignored issues
show
Unused Code introduced by
The call to WP_Error::__construct() has too many arguments starting with 'invalid_token'.

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...
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