Completed
Push — add/jetpack_connection_new_met... ( 4417f6 )
by
unknown
58:25 queued 50:08
created

ManagerTest::test_api_url_defaults()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 16
rs 9.7333
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
	 * Test the `is_user_connected` functionality.
238
	 *
239
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
240
	 */
241 View Code Duplication
	public function test_is_user_connected_with_default_user_id_logged_in() {
242
		$this->mock_function( 'get_current_user_id', 1 );
243
		$access_token = (object) array(
244
			'secret'           => 'abcd1234',
245
			'external_user_id' => 1,
246
		);
247
		$this->manager->expects( $this->once() )
248
			->method( 'get_access_token' )
249
			->will( $this->returnValue( $access_token ) );
250
251
		$this->assertTrue( $this->manager->is_user_connected() );
252
	}
253
254
	/**
255
	 * Test the `is_user_connected` functionality.
256
	 *
257
	 * @covers Automattic\Jetpack\Connection\Manager::is_user_connected
258
	 */
259 View Code Duplication
	public function test_is_user_connected_with_user_id_logged_in() {
260
		$this->mock_function( 'absint', 1 );
261
		$access_token = (object) array(
262
			'secret'           => 'abcd1234',
263
			'external_user_id' => 1,
264
		);
265
		$this->manager->expects( $this->once() )
266
			->method( 'get_access_token' )
267
			->will( $this->returnValue( $access_token ) );
268
269
		$this->assertTrue( $this->manager->is_user_connected( 1 ) );
270
	}
271
272
	/**
273
	 * Unit test for the "Delete all tokens" functionality.
274
	 *
275
	 * @covers Automattic\Jetpack\Connection\Manager::delete_all_connection_tokens
276
	 * @throws MockEnabledException PHPUnit wasn't able to enable mock functions.
277
	 */
278 View Code Duplication
	public function test_delete_all_connection_tokens() {
279
		$this->update_option->enable();
280
		$this->get_option->enable();
281
		$this->apply_filters->enable();
282
		$this->do_action->enable();
283
284
		( new Plugin( 'plugin-slug-1' ) )->add( 'Plugin Name 1' );
285
286
		( new Plugin( 'plugin-slug-2' ) )->add( 'Plugin Name 2' );
287
288
		$stub = $this->createMock( Plugin::class );
289
		$stub->method( 'is_only' )
290
			->willReturn( false );
291
		$manager = ( new Manager() )->set_plugin_instance( $stub );
292
293
		$this->assertFalse( $manager->delete_all_connection_tokens() );
294
	}
295
296
	/**
297
	 * Unit test for the "Disconnect from WP" functionality.
298
	 *
299
	 * @covers Automattic\Jetpack\Connection\Manager::disconnect_site_wpcom
300
	 * @throws MockEnabledException PHPUnit wasn't able to enable mock functions.
301
	 */
302 View Code Duplication
	public function test_disconnect_site_wpcom() {
303
		$this->update_option->enable();
304
		$this->get_option->enable();
305
		$this->apply_filters->enable();
306
		$this->do_action->enable();
307
308
		( new Plugin( 'plugin-slug-1' ) )->add( 'Plugin Name 1' );
309
310
		( new Plugin( 'plugin-slug-2' ) )->add( 'Plugin Name 2' );
311
312
		$stub = $this->createMock( Plugin::class );
313
		$stub->method( 'is_only' )
314
			->willReturn( false );
315
		$manager = ( new Manager() )->set_plugin_instance( $stub );
316
317
		$this->assertFalse( $manager->disconnect_site_wpcom() );
318
	}
319
320
	/**
321
	 * Test the `jetpack_connection_custom_caps' method.
322
	 *
323
	 * @covers Automattic\Jetpack\Connection\Manager::jetpack_connection_custom_caps
324
	 * @dataProvider jetpack_connection_custom_caps_data_provider
325
	 *
326
	 * @param bool   $in_offline_mode Whether offline mode is active.
327
	 * @param string $custom_cap The custom capability that is being tested.
328
	 * @param array  $expected_caps The expected output.
329
	 */
330
	public function test_jetpack_connection_custom_caps( $in_offline_mode, $custom_cap, $expected_caps ) {
331
		$this->apply_filters_deprecated->enable();
332
		// Mock the site_url call in Status::is_offline_mode.
333
		$this->mock_function( 'site_url', false, 'Automattic\Jetpack' );
334
335
		// Mock the apply_filters_deprecated( 'jetpack_development_mode' ) call in Status->is_offline_mode.
336
		$this->mock_function( 'apply_filters_deprecated', false, 'Automattic\Jetpack' );
337
338
		$this->apply_filters->disable();
339
		// Mock the apply_filters( 'jetpack_offline_mode', ) call in Status::is_offline_mode.
340
		add_filter(
341
			'jetpack_offline_mode',
342
			function () use ( $in_offline_mode ) {
343
				return $in_offline_mode;
344
			}
345
		);
346
347
		// Mock the apply_filters( 'jetpack_disconnect_cap', ) call in jetpack_connection_custom_caps.
348
		$this->mock_function( 'apply_filters', array( 'manage_options' ) );
349
350
		$caps = $this->manager->jetpack_connection_custom_caps( self::DEFAULT_TEST_CAPS, $custom_cap, 1, array() );
351
		$this->assertEquals( $expected_caps, $caps );
352
		$this->apply_filters_deprecated->disable();
353
	}
354
355
	/**
356
	 * Data provider test_jetpack_connection_custom_caps.
357
	 *
358
	 * Structure of the test data arrays:
359
	 *     [0] => 'in_offline_mode'   boolean Whether offline mode is active.
360
	 *     [1] => 'custom_cap'        string The custom capability that is being tested.
361
	 *     [2] => 'expected_caps'     array The expected output of the call to jetpack_connection_custom_caps.
362
	 */
363
	public function jetpack_connection_custom_caps_data_provider() {
364
365
		return array(
366
			'offline mode, jetpack_connect'          => array( true, 'jetpack_connect', array( 'do_not_allow' ) ),
367
			'offline mode, jetpack_reconnect'        => array( true, 'jetpack_reconnect', array( 'do_not_allow' ) ),
368
			'offline mode, jetpack_disconnect'       => array( true, 'jetpack_disconnect', array( 'manage_options' ) ),
369
			'offline mode, jetpack_connect_user'     => array( true, 'jetpack_connect_user', array( 'do_not_allow' ) ),
370
			'offline mode, unknown cap'              => array( true, 'unknown_cap', self::DEFAULT_TEST_CAPS ),
371
			'not offline mode, jetpack_connect'      => array( false, 'jetpack_connect', array( 'manage_options' ) ),
372
			'not offline mode, jetpack_reconnect'    => array( false, 'jetpack_reconnect', array( 'manage_options' ) ),
373
			'not offline mode, jetpack_disconnect'   => array( false, 'jetpack_disconnect', array( 'manage_options' ) ),
374
			'not offline mode, jetpack_connect_user' => array( false, 'jetpack_connect_user', array( 'read' ) ),
375
			'not offline mode, unknown cap'          => array( false, 'unknown_cap', self::DEFAULT_TEST_CAPS ),
376
		);
377
	}
378
379
	/**
380
	 * Test the `is_connected' method.
381
	 *
382
	 * @covers Automattic\Jetpack\Connection\Manager::is_connected
383
	 * @dataProvider is_connected_data_provider
384
	 *
385
	 * @param object|boolean $blog_token The blog token. False if the blog token does not exist.
386
	 * @param int|boolean    $blog_id The blog id. False if the blog id does not exist.
387
	 * @param boolean        $expected_output The expected output.
388
	 */
389
	public function test_is_connected( $blog_token, $blog_id, $expected_output ) {
390
		$this->manager->expects( $this->once() )
391
			->method( 'get_access_token' )
392
			->will( $this->returnValue( $blog_token ) );
393
394
		if ( $blog_id ) {
395
			update_option( 'jetpack_options', array( 'id' => $blog_id ) );
396
		} else {
397
			update_option( 'jetpack_options', array() );
398
		}
399
400
		$this->assertEquals( $expected_output, $this->manager->is_connected() );
401
	}
402
403
	/**
404
	 * Data provider for test_is_connected.
405
	 *
406
	 * Structure of the test data arrays:
407
	 *     [0] => 'blog_token'      object|boolean The blog token or false if the blog token does not exist.
408
	 *     [1] => 'blog_id'         int|boolean The blog id or false if the blog id does not exist.
409
	 *     [2] => 'expected_output' boolean The expected output of the call to is_connected.
410
	 */
411
	public function is_connected_data_provider() {
412
		$access_token = (object) array(
413
			'secret'           => 'abcd1234',
414
			'external_user_id' => 1,
415
		);
416
417
		return array(
418
			'blog token, blog id'       => array( $access_token, 1234, true ),
419
			'blog token, no blog id'    => array( $access_token, false, false ),
420
			'no blog token, blog id'    => array( false, 1234, false ),
421
			'no blog token, no blog id' => array( false, false, false ),
422
		);
423
	}
424
425
	/**
426
	 * Test the `get_signed_token` functionality.
427
	 *
428
	 * @covers Automattic\Jetpack\Connection\Manager::get_signed_token
429
	 */
430
	public function test_get_signed_token() {
431
		$access_token = (object) array(
432
			'external_user_id' => 1,
433
		);
434
435
		$manager = ( new Manager() );
436
		// Missing secret.
437
		$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...
438
		$this->assertEquals( $invalid_token_error, $manager->get_signed_token( $access_token ) );
439
		// Secret is null.
440
		$access_token->secret = null;
441
		$this->assertEquals( $invalid_token_error, $manager->get_signed_token( $access_token ) );
442
		// Secret is empty.
443
		$access_token->secret = '';
444
		$this->assertEquals( $invalid_token_error, $manager->get_signed_token( $access_token ) );
445
		// Valid secret.
446
		$access_token->secret = 'abcd1234';
447
448
		$signed_token = $manager->get_signed_token( $access_token );
449
		$this->assertTrue( strpos( $signed_token, 'token' ) !== false );
450
		$this->assertTrue( strpos( $signed_token, 'timestamp' ) !== false );
451
		$this->assertTrue( strpos( $signed_token, 'nonce' ) !== false );
452
		$this->assertTrue( strpos( $signed_token, 'signature' ) !== false );
453
	}
454
455
	/**
456
	 * Mock a global function and make it return a certain value.
457
	 *
458
	 * @param string $function_name Name of the function.
459
	 * @param mixed  $return_value Return value of the function.
460
	 * @param string $namespace The namespace of the function.
461
	 *
462
	 * @return Mock The mock object.
463
	 * @throws MockEnabledException PHPUnit wasn't able to enable mock functions.
464
	 */
465 View Code Duplication
	protected function mock_function( $function_name, $return_value = null, $namespace = __NAMESPACE__ ) {
466
		$builder = new MockBuilder();
467
		$builder->setNamespace( $namespace )
468
			->setName( $function_name )
469
			->setFunction(
470
				function () use ( &$return_value ) {
471
					return $return_value;
472
				}
473
			);
474
475
		$mock = $builder->build();
476
		$mock->enable();
477
478
		return $mock;
479
	}
480
481
	/**
482
	 * Filter to set the default constant values.
483
	 *
484
	 * @param string $value Existing value (empty and ignored).
485
	 * @param string $name Constant name.
486
	 *
487
	 * @see Utils::DEFAULT_JETPACK__API_BASE
488
	 * @see Utils::DEFAULT_JETPACK__API_VERSION
489
	 *
490
	 * @return string
491
	 */
492
	public function filter_api_constant( $value, $name ) {
493
		return constant( __NAMESPACE__ . "\Utils::DEFAULT_$name" );
494
	}
495
496
}
497