Completed
Push — update/affiliate-code-tracking ( 5e3dbc...15c98b )
by
unknown
151:41 queued 141:12
created

ManagerTest::test_get_signed_token()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 24
rs 9.536
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
require_once __DIR__ . '/mock/trait-options.php';
11
require_once __DIR__ . '/mock/trait-hooks.php';
12
13
use Automattic\Jetpack\Connection\Test\Mock\Hooks;
14
use Automattic\Jetpack\Connection\Test\Mock\Options;
15
use Automattic\Jetpack\Constants;
16
use phpmock\Mock;
17
use phpmock\MockBuilder;
18
use phpmock\MockEnabledException;
19
use PHPUnit\Framework\TestCase;
20
use WorDBless\Options as WorDBless_Options;
21
use WP_Error;
22
23
/**
24
 * Connection Manager functionality testing.
25
 */
26
class ManagerTest extends TestCase {
27
28
	use Options, Hooks;
29
30
	/**
31
	 * Temporary stack for `wp_redirect`.
32
	 *
33
	 * @var array
34
	 */
35
	protected $arguments_stack = array();
36
37
	const DEFAULT_TEST_CAPS = array( 'default_test_caps' );
38
39
	/**
40
	 * Initialize the object before running the test method.
41
	 */
42
	public function setUp() {
43
		$this->manager = $this->getMockBuilder( 'Automattic\Jetpack\Connection\Manager' )
44
			->setMethods( array( 'get_access_token' ) )
45
			->getMock();
46
47
		$builder = new MockBuilder();
48
		$builder->setNamespace( __NAMESPACE__ )
49
				->setName( 'apply_filters' )
50
				->setFunction(
51
					function( $filter_name, $return_value ) {
52
						return $return_value;
53
					}
54
				);
55
56
		$this->apply_filters            = $builder->build();
57
		$this->apply_filters_deprecated = $builder->build();
58
59
		$builder = new MockBuilder();
60
		$builder->setNamespace( __NAMESPACE__ )
61
				->setName( 'wp_redirect' )
62
				->setFunction(
63
					function( $url ) {
64
						$this->arguments_stack['wp_redirect'] [] = array( $url );
65
						return true;
66
					}
67
				);
68
69
		$this->wp_redirect = $builder->build();
70
71
		// Mock the apply_filters() call in Constants::get_constant().
72
		$builder = new MockBuilder();
73
		$builder->setNamespace( 'Automattic\Jetpack' )
74
				->setName( 'apply_filters' )
75
				->setFunction(
76
					function( $filter_name, $value, $name ) {
77
						return constant( __NAMESPACE__ . "\Utils::DEFAULT_$name" );
78
					}
79
				);
80
		$this->constants_apply_filters = $builder->build();
81
82
		$this->build_mock_options();
83
		$this->build_mock_actions();
84
	}
85
86
	/**
87
	 * Clean up the testing environment.
88
	 */
89
	public function tearDown() {
90
		WorDBless_Options::init()->clear_options();
91
		unset( $this->manager );
92
		Constants::clear_constants();
93
		Mock::disableAll();
94
	}
95
96
	/**
97
	 * Test the `is_active` functionality when connected.
98
	 *
99
	 * @covers Automattic\Jetpack\Connection\Manager::is_active
100
	 */
101
	public function test_is_active_when_connected() {
102
		$access_token = (object) array(
103
			'secret'           => 'abcd1234',
104
			'external_user_id' => 1,
105
		);
106
		$this->manager->expects( $this->once() )
107
			->method( 'get_access_token' )
108
			->will( $this->returnValue( $access_token ) );
109
110
		$this->assertTrue( $this->manager->is_active() );
111
	}
112
113
	/**
114
	 * Test the `is_active` functionality when not connected.
115
	 *
116
	 * @covers Automattic\Jetpack\Connection\Manager::is_active
117
	 */
118
	public function test_is_active_when_not_connected() {
119
		$this->manager->expects( $this->once() )
120
			->method( 'get_access_token' )
121
			->will( $this->returnValue( false ) );
122
123
		$this->assertFalse( $this->manager->is_active() );
124
	}
125
126
	/**
127
	 * Test the `api_url` generation.
128
	 *
129
	 * @covers Automattic\Jetpack\Connection\Manager::api_url
130
	 */
131
	public function test_api_url_defaults() {
132
		$this->apply_filters->enable();
133
134
		add_filter( 'jetpack_constant_default_value', array( $this, 'filter_api_constant' ), 10, 2 );
135
136
		$this->assertEquals(
137
			'https://jetpack.wordpress.com/jetpack.something/1/',
138
			$this->manager->api_url( 'something' )
139
		);
140
		$this->assertEquals(
141
			'https://jetpack.wordpress.com/jetpack.another_thing/1/',
142
			$this->manager->api_url( 'another_thing/' )
143
		);
144
145
		remove_filter( 'jetpack_constant_default_value', array( $this, 'filter_api_constant' ), 10, 2 );
146
	}
147
148
	/**
149
	 * Testing the ability of the api_url method to follow set constants and filters.
150
	 *
151
	 * @covers Automattic\Jetpack\Connection\Manager::api_url
152
	 */
153
	public function test_api_url_uses_constants_and_filters() {
154
		$this->apply_filters->enable();
155
		$this->constants_apply_filters->enable();
156
157
		Constants::set_constant( 'JETPACK__API_BASE', 'https://example.com/api/base.' );
158
		Constants::set_constant( 'JETPACK__API_VERSION', '1' );
159
		$this->assertEquals(
160
			'https://example.com/api/base.something/1/',
161
			$this->manager->api_url( 'something' )
162
		);
163
164
		Constants::set_constant( 'JETPACK__API_BASE', 'https://example.com/api/another.' );
165
		Constants::set_constant( 'JETPACK__API_VERSION', '99' );
166
		$this->assertEquals(
167
			'https://example.com/api/another.something/99/',
168
			$this->manager->api_url( 'something' )
169
		);
170
171
		$this->apply_filters->disable();
172
173
		$overwrite_filter = function() {
174
			$this->arguments_stack['jetpack_api_url'][] = array_merge( array( 'jetpack_api_url' ), func_get_args() );
175
			return 'completely overwrite';
176
		};
177
		add_filter( 'jetpack_api_url', $overwrite_filter, 10, 4 );
178
179
		$this->assertEquals(
180
			'completely overwrite',
181
			$this->manager->api_url( 'something' )
182
		);
183
184
		// The jetpack_api_url argument stack should not be empty, making sure the filter was
185
		// called with a proper name and arguments.
186
		$call_arguments = array_pop( $this->arguments_stack['jetpack_api_url'] );
187
		$this->assertEquals( 'something', $call_arguments[2] );
188
		$this->assertEquals(
189
			Constants::get_constant( 'JETPACK__API_BASE' ),
190
			$call_arguments[3]
191
		);
192
		$this->assertEquals(
193
			'/' . Constants::get_constant( 'JETPACK__API_VERSION' ) . '/',
194
			$call_arguments[4]
195
		);
196
197
		remove_filter( 'jetpack_api_url', $overwrite_filter, 10 );
198
	}
199
200
	/**
201
	 * Test the `is_user_connected` functionality.
202
	 *
203
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
204
	 */
205
	public function test_is_user_connected_with_default_user_id_logged_out() {
206
		$this->mock_function( 'get_current_user_id', 0 );
207
208
		$this->assertFalse( $this->manager->is_user_connected() );
209
	}
210
211
	/**
212
	 * Test the `is_user_connected` functionality.
213
	 *
214
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
215
	 */
216
	public function test_is_user_connected_with_false_user_id_logged_out() {
217
		$this->mock_function( 'get_current_user_id', 0 );
218
219
		$this->assertFalse( $this->manager->is_user_connected( false ) );
220
	}
221
222
	/**
223
	 * Test the `is_user_connected` functionality
224
	 *
225
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
226
	 */
227
	public function test_is_user_connected_with_user_id_logged_out_not_connected() {
228
		$this->mock_function( 'absint', 1 );
229
		$this->manager->expects( $this->once() )
230
			->method( 'get_access_token' )
231
			->will( $this->returnValue( false ) );
232
233
		$this->assertFalse( $this->manager->is_user_connected( 1 ) );
234
	}
235
236
237
	/**
238
	 * Test the `is_user_connected` functionality.
239
	 *
240
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
241
	 */
242 View Code Duplication
	public function test_is_user_connected_with_default_user_id_logged_in() {
243
		$this->mock_function( 'get_current_user_id', 1 );
244
		$access_token = (object) array(
245
			'secret'           => 'abcd1234',
246
			'external_user_id' => 1,
247
		);
248
		$this->manager->expects( $this->once() )
249
			->method( 'get_access_token' )
250
			->will( $this->returnValue( $access_token ) );
251
252
		$this->assertTrue( $this->manager->is_user_connected() );
253
	}
254
255
	/**
256
	 * Test the `is_user_connected` functionality.
257
	 *
258
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
259
	 */
260 View Code Duplication
	public function test_is_user_connected_with_user_id_logged_in() {
261
		$this->mock_function( 'absint', 1 );
262
		$access_token = (object) array(
263
			'secret'           => 'abcd1234',
264
			'external_user_id' => 1,
265
		);
266
		$this->manager->expects( $this->once() )
267
			->method( 'get_access_token' )
268
			->will( $this->returnValue( $access_token ) );
269
270
		$this->assertTrue( $this->manager->is_user_connected( 1 ) );
271
	}
272
273
	/**
274
	 * Unit test for the "Delete all tokens" functionality.
275
	 *
276
	 * @covers Automattic\Jetpack\Connection\Manager::delete_all_connection_tokens
277
	 * @throws MockEnabledException PHPUnit wasn't able to enable mock functions.
278
	 */
279 View Code Duplication
	public function test_delete_all_connection_tokens() {
280
		$this->update_option->enable();
281
		$this->get_option->enable();
282
		$this->apply_filters->enable();
283
		$this->do_action->enable();
284
285
		( new Plugin( 'plugin-slug-1' ) )->add( 'Plugin Name 1' );
286
287
		( new Plugin( 'plugin-slug-2' ) )->add( 'Plugin Name 2' );
288
289
		$stub = $this->createMock( Plugin::class );
290
		$stub->method( 'is_only' )
291
			->willReturn( false );
292
		$manager = ( new Manager() )->set_plugin_instance( $stub );
293
294
		$this->assertFalse( $manager->delete_all_connection_tokens() );
295
	}
296
297
	/**
298
	 * Unit test for the "Disconnect from WP" functionality.
299
	 *
300
	 * @covers Automattic\Jetpack\Connection\Manager::disconnect_site_wpcom
301
	 * @throws MockEnabledException PHPUnit wasn't able to enable mock functions.
302
	 */
303 View Code Duplication
	public function test_disconnect_site_wpcom() {
304
		$this->update_option->enable();
305
		$this->get_option->enable();
306
		$this->apply_filters->enable();
307
		$this->do_action->enable();
308
309
		( new Plugin( 'plugin-slug-1' ) )->add( 'Plugin Name 1' );
310
311
		( new Plugin( 'plugin-slug-2' ) )->add( 'Plugin Name 2' );
312
313
		$stub = $this->createMock( Plugin::class );
314
		$stub->method( 'is_only' )
315
			->willReturn( false );
316
		$manager = ( new Manager() )->set_plugin_instance( $stub );
317
318
		$this->assertFalse( $manager->disconnect_site_wpcom() );
319
	}
320
321
	/**
322
	 * Test the `jetpack_connection_custom_caps' method.
323
	 *
324
	 * @covers Automattic\Jetpack\Connection\Manager::jetpack_connection_custom_caps
325
	 * @dataProvider jetpack_connection_custom_caps_data_provider
326
	 *
327
	 * @param bool   $in_offline_mode Whether offline mode is active.
328
	 * @param string $custom_cap The custom capability that is being tested.
329
	 * @param array  $expected_caps The expected output.
330
	 */
331
	public function test_jetpack_connection_custom_caps( $in_offline_mode, $custom_cap, $expected_caps ) {
332
		$this->apply_filters_deprecated->enable();
333
		// Mock the site_url call in Status::is_offline_mode.
334
		$this->mock_function( 'site_url', false, 'Automattic\Jetpack' );
335
336
		// Mock the apply_filters_deprecated( 'jetpack_development_mode' ) call in Status->is_offline_mode.
337
		$this->mock_function( 'apply_filters_deprecated', false, 'Automattic\Jetpack' );
338
339
		$this->apply_filters->disable();
340
		// Mock the apply_filters( 'jetpack_offline_mode', ) call in Status::is_offline_mode.
341
		add_filter(
342
			'jetpack_offline_mode',
343
			function() use ( $in_offline_mode ) {
344
				return $in_offline_mode;
345
			}
346
		);
347
348
		// Mock the apply_filters( 'jetpack_disconnect_cap', ) call in jetpack_connection_custom_caps.
349
		$this->mock_function( 'apply_filters', array( 'manage_options' ) );
350
351
		$caps = $this->manager->jetpack_connection_custom_caps( self::DEFAULT_TEST_CAPS, $custom_cap, 1, array() );
352
		$this->assertEquals( $expected_caps, $caps );
353
		$this->apply_filters_deprecated->disable();
354
	}
355
356
	/**
357
	 * Data provider test_jetpack_connection_custom_caps.
358
	 *
359
	 * Structure of the test data arrays:
360
	 *     [0] => 'in_offline_mode'   boolean Whether offline mode is active.
361
	 *     [1] => 'custom_cap'        string The custom capability that is being tested.
362
	 *     [2] => 'expected_caps'     array The expected output of the call to jetpack_connection_custom_caps.
363
	 */
364
	public function jetpack_connection_custom_caps_data_provider() {
365
366
		return array(
367
			'offline mode, jetpack_connect'          => array( true, 'jetpack_connect', array( 'do_not_allow' ) ),
368
			'offline mode, jetpack_reconnect'        => array( true, 'jetpack_reconnect', array( 'do_not_allow' ) ),
369
			'offline mode, jetpack_disconnect'       => array( true, 'jetpack_disconnect', array( 'manage_options' ) ),
370
			'offline mode, jetpack_connect_user'     => array( true, 'jetpack_connect_user', array( 'do_not_allow' ) ),
371
			'offline mode, unknown cap'              => array( true, 'unknown_cap', self::DEFAULT_TEST_CAPS ),
372
			'not offline mode, jetpack_connect'      => array( false, 'jetpack_connect', array( 'manage_options' ) ),
373
			'not offline mode, jetpack_reconnect'    => array( false, 'jetpack_reconnect', array( 'manage_options' ) ),
374
			'not offline mode, jetpack_disconnect'   => array( false, 'jetpack_disconnect', array( 'manage_options' ) ),
375
			'not offline mode, jetpack_connect_user' => array( false, 'jetpack_connect_user', array( 'read' ) ),
376
			'not offline mode, unknown cap'          => array( false, 'unknown_cap', self::DEFAULT_TEST_CAPS ),
377
		);
378
	}
379
380
	/**
381
	 * Test the `is_registered' method.
382
	 *
383
	 * @covers Automattic\Jetpack\Connection\Manager::is_registered
384
	 * @dataProvider is_registered_data_provider
385
	 *
386
	 * @param object|boolean $blog_token The blog token. False if the blog token does not exist.
387
	 * @param int|boolean    $blog_id The blog id. False if the blog id does not exist.
388
	 * @param boolean        $expected_output The expected output.
389
	 */
390
	public function test_is_registered( $blog_token, $blog_id, $expected_output ) {
391
		$this->manager->expects( $this->once() )
392
			->method( 'get_access_token' )
393
			->will( $this->returnValue( $blog_token ) );
394
395
		if ( $blog_id ) {
396
			update_option( 'jetpack_options', array( 'id' => $blog_id ) );
397
		} else {
398
			update_option( 'jetpack_options', array() );
399
		}
400
401
		$this->assertEquals( $expected_output, $this->manager->is_registered() );
402
	}
403
404
	/**
405
	 * Data provider for test_is_registered.
406
	 *
407
	 * Structure of the test data arrays:
408
	 *     [0] => 'blog_token'      object|boolean The blog token or false if the blog token does not exist.
409
	 *     [1] => 'blog_id'         int|boolean The blog id or false if the blog id does not exist.
410
	 *     [2] => 'expected_output' boolean The expected output of the call to is_registered.
411
	 */
412
	public function is_registered_data_provider() {
413
		$access_token = (object) array(
414
			'secret'           => 'abcd1234',
415
			'external_user_id' => 1,
416
		);
417
418
		return array(
419
			'blog token, blog id'       => array( $access_token, 1234, true ),
420
			'blog token, no blog id'    => array( $access_token, false, false ),
421
			'no blog token, blog id'    => array( false, 1234, false ),
422
			'no blog token, no blog id' => array( false, false, false ),
423
		);
424
	}
425
426
	/**
427
	 * Test the `get_signed_token` functionality.
428
	 *
429
	 * @covers Automattic\Jetpack\Connection\Manager::get_signed_token
430
	 */
431
	public function test_get_signed_token() {
432
		$access_token = (object) array(
433
			'external_user_id' => 1,
434
		);
435
436
		$manager = ( new Manager() );
437
		// Missing secret.
438
		$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...
439
		$this->assertEquals( $invalid_token_error, $manager->get_signed_token( $access_token ) );
440
		// Secret is null.
441
		$access_token->secret = null;
442
		$this->assertEquals( $invalid_token_error, $manager->get_signed_token( $access_token ) );
443
		// Secret is empty.
444
		$access_token->secret = '';
445
		$this->assertEquals( $invalid_token_error, $manager->get_signed_token( $access_token ) );
446
		// Valid secret.
447
		$access_token->secret = 'abcd1234';
448
449
		$signed_token = $manager->get_signed_token( $access_token );
450
		$this->assertTrue( strpos( $signed_token, 'token' ) !== false );
451
		$this->assertTrue( strpos( $signed_token, 'timestamp' ) !== false );
452
		$this->assertTrue( strpos( $signed_token, 'nonce' ) !== false );
453
		$this->assertTrue( strpos( $signed_token, 'signature' ) !== false );
454
	}
455
456
	/**
457
	 * Mock a global function and make it return a certain value.
458
	 *
459
	 * @param string $function_name Name of the function.
460
	 * @param mixed  $return_value Return value of the function.
461
	 * @param string $namespace The namespace of the function.
462
	 *
463
	 * @return Mock The mock object.
464
	 * @throws MockEnabledException PHPUnit wasn't able to enable mock functions.
465
	 */
466 View Code Duplication
	protected function mock_function( $function_name, $return_value = null, $namespace = __NAMESPACE__ ) {
467
		$builder = new MockBuilder();
468
		$builder->setNamespace( $namespace )
469
			->setName( $function_name )
470
			->setFunction(
471
				function() use ( &$return_value ) {
472
					return $return_value;
473
				}
474
			);
475
476
		$mock = $builder->build();
477
		$mock->enable();
478
479
		return $mock;
480
	}
481
482
	/**
483
	 * Filter to set the default constant values.
484
	 *
485
	 * @param string $value Existing value (empty and ignored).
486
	 * @param string $name Constant name.
487
	 *
488
	 * @see Utils::DEFAULT_JETPACK__API_BASE
489
	 * @see Utils::DEFAULT_JETPACK__API_VERSION
490
	 *
491
	 * @return string
492
	 */
493
	public function filter_api_constant( $value, $name ) {
494
		return constant( __NAMESPACE__ . "\Utils::DEFAULT_$name" );
495
	}
496
497
}
498