AbstractRestApiTest::assertExpectedResponse()   A
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 19
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 16
nc 6
nop 3
dl 0
loc 19
rs 9.1111
c 1
b 0
f 0
1
<?php
2
/**
3
 * REST API Endpoint Test base class.
4
 *
5
 * This class can be extended to add test coverage to REST API endpoints.
6
 *
7
 * For each endpoint, please test:
8
 * - Create
9
 * - Read
10
 * - Update
11
 * - Delete
12
 * - How the API responds to logged out/unauthorised users
13
 * - Schema
14
 * - Routes
15
 * - Collection params/queries
16
 *
17
 * @package Automattic/WooCommerce/RestApi/Tests
18
 */
19
20
namespace Automattic\WooCommerce\RestApi\UnitTests;
21
22
defined( 'ABSPATH' ) || exit;
23
24
use \WC_REST_Unit_Test_Case;
0 ignored issues
show
Bug introduced by
The type WC_REST_Unit_Test_Case was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
25
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\CustomerHelper;
26
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper;
27
use Automattic\WooCommerce\RestApi\UnitTests\Helpers\ProductHelper;
28
29
/**
30
 * Abstract Rest API Test Class
31
 *
32
 * @extends  WC_REST_Unit_Test_Case
33
 */
34
abstract class AbstractRestApiTest extends WC_REST_Unit_Test_Case {
35
36
	/**
37
	 * The endpoint schema.
38
	 *
39
	 * @var array Keys are property names, values are supported context.
40
	 */
41
	protected $properties = [];
42
43
	/**
44
	 * Routes that this endpoint creates.
45
	 *
46
	 * @var array
47
	 */
48
	protected $routes = [];
49
50
	/**
51
	 * User variable.
52
	 *
53
	 * @var WP_User
0 ignored issues
show
Bug introduced by
The type Automattic\WooCommerce\RestApi\UnitTests\WP_User was not found. Did you mean WP_User? If so, make sure to prefix the type with \.
Loading history...
54
	 */
55
	protected static $user;
56
57
	/**
58
	 * Setup once before running tests.
59
	 *
60
	 * @param object $factory Factory object.
61
	 */
62
	public static function wpSetUpBeforeClass( $factory ) {
63
		self::$user = $factory->user->create(
64
			array(
65
				'role' => 'administrator',
66
			)
67
		);
68
	}
69
70
	/**
71
	 * Setup test class.
72
	 */
73
	public function setUp() {
74
		parent::setUp();
75
		wp_set_current_user( self::$user );
0 ignored issues
show
Bug introduced by
self::user of type Automattic\WooCommerce\RestApi\UnitTests\WP_User is incompatible with the type integer expected by parameter $id of wp_set_current_user(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

75
		wp_set_current_user( /** @scrutinizer ignore-type */ self::$user );
Loading history...
76
	}
77
78
	/**
79
	 * Test route registration.
80
	 */
81
	public function test_register_routes() {
82
		$actual_routes   = $this->server->get_routes();
83
		$expected_routes = $this->routes;
84
85
		foreach ( $expected_routes as $expected_route ) {
86
			$this->assertArrayHasKey( $expected_route, $actual_routes );
87
		}
88
	}
89
90
	/**
91
	 * Validate that the returned API schema matches what is expected.
92
	 *
93
	 * @return void
94
	 */
95
	public function test_schema_properties() {
96
		$request    = new \WP_REST_Request( 'OPTIONS', $this->routes[0] );
97
		$response   = $this->server->dispatch( $request );
98
		$data       = $response->get_data();
99
		$properties = $data['schema']['properties'];
100
101
		$this->assertEquals( count( array_keys( $this->properties ) ), count( $properties ), print_r( array_diff( array_keys( $properties ), array_keys( $this->properties ) ), true ) );
102
103
		foreach ( array_keys( $this->properties ) as $property ) {
104
			$this->assertArrayHasKey( $property, $properties );
105
		}
106
	}
107
108
	/**
109
	 * Test creation using this method.
110
	 * If read-only, test to confirm this.
111
	 */
112
	abstract public function test_create();
113
114
	/**
115
	 * Test get/read using this method.
116
	 */
117
	abstract public function test_read();
118
119
	/**
120
	 * Test updates using this method.
121
	 * If read-only, test to confirm this.
122
	 */
123
	abstract public function test_update();
124
125
	/**
126
	 * Test delete using this method.
127
	 * If read-only, test to confirm this.
128
	 */
129
	abstract public function test_delete();
130
131
	/**
132
	 * Perform a request and return the status and returned data.
133
	 *
134
	 * @param string  $endpoint Endpoint to hit.
135
	 * @param string  $type Type of request e.g GET or POST.
136
	 * @param array   $params Request body or query.
137
	 * @return object
138
	 */
139
	protected function do_request( $endpoint, $type = 'GET', $params = [] ) {
140
		$request = new \WP_REST_Request( $type, untrailingslashit( $endpoint ) );
141
		'GET' === $type ? $request->set_query_params( $params ) : $request->set_body_params( $params );
142
		$response = $this->server->dispatch( $request );
143
144
		return (object) array(
145
			'status' => $response->get_status(),
146
			'data'   => json_decode( wp_json_encode( $response->get_data() ), true ),
0 ignored issues
show
Bug introduced by
It seems like wp_json_encode($response->get_data()) can also be of type false; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
			'data'   => json_decode( /** @scrutinizer ignore-type */ wp_json_encode( $response->get_data() ), true ),
Loading history...
147
			'raw'    => $response->get_data(),
148
		);
149
	}
150
151
	/**
152
	 * Test the request/response matched the data we sent.
153
	 *
154
	 * @param array $response Array of response data from do_request above.
155
	 * @param int   $status_code Expected status code.
156
	 * @param array $data Array of expected data.
157
	 */
158
	protected function assertExpectedResponse( $response, $status_code = 200, $data = array() ) {
159
		$this->assertObjectHasAttribute( 'status', $response );
160
		$this->assertObjectHasAttribute( 'data', $response );
161
		$this->assertEquals( $status_code, $response->status, print_r( $response->data, true ) );
162
163
		if ( $data ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
164
			foreach ( $data as $key => $value ) {
165
				if ( ! isset( $response->data[ $key ] ) ) {
166
					continue;
167
				}
168
				switch ( $key ) {
169
					case 'meta_data':
170
						$this->assertMetaData( $value, $response->data[ $key ] );
171
						break;
172
					default:
173
						if ( is_array( $value ) ) {
174
							$this->assertArraySubset( $value, $response->data[ $key ], print_r( array( 'key' => $key, 'data' => $response->data ), true ) );
175
						} else {
176
							$this->assertEquals( $value, $response->data[ $key ], print_r( array( 'key' => $key, 'data' => $response->data ), true ) );
177
						}
178
				}
179
			}
180
		}
181
	}
182
183
	/**
184
	 * Test meta data in a response matches what we expect.
185
	 *
186
	 * @param array $expected_meta_data Array of data.
187
	 * @param array $actual_meta_data Array of data.
188
	 */
189
	protected function assertMetaData( $expected_meta_data, $actual_meta_data ) {
190
		$this->assertTrue( is_array( $actual_meta_data ) );
191
		$this->assertEquals( count( $expected_meta_data ), count( $actual_meta_data ) );
192
193
		foreach ( $actual_meta_data as $key => $meta ) {
194
			$this->assertArrayHasKey( 'id', $meta );
195
			$this->assertArrayHasKey( 'key', $meta );
196
			$this->assertArrayHasKey( 'value', $meta );
197
			$this->assertEquals( $expected_meta_data[ $key ]['key'], $meta['key'] );
198
			$this->assertEquals( $expected_meta_data[ $key ]['value'], $meta['value'] );
199
		}
200
	}
201
202
	/**
203
	 * Return array of properties for a given context.
204
	 *
205
	 * @param string $context Context to use.
206
	 * @return array
207
	 */
208
	protected function get_properties( $context = 'edit' ) {
209
		return array_keys( array_filter( $this->properties, function( $contexts ) use( $context ) {
210
			return in_array( $context, $contexts );
211
		} ) );
212
	}
213
}
214