Completed
Push — feature/replace-yarn-1-with-pn... ( a7a72b...b69108 )
by
unknown
649:09 queued 639:32
created

intercept_validate_tokens_request_failed()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 14
Ratio 100 %

Importance

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