Completed
Push — master ( 170fde...562033 )
by Jan
03:08
created

ApiClientTest::testRequests()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 67
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
c 5
b 0
f 0
dl 0
loc 67
rs 8.5896
cc 6
eloc 45
nc 6
nop 8

How to fix   Long Method    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace SlevomatCsobGateway\Api;
4
5
use SlevomatCsobGateway\Crypto\CryptoService;
6
use SlevomatCsobGateway\Crypto\SignatureDataFormatter;
7
8
class ApiClientTest extends \PHPUnit_Framework_TestCase
9
{
10
11
	const API_URL = 'http://foo.csob.cz';
12
13
	public function getRequests()
14
	{
15
		return [
16
			[
17
				new HttpMethod(HttpMethod::GET),
18
				'fooUrl/{dttm}/{signature}',
19
				'fooUrl/' . date('YmdHis') . '/signature',
20
				[],
21
				null,
22
				[
23
					'bar' => 2,
24
				],
25
				new ResponseCode(ResponseCode::S200_OK),
26
				[
27
					'header' => 'value',
28
				],
29
			],
30
			[
31
				new HttpMethod(HttpMethod::GET),
32
				'fooUrl/{fooId}/{dttm}/{signature}',
33
				'fooUrl/3/' . date('YmdHis') . '/signature',
34
				[
35
					'fooId' => 3,
36
				],
37
				null,
38
				[
39
					'bar' => 2,
40
				],
41
				new ResponseCode(ResponseCode::S200_OK),
42
				[
43
					'header' => 'value',
44
				],
45
			],
46
			[
47
				new HttpMethod(HttpMethod::POST),
48
				'fooUrl',
49
				'fooUrl',
50
				[
51
					'foo' => 1,
52
				],
53
				[
54
					'foo' => 1,
55
				],
56
				[
57
					'bar' => 2,
58
				],
59
				new ResponseCode(ResponseCode::S200_OK),
60
				[
61
					'header' => 'value',
62
				],
63
			],
64
			[
65
				new HttpMethod(HttpMethod::PUT),
66
				'fooUrl',
67
				'fooUrl',
68
				[
69
					'foo' => 1,
70
				],
71
				[
72
					'foo' => 1,
73
				],
74
				[
75
					'bar' => 2,
76
				],
77
				new ResponseCode(ResponseCode::S200_OK),
78
				[
79
					'header' => 'value',
80
				],
81
			],
82
			[
83
				new HttpMethod(HttpMethod::GET),
84
				'fooUrl/{dttm}/{signature}',
85
				'fooUrl/' . date('YmdHis') . '/signature',
86
				[],
87
				null,
88
				null,
89
				new ResponseCode(ResponseCode::S303_SEE_OTHER),
90
				[
91
					'header' => 'value',
92
				],
93
			],
94
		];
95
	}
96
97
	/**
98
	 * @param HttpMethod $httpMethod
99
	 * @param string $url
100
	 * @param string $expectedUrl
101
	 * @param mixed[] $requestData
102
	 * @param mixed[]|null $expectedRequestData
103
	 * @param mixed[]|null $responseData
104
	 * @param ResponseCode $responseCode
105
	 * @param mixed[] $responseHeaders
106
	 *
107
	 * @dataProvider getRequests
108
	 */
109
	public function testRequests(HttpMethod $httpMethod, $url, $expectedUrl, array $requestData, array $expectedRequestData = null, $responseData, ResponseCode $responseCode, array $responseHeaders)
110
	{
111
		self::markTestIncomplete('Contains imprecise DateTime handling');
112
		$currentTime = new \DateTimeImmutable();
113
114
		$cryptoService = $this->getMockBuilder(CryptoService::class)
115
			->disableOriginalConstructor()
116
			->getMock();
117
118
		$cryptoService->expects(self::any())
119
			->method('signData')
120
			->willReturn('signature');
121
122
		$cryptoService->expects(self::any())
123
			->method('verifyData')
124
			->willReturn(true);
125
126
		/** @var CryptoService $cryptoService */
127
		$apiClientDriver = $this->getMockBuilder(ApiClientDriver::class)
128
			->getMock();
129
130
		if ($httpMethod->equalsValue(HttpMethod::GET)) {
131
			$apiClientDriver->expects(self::once())
132
				->method('request')
133
				->with($httpMethod, self::API_URL . '/' . $expectedUrl, $expectedRequestData)
134
				->willReturn(new Response(
135
					$responseCode,
136
					($responseData ? $responseData : []) + [
137
						'signature' => 'signature',
138
					],
139
					$responseHeaders
140
				));
141
142
		} else {
143
			$apiClientDriver->expects(self::once())
144
				->method('request')
145
				->with($httpMethod, self::API_URL . '/' . $expectedUrl, $expectedRequestData + [
146
						'signature' => $cryptoService->signData($requestData, new SignatureDataFormatter([])),
147
						'dttm' => $currentTime->format('YmdHis'),
148
					])
149
				->willReturn(new Response(
150
					$responseCode,
151
					($responseData ? $responseData : []) + [
152
						'signature' => 'signature',
153
					],
154
					$responseHeaders
155
				));
156
		}
157
158
		/** @var ApiClientDriver $apiClientDriver */
159
		$apiClient = new ApiClient($apiClientDriver, $cryptoService, self::API_URL);
160
161
		if ($httpMethod->equalsValue(HttpMethod::GET)) {
162
			$response = $apiClient->get($url, $requestData, new SignatureDataFormatter([]), new SignatureDataFormatter([]));
163
164
		} elseif ($httpMethod->equalsValue(HttpMethod::POST)) {
165
			$response = $apiClient->post($url, $requestData, new SignatureDataFormatter([]), new SignatureDataFormatter([]));
166
167
		} else {
168
			$response = $apiClient->put($url, $requestData, new SignatureDataFormatter([]), new SignatureDataFormatter([]));
169
		}
170
171
		$this->assertInstanceOf(Response::class, $response);
172
		$this->assertSame($responseCode->getValue(), $response->getResponseCode()->getValue());
173
		$this->assertEquals($responseHeaders, $response->getHeaders());
174
		$this->assertEquals($responseData, $response->getData());
175
	}
176
177
	public function getTestExceptions()
178
	{
179
		return [
180
			[
181
				new Response(
182
					new ResponseCode(ResponseCode::S400_BAD_REQUEST),
183
					[]
184
				),
185
				BadRequestException::class,
186
			],
187
			[
188
				new Response(
189
					new ResponseCode(ResponseCode::S403_FORBIDDEN),
190
					[]
191
				),
192
				ForbiddenException::class,
193
			],
194
			[
195
				new Response(
196
					new ResponseCode(ResponseCode::S404_NOT_FOUND),
197
					[]
198
				),
199
				NotFoundException::class,
200
			],
201
			[
202
				new Response(
203
					new ResponseCode(ResponseCode::S405_METHOD_NOT_ALLOWED),
204
					[]
205
				),
206
				MethodNotAllowedException::class,
207
			],
208
			[
209
				new Response(
210
					new ResponseCode(ResponseCode::S429_TOO_MANY_REQUESTS),
211
					[]
212
				),
213
				TooManyRequestsException::class,
214
			],
215
			[
216
				new Response(
217
					new ResponseCode(ResponseCode::S503_SERVICE_UNAVAILABLE),
218
					[]
219
				),
220
				ServiceUnavailableException::class,
221
			],
222
			[
223
				new Response(
224
					new ResponseCode(ResponseCode::S500_INTERNAL_ERROR),
225
					[]
226
				),
227
				InternalErrorException::class,
228
			],
229
		];
230
	}
231
232
	/**
233
	 * @param Response $response
234
	 * @param string $expectedExceptionClass
235
	 *
236
	 * @dataProvider getTestExceptions
237
	 */
238
	public function testExceptions(Response $response, $expectedExceptionClass)
239
	{
240
		$cryptoService = $this->getMockBuilder(CryptoService::class)
241
			->disableOriginalConstructor()
242
			->getMock();
243
244
		$cryptoService->expects(self::once())
245
			->method('signData')
246
			->willReturn('signature');
247
248
		$cryptoService->expects(self::any())
249
			->method('verifyData')
250
			->willReturn(true);
251
252
		$apiClientDriver = $this->getMockBuilder(ApiClientDriver::class)
253
			->getMock();
254
255
		$apiClientDriver->expects(self::once())
256
			->method('request')
257
			->willReturn($response);
258
259
		/** @var CryptoService $cryptoService */
260
		/** @var ApiClientDriver $apiClientDriver */
261
		$apiClient = new ApiClient($apiClientDriver, $cryptoService);
262
263
		try {
264
			$apiClient->get('foo/{dttm}/{signature}', [], new SignatureDataFormatter([]), new SignatureDataFormatter([]));
265
			$this->fail();
266
267
		} catch (RequestException $e) {
268
			$this->assertInstanceOf($expectedExceptionClass, $e);
269
			$this->assertSame($response, $e->getResponse());
270
		}
271
	}
272
273
	public function testMissingSignature()
274
	{
275
		$response = new Response(
276
			new ResponseCode(ResponseCode::S200_OK),
277
			[]
278
		);
279
280
		$cryptoService = $this->getMockBuilder(CryptoService::class)
281
			->disableOriginalConstructor()
282
			->getMock();
283
284
		$cryptoService->expects(self::once())
285
			->method('signData')
286
			->willReturn('signature');
287
288
		$apiClientDriver = $this->getMockBuilder(ApiClientDriver::class)
289
			->getMock();
290
291
		$apiClientDriver->expects(self::once())
292
			->method('request')
293
			->willReturn($response);
294
295
		/** @var CryptoService $cryptoService */
296
		/** @var ApiClientDriver $apiClientDriver */
297
		$apiClient = new ApiClient($apiClientDriver, $cryptoService);
298
299
		try {
300
			$apiClient->get('foo/{dttm}/{signature}', [], new SignatureDataFormatter([]), new SignatureDataFormatter([]));
301
			$this->fail();
302
303
		} catch (InvalidSignatureException $e) {
304
			$this->assertSame($response, $e->getResponse());
305
		}
306
	}
307
308
	public function testInvalidSignature()
309
	{
310
		$response = new Response(
311
			new ResponseCode(ResponseCode::S200_OK),
312
			[
313
				'signature' => 'invalidSignature',
314
			]
315
		);
316
317
		$cryptoService = $this->getMockBuilder(CryptoService::class)
318
			->disableOriginalConstructor()
319
			->getMock();
320
321
		$cryptoService->expects(self::once())
322
			->method('signData')
323
			->willReturn('signature');
324
325
		$cryptoService->expects(self::any())
326
			->method('verifyData')
327
			->willReturn(false);
328
329
		$apiClientDriver = $this->getMockBuilder(ApiClientDriver::class)
330
			->getMock();
331
332
		$apiClientDriver->expects(self::once())
333
			->method('request')
334
			->willReturn($response);
335
336
		/** @var CryptoService $cryptoService */
337
		/** @var ApiClientDriver $apiClientDriver */
338
		$apiClient = new ApiClient($apiClientDriver, $cryptoService);
339
340
		try {
341
			$apiClient->get('foo/{dttm}/{signature}', [], new SignatureDataFormatter([]), new SignatureDataFormatter([]));
342
			$this->fail();
343
344
		} catch (InvalidSignatureException $e) {
345
			$this->assertSame($response, $e->getResponse());
346
		}
347
	}
348
349
	public function testCreateResponseByData()
350
	{
351
		$data = [
352
			'signature' => 'abc',
353
			'foo' => 123,
354
			'bar' => 456,
355
		];
356
357
		$cryptoService = $this->getMockBuilder(CryptoService::class)
358
			->disableOriginalConstructor()
359
			->getMock();
360
361
		$cryptoService->expects(self::any())
362
			->method('verifyData')
363
			->willReturn(true);
364
365
		$apiClientDriver = $this->getMockBuilder(ApiClientDriver::class)
366
			->getMock();
367
368
		/** @var CryptoService $cryptoService */
369
		/** @var ApiClientDriver $apiClientDriver */
370
		$apiClient = new ApiClient($apiClientDriver, $cryptoService);
371
372
		$response = $apiClient->createResponseByData($data, new SignatureDataFormatter([]));
373
374
		unset($data['signature']);
375
376
		$this->assertInstanceOf(Response::class, $response);
377
		$this->assertSame(ResponseCode::S200_OK, $response->getResponseCode()->getValue());
378
		$this->assertEquals([], $response->getHeaders());
379
		$this->assertEquals($data, $response->getData());
380
	}
381
382
}
383