Completed
Push — add/user-token-restore ( 6b5dc6...e2c67a )
by
unknown
119:26 queued 108:15
created

Test_REST_Endpoints   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 910
Duplicated Lines 12.53 %

Coupling/Cohesion

Components 3
Dependencies 7

Importance

Changes 0
Metric Value
dl 114
loc 910
rs 2.97
c 0
b 0
f 0
wmc 64
lcom 3
cbo 7

36 Methods

Rating   Name   Duplication   Size   Complexity  
A set_up() 0 22 1
A tear_down() 0 12 1
B test_remote_authorize() 0 58 3
A test_connection() 0 15 1
A test_connection_jetpack_connection_status_filter() 0 19 1
A test_connection_plugins() 0 32 1
A test_connection_reconnect_full() 0 16 1
A test_connection_reconnect_partial_blog_token_success() 13 13 1
A test_connection_reconnect_partial_blog_token_fail() 13 13 1
A test_connection_reconnect_partial_user_token_success() 0 12 1
A test_connection_reconnect_site_connection() 0 15 1
A test_connection_reconnect_when_token_validation_request_fails() 0 16 1
A test_connection_register() 23 23 1
A test_connection_register_allow_inplace() 0 20 1
A test_connection_register_with_alternate_auth_url() 23 23 1
A test_set_user_token_unauthroized() 0 12 1
A test_set_user_token_success() 0 21 1
A test_set_user_token_site_not_connected() 0 16 1
A bypass_raw_options() 0 5 1
A intercept_register_request() 0 7 2
A intercept_register_request_with_allow_inplace() 0 7 2
A intercept_register_request_with_alternate_auth_url() 0 7 2
A get_register_request_mock_response() 0 17 1
A intercept_validate_tokens_request_invalid_blog_token() 0 7 2
A intercept_validate_tokens_request_invalid_user_token() 0 7 2
A intercept_validate_tokens_request_valid_tokens() 0 7 2
A intercept_validate_tokens_request_failed() 14 14 2
A build_validate_tokens_response() 0 35 3
A intercept_refresh_blog_token_request() 14 14 2
A intercept_refresh_blog_token_request_fail() 14 14 2
A intercept_auth_token_request() 0 20 2
A mock_jetpack_site_connection_options() 0 10 3
A mock_jetpack_options() 0 16 5
A build_reconnect_request() 0 6 1
B setup_reconnect_test() 0 50 5
B shutdown_reconnect_test() 0 47 5

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Test_REST_Endpoints often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Test_REST_Endpoints, and based on these observations, apply Extract Interface, too.

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