Completed
Push — add/user-token-restore ( ba00e4...01a863 )
by
unknown
47:52 queued 36:38
created

Test_REST_Endpoints::test_set_user_token_success()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 56
rs 8.9599
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3
namespace Automattic\Jetpack\Connection;
4
5
use Automattic\Jetpack\Connection\Plugin as Connection_Plugin;
6
use Automattic\Jetpack\Connection\Plugin_Storage as Connection_Plugin_Storage;
7
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
8
use Automattic\Jetpack\Constants;
9
use Automattic\Jetpack\Redirect;
10
use Jetpack_Options;
11
use PHPUnit\Framework\TestCase;
12
use Requests_Utility_CaseInsensitiveDictionary;
13
use WorDBless\Options as WorDBless_Options;
14
use WP_REST_Request;
15
use WP_REST_Server;
16
use WP_User;
17
18
/**
19
 * Unit tests for the REST API endpoints.
20
 *
21
 * @package automattic/jetpack-connection
22
 * @see \Automattic\Jetpack\Connection\REST_Connector
23
 */
24
class Test_REST_Endpoints extends TestCase {
25
26
	const BLOG_TOKEN = 'new.blogtoken';
27
	const BLOG_ID    = 42;
28
	const USER_ID    = 111;
29
30
	/**
31
	 * REST Server object.
32
	 *
33
	 * @var WP_REST_Server
34
	 */
35
	private $server;
36
37
	/**
38
	 * The original hostname to restore after tests are finished.
39
	 *
40
	 * @var string
41
	 */
42
	private $api_host_original;
43
44
	/**
45
	 * Setting up the test.
46
	 *
47
	 * @before
48
	 */
49
	public function set_up() {
50
		global $wp_rest_server;
51
52
		$wp_rest_server = new WP_REST_Server();
53
		$this->server   = $wp_rest_server;
54
55
		do_action( 'rest_api_init' );
56
		new REST_Connector( new Manager() );
57
58
		add_action( 'jetpack_disabled_raw_options', array( $this, 'bypass_raw_options' ) );
59
60
		$user = wp_get_current_user();
61
		$user->add_cap( 'jetpack_reconnect' );
62
		$user->add_cap( 'jetpack_connect' );
63
64
		$this->api_host_original                                  = Constants::get_constant( 'JETPACK__WPCOM_JSON_API_BASE' );
65
		Constants::$set_constants['JETPACK__WPCOM_JSON_API_BASE'] = 'https://public-api.wordpress.com';
66
67
		Constants::$set_constants['JETPACK__API_BASE'] = 'https://jetpack.wordpress.com/jetpack.';
68
69
		set_transient( 'jetpack_assumed_site_creation_date', '2020-02-28 01:13:27' );
70
	}
71
72
	/**
73
	 * Returning the environment into its initial state.
74
	 *
75
	 * @after
76
	 */
77
	public function tear_down() {
78
		remove_action( 'jetpack_disabled_raw_options', array( $this, 'bypass_raw_options' ) );
79
80
		$user = wp_get_current_user();
81
		$user->remove_cap( 'jetpack_reconnect' );
82
83
		Constants::$set_constants['JETPACK__WPCOM_JSON_API_BASE'] = $this->api_host_original;
84
85
		delete_transient( 'jetpack_assumed_site_creation_date' );
86
87
		WorDBless_Options::init()->clear_options();
88
89
		unset( $_SERVER['REQUEST_METHOD'] );
90
		$_GET = array();
91
	}
92
93
	/**
94
	 * Testing the `/jetpack/v4/remote_authorize` endpoint.
95
	 */
96
	public function test_remote_authorize() {
97
		add_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ), 10, 2 );
98
		add_filter( 'pre_http_request', array( $this, 'intercept_auth_token_request' ), 10, 3 );
99
100
		wp_cache_set(
101
			self::USER_ID,
102
			(object) array(
103
				'ID'         => self::USER_ID,
104
				'user_email' => '[email protected]',
105
			),
106
			'users'
107
		);
108
109
		$secret_1 = 'Az0g39toGWlYiTJ4NnDuAz0g39toGWlY';
110
111
		$secrets = array(
112
			'jetpack_authorize_' . self::USER_ID => array(
113
				'secret_1' => $secret_1,
114
				'secret_2' => 'zfIFcym2Jlzd8AVgzfIFcym2Jlzd8AVg',
115
				'exp'      => time() + 60,
116
			),
117
		);
118
119
		// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
120
		$options_filter = function ( $value ) use ( $secrets ) {
121
			return $secrets;
122
		};
123
		add_filter( 'pre_option_' . Secrets::LEGACY_SECRETS_OPTION_NAME, $options_filter );
124
125
		$user_caps_filter = function ( $allcaps, $caps, $args, $user ) {
126
			if ( $user instanceof WP_User && self::USER_ID === $user->ID ) {
0 ignored issues
show
Bug introduced by
The class WP_User does not exist. Is this class maybe located in a folder that is not analyzed, or in a newer version of your dependencies than listed in your composer.lock/composer.json?
Loading history...
127
				$allcaps['manage_options'] = true;
128
				$allcaps['administrator']  = true;
129
			}
130
131
			return $allcaps;
132
		};
133
		add_filter( 'user_has_cap', $user_caps_filter, 10, 4 );
134
135
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/remote_authorize' );
136
		$this->request->set_header( 'Content-Type', 'application/json' );
137
		$this->request->set_body( '{ "state": "' . self::USER_ID . '", "secret": "' . $secret_1 . '", "redirect_uri": "https://example.org", "code": "54321" }' );
138
139
		$response = $this->server->dispatch( $this->request );
140
		$data     = $response->get_data();
141
142
		remove_filter( 'user_has_cap', $user_caps_filter );
143
		remove_filter( 'pre_option_' . Secrets::LEGACY_SECRETS_OPTION_NAME, $options_filter );
144
		remove_filter( 'pre_http_request', array( $this, 'intercept_auth_token_request' ) );
145
		remove_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ) );
146
147
		wp_cache_delete( self::USER_ID, 'users' );
148
149
		wp_set_current_user( 0 );
150
151
		$this->assertEquals( 200, $response->get_status() );
152
		$this->assertEquals( 'authorized', $data['result'] );
153
	}
154
155
	/**
156
	 * Testing the `/jetpack/v4/connection` endpoint.
157
	 */
158
	public function test_connection() {
159
		add_filter( 'jetpack_offline_mode', '__return_true' );
160
		try {
161
			$this->request = new WP_REST_Request( 'GET', '/jetpack/v4/connection' );
162
163
			$response = $this->server->dispatch( $this->request );
164
			$data     = $response->get_data();
165
166
			$this->assertFalse( $data['isActive'] );
167
			$this->assertFalse( $data['isRegistered'] );
168
			$this->assertTrue( $data['offlineMode']['isActive'] );
169
		} finally {
170
			remove_filter( 'jetpack_offline_mode', '__return_true' );
171
		}
172
	}
173
174
	/**
175
	 * Testing the `/jetpack/v4/connection` endpoint jetpack_connection_status filter.
176
	 */
177
	public function test_connection_jetpack_connection_status_filter() {
178
		add_filter(
179
			'jetpack_connection_status',
180
			function ( $status_data ) {
181
				$this->assertTrue( is_array( $status_data ) );
182
				return array();
183
			}
184
		);
185
		try {
186
			$this->request = new WP_REST_Request( 'GET', '/jetpack/v4/connection' );
187
188
			$response = $this->server->dispatch( $this->request );
189
			$data     = $response->get_data();
190
191
			$this->assertSame( array(), $data );
192
		} finally {
193
			remove_all_filters( 'jetpack_connection_status' );
194
		}
195
	}
196
197
	/**
198
	 * Testing the `/jetpack/v4/connection/plugins` endpoint.
199
	 */
200
	public function test_connection_plugins() {
201
		$user = wp_get_current_user();
202
		$user->add_cap( 'activate_plugins' );
203
204
		$plugins = array(
205
			array(
206
				'name' => 'Plugin Name 1',
207
				'slug' => 'plugin-slug-1',
208
			),
209
			array(
210
				'name' => 'Plugin Name 2',
211
				'slug' => 'plugin-slug-2',
212
			),
213
		);
214
215
		array_walk(
216
			$plugins,
217
			function ( $plugin ) {
218
				( new Connection_Plugin( $plugin['slug'] ) )->add( $plugin['name'] );
219
			}
220
		);
221
222
		Connection_Plugin_Storage::configure();
223
224
		$this->request = new WP_REST_Request( 'GET', '/jetpack/v4/connection/plugins' );
225
226
		$response = $this->server->dispatch( $this->request );
227
228
		$user->remove_cap( 'activate_plugins' );
229
230
		$this->assertEquals( $plugins, $response->get_data() );
231
	}
232
233
	/**
234
	 * Testing the `connection/reconnect` endpoint, full reconnect.
235
	 */
236
	public function test_connection_reconnect_full() {
237
		$this->setup_reconnect_test( null );
238
		add_filter( 'jetpack_connection_disconnect_site_wpcom', '__return_false' );
239
		add_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10, 3 );
240
241
		$response = $this->server->dispatch( $this->build_reconnect_request() );
242
		$data     = $response->get_data();
243
244
		remove_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10 );
245
		remove_filter( 'jetpack_connection_disconnect_site_wpcom', '__return_false' );
246
		$this->shutdown_reconnect_test( null );
247
248
		$this->assertEquals( 200, $response->get_status() );
249
		$this->assertEquals( 'in_progress', $data['status'] );
250
		$this->assertSame( 0, strpos( $data['authorizeUrl'], 'https://jetpack.wordpress.com/jetpack.authorize/' ) );
251
	}
252
253
	/**
254
	 * Testing the `connection/reconnect` endpoint, successful partial reconnect (blog token).
255
	 */
256 View Code Duplication
	public function test_connection_reconnect_partial_blog_token_success() {
257
		$this->setup_reconnect_test( 'blog_token' );
258
		add_filter( 'pre_http_request', array( $this, 'intercept_refresh_blog_token_request' ), 10, 3 );
259
260
		$response = $this->server->dispatch( $this->build_reconnect_request() );
261
		$data     = $response->get_data();
262
263
		remove_filter( 'pre_http_request', array( $this, 'intercept_refresh_blog_token_request' ), 10 );
264
		$this->shutdown_reconnect_test( 'blog_token' );
265
266
		$this->assertEquals( 200, $response->get_status() );
267
		$this->assertEquals( 'completed', $data['status'] );
268
	}
269
270
	/**
271
	 * Testing the `connection/reconnect` endpoint, failed partial reconnect (blog token).
272
	 */
273 View Code Duplication
	public function test_connection_reconnect_partial_blog_token_fail() {
274
		$this->setup_reconnect_test( 'blog_token' );
275
		add_filter( 'pre_http_request', array( $this, 'intercept_refresh_blog_token_request_fail' ), 10, 3 );
276
277
		$response = $this->server->dispatch( $this->build_reconnect_request() );
278
		$data     = $response->get_data();
279
280
		remove_filter( 'pre_http_request', array( $this, 'intercept_refresh_blog_token_request_fail' ), 10 );
281
		$this->shutdown_reconnect_test( 'blog_token' );
282
283
		$this->assertEquals( 500, $response->get_status() );
284
		$this->assertEquals( 'jetpack_secret', $data['code'] );
285
	}
286
287
	/**
288
	 * Testing the `connection/reconnect` endpoint, successful partial reconnect (user token).
289
	 */
290
	public function test_connection_reconnect_partial_user_token_success() {
291
		$this->setup_reconnect_test( 'user_token' );
292
293
		$response = $this->server->dispatch( $this->build_reconnect_request() );
294
		$data     = $response->get_data();
295
296
		$this->shutdown_reconnect_test( 'user_token' );
297
298
		$this->assertEquals( 200, $response->get_status() );
299
		$this->assertEquals( 'in_progress', $data['status'] );
300
		$this->assertSame( 0, strpos( $data['authorizeUrl'], 'https://jetpack.wordpress.com/jetpack.authorize/' ) );
301
	}
302
303
	/**
304
	 * Testing the `connection/reconnect` endpoint, site_connection (full reconnect).
305
	 */
306
	public function test_connection_reconnect_site_connection() {
307
		add_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ), 10, 2 );
308
		add_filter( 'jetpack_connection_disconnect_site_wpcom', '__return_false' );
309
		add_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10, 3 );
310
311
		$response = $this->server->dispatch( $this->build_reconnect_request() );
312
		$data     = $response->get_data();
313
314
		remove_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10 );
315
		remove_filter( 'jetpack_connection_disconnect_site_wpcom', '__return_false' );
316
		remove_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ) );
317
318
		$this->assertEquals( 200, $response->get_status() );
319
		$this->assertEquals( 'completed', $data['status'] );
320
	}
321
322
	/**
323
	 * Testing the `connection/reconnect` endpoint when the token validation request fails.
324
	 */
325
	public function test_connection_reconnect_when_token_validation_request_fails() {
326
		$this->setup_reconnect_test( 'token_validation_failed' );
327
		add_filter( 'jetpack_connection_disconnect_site_wpcom', '__return_false' );
328
		add_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10, 3 );
329
330
		$response = $this->server->dispatch( $this->build_reconnect_request() );
331
		$data     = $response->get_data();
332
333
		remove_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10 );
334
		remove_filter( 'jetpack_connection_disconnect_site_wpcom', '__return_false' );
335
		$this->shutdown_reconnect_test( 'token_validation_failed' );
336
337
		$this->assertEquals( 200, $response->get_status() );
338
		$this->assertEquals( 'in_progress', $data['status'] );
339
		$this->assertSame( 0, strpos( $data['authorizeUrl'], 'https://jetpack.wordpress.com/jetpack.authorize/' ) );
340
	}
341
342
	/**
343
	 * Testing the `connection/register` endpoint.
344
	 */
345 View Code Duplication
	public function test_connection_register() {
346
		add_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10, 3 );
347
348
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/connection/register' );
349
		$this->request->set_header( 'Content-Type', 'application/json' );
350
351
		$this->request->set_body( wp_json_encode( array( 'registration_nonce' => wp_create_nonce( 'jetpack-registration-nonce' ) ) ) );
352
353
		$response = $this->server->dispatch( $this->request );
354
		$data     = $response->get_data();
355
356
		remove_filter( 'pre_http_request', array( static::class, 'intercept_register_request' ), 10 );
357
358
		// Manually clears filter added by Manager::register().
359
		remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_false', 20 );
360
361
		$this->assertEquals( 200, $response->get_status() );
362
		$this->assertSame( 0, strpos( $data['authorizeUrl'], 'https://jetpack.wordpress.com/jetpack.authorize/' ) );
363
364
		// Asserts jetpack_register_site_rest_response filter is being properly hooked to add data from wpcom register endpoint response.
365
		$this->assertFalse( $data['allowInplaceAuthorization'] );
366
		$this->assertSame( '', $data['alternateAuthorizeUrl'] );
367
	}
368
369
	/**
370
	 * Testing the `connection/register` endpoint with allow_inplace_authorization as true.
371
	 */
372
	public function test_connection_register_allow_inplace() {
373
		add_filter( 'pre_http_request', array( static::class, 'intercept_register_request_with_allow_inplace' ), 10, 3 );
374
375
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/connection/register' );
376
		$this->request->set_header( 'Content-Type', 'application/json' );
377
378
		$this->request->set_body( wp_json_encode( array( 'registration_nonce' => wp_create_nonce( 'jetpack-registration-nonce' ) ) ) );
379
380
		$response = $this->server->dispatch( $this->request );
381
		$data     = $response->get_data();
382
383
		remove_filter( 'pre_http_request', array( static::class, 'intercept_register_request_with_allow_inplace' ), 10 );
384
385
		$this->assertEquals( 200, $response->get_status() );
386
		$this->assertSame( 0, strpos( $data['authorizeUrl'], 'https://jetpack.wordpress.com/jetpack.authorize_iframe/' ) );
387
388
		// Asserts jetpack_register_site_rest_response filter is being properly hooked to add data from wpcom register endpoint response.
389
		$this->assertTrue( $data['allowInplaceAuthorization'] );
390
		$this->assertSame( '', $data['alternateAuthorizeUrl'] );
391
	}
392
393
	/**
394
	 * Testing the `connection/register` endpoint with alternate_authorization_url
395
	 */
396 View Code Duplication
	public function test_connection_register_with_alternate_auth_url() {
397
		add_filter( 'pre_http_request', array( static::class, 'intercept_register_request_with_alternate_auth_url' ), 10, 3 );
398
399
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/connection/register' );
400
		$this->request->set_header( 'Content-Type', 'application/json' );
401
402
		$this->request->set_body( wp_json_encode( array( 'registration_nonce' => wp_create_nonce( 'jetpack-registration-nonce' ) ) ) );
403
404
		$response = $this->server->dispatch( $this->request );
405
		$data     = $response->get_data();
406
407
		remove_filter( 'pre_http_request', array( static::class, 'intercept_register_request_with_alternate_auth_url' ), 10 );
408
409
		// Manually clears filter added by Manager::register().
410
		remove_filter( 'jetpack_use_iframe_authorization_flow', '__return_false', 20 );
411
412
		$this->assertEquals( 200, $response->get_status() );
413
		$this->assertSame( 0, strpos( $data['authorizeUrl'], 'https://jetpack.wordpress.com/jetpack.authorize/' ) );
414
415
		// Asserts jetpack_register_site_rest_response filter is being properly hooked to add data from wpcom register endpoint response.
416
		$this->assertFalse( $data['allowInplaceAuthorization'] );
417
		$this->assertSame( Redirect::get_url( 'https://dummy.com' ), $data['alternateAuthorizeUrl'] );
418
	}
419
420
	/**
421
	 * Testing the `user-token` endpoint using blog token authorization.
422
	 * Response: failed authorization.
423
	 */
424
	public function test_set_user_token_unauthroized() {
425
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/user-token' );
426
		$this->request->set_header( 'Content-Type', 'application/json' );
427
428
		$this->request->set_body( wp_json_encode( array( 'user_token' => 'test.test.1' ) ) );
429
430
		$response = $this->server->dispatch( $this->request );
431
		$data     = $response->get_data();
432
433
		static::assertEquals( 'invalid_permission_update_user_token', $data['code'] );
434
		static::assertEquals( 401, $data['data']['status'] );
435
	}
436
437
	/**
438
	 * Testing the `user-token` endpoint using blog token authorization.
439
	 * Response: user token updated.
440
	 */
441
	public function test_set_user_token_success() {
442
		add_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ), 10, 2 );
443
444
		$token     = 'new:1:0';
445
		$timestamp = (string) time();
446
		$nonce     = 'testing123';
447
		$body_hash = '';
448
449
		$_SERVER['REQUEST_METHOD'] = 'POST';
450
451
		$_GET['_for']      = 'jetpack';
452
		$_GET['token']     = $token;
453
		$_GET['timestamp'] = $timestamp;
454
		$_GET['nonce']     = $nonce;
455
		$_GET['body-hash'] = $body_hash;
456
		// This is intentionally using base64_encode().
457
		// phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode
458
		$_GET['signature'] = base64_encode(
459
			hash_hmac(
460
				'sha1',
461
				implode(
462
					"\n",
463
					$data  = array(
464
						$token,
465
						$timestamp,
466
						$nonce,
467
						$body_hash,
468
						'POST',
469
						'anything.example',
470
						'80',
471
						'',
472
					)
473
				) . "\n",
474
				'blogtoken',
475
				true
476
			)
477
		);
478
479
		Connection_Rest_Authentication::init()->wp_rest_authenticate( false );
480
481
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/user-token' );
482
		$this->request->set_header( 'Content-Type', 'application/json' );
483
484
		$user_token = 'test.test.1';
485
486
		$this->request->set_body( wp_json_encode( array( 'user_token' => $user_token ) ) );
487
488
		$response = $this->server->dispatch( $this->request );
489
		$data     = $response->get_data();
490
491
		remove_filter( 'jetpack_options', array( $this, 'mock_jetpack_site_connection_options' ) );
492
493
		static::assertTrue( $data['success'] );
494
		static::assertEquals( 200, $response->status );
495
		static::assertEquals( array( 1 => $user_token ), Jetpack_Options::get_option( 'user_tokens' ) );
496
	}
497
498
	/**
499
	 * This filter callback allows us to skip the database query by `Jetpack_Options` to retrieve the option.
500
	 *
501
	 * @param array $options List of options already skipping the database request.
502
	 *
503
	 * @return array
504
	 */
505
	public function bypass_raw_options( array $options ) {
506
		$options[ Secrets::LEGACY_SECRETS_OPTION_NAME ] = true;
507
508
		return $options;
509
	}
510
511
	/**
512
	 * Intercept the `jetpack.register` API request sent to WP.com, and mock the response.
513
	 *
514
	 * @param bool|array $response The existing response.
515
	 * @param array      $args The request arguments.
516
	 * @param string     $url The request URL.
517
	 *
518
	 * @return array
519
	 */
520
	public static function intercept_register_request( $response, $args, $url ) {
521
		if ( false === strpos( $url, 'jetpack.register' ) ) {
522
			return $response;
523
		}
524
525
		return self::get_register_request_mock_response();
526
	}
527
528
	/**
529
	 * Intercept the `jetpack.register` API request sent to WP.com, and mock the response with allow_inplace_authorization as true.
530
	 *
531
	 * @param bool|array $response The existing response.
532
	 * @param array      $args The request arguments.
533
	 * @param string     $url The request URL.
534
	 *
535
	 * @return array
536
	 */
537
	public static function intercept_register_request_with_allow_inplace( $response, $args, $url ) {
538
		if ( false === strpos( $url, 'jetpack.register' ) ) {
539
			return $response;
540
		}
541
542
		return self::get_register_request_mock_response( true );
543
	}
544
545
	/**
546
	 * Intercept the `jetpack.register` API request sent to WP.com, and mock the response with a value in alternate_authorization_url key.
547
	 *
548
	 * @param bool|array $response The existing response.
549
	 * @param array      $args The request arguments.
550
	 * @param string     $url The request URL.
551
	 *
552
	 * @return array
553
	 */
554
	public static function intercept_register_request_with_alternate_auth_url( $response, $args, $url ) {
555
		if ( false === strpos( $url, 'jetpack.register' ) ) {
556
			return $response;
557
		}
558
559
		return self::get_register_request_mock_response( false, 'https://dummy.com' );
560
	}
561
562
	/**
563
	 * Gets a mocked REST response from jetpack.register WPCOM endpoint
564
	 *
565
	 * @param boolean $allow_inplace_authorization the value of allow_inplace_authorization returned by the server.
566
	 * @param string  $alternate_authorization_url the value of alternate_authorization_url returned by the server.
567
	 * @return array
568
	 */
569
	private static function get_register_request_mock_response( $allow_inplace_authorization = false, $alternate_authorization_url = '' ) {
570
		return array(
571
			'headers'  => new Requests_Utility_CaseInsensitiveDictionary( array( 'content-type' => 'application/json' ) ),
572
			'body'     => wp_json_encode(
573
				array(
574
					'jetpack_id'                  => '12345',
575
					'jetpack_secret'              => 'sample_secret',
576
					'allow_inplace_authorization' => $allow_inplace_authorization,
577
					'alternate_authorization_url' => $alternate_authorization_url,
578
				)
579
			),
580
			'response' => array(
581
				'code'    => 200,
582
				'message' => 'OK',
583
			),
584
		);
585
	}
586
587
	/**
588
	 * Intercept the `jetpack-token-health` API request sent to WP.com, and mock the "invalid blog token" response.
589
	 *
590
	 * @param bool|array $response The existing response.
591
	 * @param array      $args The request arguments.
592
	 * @param string     $url The request URL.
593
	 *
594
	 * @return array
595
	 */
596
	public function intercept_validate_tokens_request_invalid_blog_token( $response, $args, $url ) {
597
		if ( false === strpos( $url, 'jetpack-token-health' ) ) {
598
			return $response;
599
		}
600
601
		return $this->build_validate_tokens_response( 'blog_token' );
602
	}
603
604
	/**
605
	 * Intercept the `jetpack-token-health` API request sent to WP.com, and mock the "invalid user token" response.
606
	 *
607
	 * @param bool|array $response The existing response.
608
	 * @param array      $args The request arguments.
609
	 * @param string     $url The request URL.
610
	 *
611
	 * @return array
612
	 */
613
	public function intercept_validate_tokens_request_invalid_user_token( $response, $args, $url ) {
614
		if ( false === strpos( $url, 'jetpack-token-health' ) ) {
615
			return $response;
616
		}
617
618
		return $this->build_validate_tokens_response( 'user_token' );
619
	}
620
621
	/**
622
	 * Intercept the `jetpack-token-health` API request sent to WP.com, and mock the "valid tokens" response.
623
	 *
624
	 * @param bool|array $response The existing response.
625
	 * @param array      $args The request arguments.
626
	 * @param string     $url The request URL.
627
	 *
628
	 * @return array
629
	 */
630
	public function intercept_validate_tokens_request_valid_tokens( $response, $args, $url ) {
631
		if ( false === strpos( $url, 'jetpack-token-health' ) ) {
632
			return $response;
633
		}
634
635
		return $this->build_validate_tokens_response( null );
636
	}
637
638
	/**
639
	 * Intercept the `jetpack-token-health` API request sent to WP.com, and mock failed response.
640
	 *
641
	 * @param bool|array $response The existing response.
642
	 * @param array      $args The request arguments.
643
	 * @param string     $url The request URL.
644
	 *
645
	 * @return array
646
	 */
647 View Code Duplication
	public function intercept_validate_tokens_request_failed( $response, $args, $url ) {
648
		if ( false === strpos( $url, 'jetpack-token-health' ) ) {
649
			return $response;
650
		}
651
652
		return array(
653
			'headers'  => new Requests_Utility_CaseInsensitiveDictionary( array( 'content-type' => 'application/json' ) ),
654
			'body'     => wp_json_encode( array( 'dummy_error' => true ) ),
655
			'response' => array(
656
				'code'    => 500,
657
				'message' => 'failed',
658
			),
659
		);
660
	}
661
662
	/**
663
	 * Build the response for a tokens validation request
664
	 *
665
	 * @param string $invalid_token Accepted values: 'blog_token', 'user_token'.
666
	 *
667
	 * @return array
668
	 */
669
	private function build_validate_tokens_response( $invalid_token ) {
670
		$body = array(
671
			'blog_token' => array(
672
				'is_healthy' => true,
673
			),
674
			'user_token' => array(
675
				'is_healthy'     => true,
676
				'is_master_user' => true,
677
			),
678
		);
679
680
		switch ( $invalid_token ) {
681
			case 'blog_token':
682
				$body['blog_token'] = array(
683
					'is_healthy' => false,
684
					'code'       => 'unknown_token',
685
				);
686
				break;
687
			case 'user_token':
688
				$body['user_token'] = array(
689
					'is_healthy' => false,
690
					'code'       => 'unknown_token',
691
				);
692
				break;
693
		}
694
695
		return array(
696
			'headers'  => new Requests_Utility_CaseInsensitiveDictionary( array( 'content-type' => 'application/json' ) ),
697
			'body'     => wp_json_encode( $body ),
698
			'response' => array(
699
				'code'    => 200,
700
				'message' => 'OK',
701
			),
702
		);
703
	}
704
705
	/**
706
	 * Intercept the `jetpack-refresh-blog-token` API request sent to WP.com, and mock the success response.
707
	 *
708
	 * @param bool|array $response The existing response.
709
	 * @param array      $args The request arguments.
710
	 * @param string     $url The request URL.
711
	 *
712
	 * @return array
713
	 */
714 View Code Duplication
	public function intercept_refresh_blog_token_request( $response, $args, $url ) {
715
		if ( false === strpos( $url, 'jetpack-refresh-blog-token' ) ) {
716
			return $response;
717
		}
718
719
		return array(
720
			'headers'  => new Requests_Utility_CaseInsensitiveDictionary( array( 'content-type' => 'application/json' ) ),
721
			'body'     => wp_json_encode( array( 'jetpack_secret' => self::BLOG_TOKEN ) ),
722
			'response' => array(
723
				'code'    => 200,
724
				'message' => 'OK',
725
			),
726
		);
727
	}
728
729
	/**
730
	 * Intercept the `jetpack-refresh-blog-token` API request sent to WP.com, and mock the failure response.
731
	 *
732
	 * @param bool|array $response The existing response.
733
	 * @param array      $args The request arguments.
734
	 * @param string     $url The request URL.
735
	 *
736
	 * @return array
737
	 */
738 View Code Duplication
	public function intercept_refresh_blog_token_request_fail( $response, $args, $url ) {
739
		if ( false === strpos( $url, 'jetpack-refresh-blog-token' ) ) {
740
			return $response;
741
		}
742
743
		return array(
744
			'headers'  => new Requests_Utility_CaseInsensitiveDictionary( array( 'content-type' => 'application/json' ) ),
745
			'body'     => wp_json_encode( array( 'jetpack_secret_missing' => true ) ), // Meaningless body.
746
			'response' => array(
747
				'code'    => 200,
748
				'message' => 'OK',
749
			),
750
		);
751
	}
752
753
	/**
754
	 * Intercept the `jetpack-token-health` API request sent to WP.com, and mock the "invalid blog token" response.
755
	 *
756
	 * @param bool|array $response The existing response.
757
	 * @param array      $args The request arguments.
758
	 * @param string     $url The request URL.
759
	 *
760
	 * @return array
761
	 */
762
	public function intercept_auth_token_request( $response, $args, $url ) {
763
		if ( false === strpos( $url, '/jetpack.token/' ) ) {
764
			return $response;
765
		}
766
767
		return array(
768
			'headers'  => new Requests_Utility_CaseInsensitiveDictionary( array( 'content-type' => 'application/json' ) ),
769
			'body'     => wp_json_encode(
770
				array(
771
					'access_token' => 'mock.token',
772
					'token_type'   => 'X_JETPACK',
773
					'scope'        => ( new Manager() )->sign_role( 'administrator' ),
774
				)
775
			),
776
			'response' => array(
777
				'code'    => 200,
778
				'message' => 'OK',
779
			),
780
		);
781
	}
782
783
	/**
784
	 * Intercept the `Jetpack_Options` call and mock the values.
785
	 * Site level / user-less connection set-up.
786
	 *
787
	 * @param mixed  $value The current option value.
788
	 * @param string $name Option name.
789
	 *
790
	 * @return mixed
791
	 */
792
	public function mock_jetpack_site_connection_options( $value, $name ) {
793
		switch ( $name ) {
794
			case 'blog_token':
795
				return self::BLOG_TOKEN;
796
			case 'id':
797
				return self::BLOG_ID;
798
		}
799
800
		return $value;
801
	}
802
803
	/**
804
	 * Intercept the `Jetpack_Options` call and mock the values.
805
	 * Full connection set-up.
806
	 *
807
	 * @param mixed  $value The current option value.
808
	 * @param string $name Option name.
809
	 *
810
	 * @return mixed
811
	 */
812
	public function mock_jetpack_options( $value, $name ) {
813
		switch ( $name ) {
814
			case 'blog_token':
815
				return self::BLOG_TOKEN;
816
			case 'id':
817
				return self::BLOG_ID;
818
			case 'master_user':
819
				return self::USER_ID;
820
			case 'user_tokens':
821
				return array(
822
					self::USER_ID => 'new.usertoken.' . self::USER_ID,
823
				);
824
		}
825
826
		return $value;
827
	}
828
829
	/**
830
	 * Build the `connection/reconnect` request object.
831
	 *
832
	 * @return WP_REST_Request
833
	 */
834
	private function build_reconnect_request() {
835
		$this->request = new WP_REST_Request( 'POST', '/jetpack/v4/connection/reconnect' );
836
		$this->request->set_header( 'Content-Type', 'application/json' );
837
838
		return $this->request;
839
	}
840
841
	/**
842
	 * Setup the environment to test the reconnection process.
843
	 *
844
	 * @param string|null $invalid_token The invalid token to be returned in the response. Null if the tokens should be valid.
845
	 */
846
	private function setup_reconnect_test( $invalid_token ) {
847
		switch ( $invalid_token ) {
848
			case 'blog_token':
849
				add_filter(
850
					'pre_http_request',
851
					array(
852
						$this,
853
						'intercept_validate_tokens_request_invalid_blog_token',
854
					),
855
					10,
856
					3
857
				);
858
				break;
859
			case 'user_token':
860
				add_filter(
861
					'pre_http_request',
862
					array(
863
						$this,
864
						'intercept_validate_tokens_request_invalid_user_token',
865
					),
866
					10,
867
					3
868
				);
869
				break;
870
			case 'token_validation_failed':
871
				add_filter(
872
					'pre_http_request',
873
					array(
874
						$this,
875
						'intercept_validate_tokens_request_failed',
876
					),
877
					10,
878
					3
879
				);
880
				break;
881
			case null:
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $invalid_token of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
882
				add_filter(
883
					'pre_http_request',
884
					array(
885
						$this,
886
						'intercept_validate_tokens_request_valid_tokens',
887
					),
888
					10,
889
					3
890
				);
891
				break;
892
		}
893
894
		add_filter( 'jetpack_options', array( $this, 'mock_jetpack_options' ), 10, 2 );
895
	}
896
897
	/**
898
	 * Restore the environment after the `reconnect` test has been run.
899
	 *
900
	 * @param string|null $invalid_token The invalid token to be returned in the response. Null if the tokens should be valid.
901
	 */
902
	private function shutdown_reconnect_test( $invalid_token ) {
903
		switch ( $invalid_token ) {
904
			case 'blog_token':
905
				remove_filter(
906
					'pre_http_request',
907
					array(
908
						$this,
909
						'intercept_validate_tokens_request_invalid_blog_token',
910
					),
911
					10
912
				);
913
				break;
914
			case 'user_token':
915
				remove_filter(
916
					'pre_http_request',
917
					array(
918
						$this,
919
						'intercept_validate_tokens_request_invalid_user_token',
920
					),
921
					10
922
				);
923
				break;
924
			case 'token_validation_failed':
925
				remove_filter(
926
					'pre_http_request',
927
					array(
928
						$this,
929
						'intercept_validate_tokens_request_failed',
930
					),
931
					10
932
				);
933
				break;
934
			case null:
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $invalid_token of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
935
				remove_filter(
936
					'pre_http_request',
937
					array(
938
						$this,
939
						'intercept_validate_tokens_request_valid_tokens',
940
					),
941
					10
942
				);
943
				break;
944
		}
945
946
		remove_filter( 'jetpack_options', array( $this, 'mock_jetpack_options' ), 10 );
947
		remove_filter( 'pre_http_request', array( $this, 'intercept_validate_tokens_request' ), 10 );
948
	}
949
950
}
951