|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace App\Port\Tests\PHPUnit\Traits; |
|
4
|
|
|
|
|
5
|
|
|
use App; |
|
6
|
|
|
use App\Containers\Authorization\Models\Role; |
|
7
|
|
|
use App\Containers\User\Actions\CreateUserAction; |
|
8
|
|
|
use Dingo\Api\Http\Response as DingoAPIResponse; |
|
9
|
|
|
use Illuminate\Http\Response; |
|
10
|
|
|
use Illuminate\Support\Arr as LaravelArr; |
|
11
|
|
|
use Illuminate\Support\Str as LaravelStr; |
|
12
|
|
|
use Mockery; |
|
13
|
|
|
use Symfony\Component\Debug\Exception\UndefinedMethodException; |
|
14
|
|
|
|
|
15
|
|
|
/** |
|
16
|
|
|
* Class TestingTrait. |
|
17
|
|
|
* |
|
18
|
|
|
* All the functions in this trait are accessible from all your tests. |
|
19
|
|
|
* |
|
20
|
|
|
* @author Mahmoud Zalt <[email protected]> |
|
21
|
|
|
*/ |
|
22
|
|
|
trait TestingTrait |
|
23
|
|
|
{ |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* the Logged in user, used for protected routes. |
|
27
|
|
|
* |
|
28
|
|
|
* @var User |
|
29
|
|
|
*/ |
|
30
|
|
|
public $loggedInTestingUser; |
|
31
|
|
|
|
|
32
|
|
|
/** |
|
33
|
|
|
* @param $endpoint |
|
34
|
|
|
* @param string $verb |
|
35
|
|
|
* @param array $data |
|
36
|
|
|
* @param bool|true $protected |
|
37
|
|
|
* @param array $header |
|
38
|
|
|
* |
|
39
|
|
|
* @throws \Symfony\Component\Debug\Exception\UndefinedMethodException |
|
40
|
|
|
* |
|
41
|
|
|
* @return mixed |
|
42
|
|
|
*/ |
|
43
|
|
|
public function apiCall($endpoint, $verb = 'get', array $data = [], $protected = true, array $header = []) |
|
44
|
|
|
{ |
|
45
|
|
|
$content = json_encode($data); |
|
46
|
|
|
|
|
47
|
|
|
$headers = array_merge([ |
|
48
|
|
|
'CONTENT_LENGTH' => mb_strlen($content, '8bit'), |
|
49
|
|
|
// 'CONTENT_TYPE' => 'application/json', |
|
|
|
|
|
|
50
|
|
|
'Accept' => 'application/json', |
|
51
|
|
|
], $header); |
|
52
|
|
|
|
|
53
|
|
|
// if endpoint is protected (requires token to access it's functionality) |
|
54
|
|
|
if ($protected && !array_has($header, 'Authorization')) { |
|
55
|
|
|
// append the token to the header |
|
56
|
|
|
$headers['Authorization'] = 'Bearer ' . $this->getLoggedInTestingUserToken(); |
|
57
|
|
|
} |
|
58
|
|
|
|
|
59
|
|
|
$verb = strtolower($verb); |
|
60
|
|
|
|
|
61
|
|
|
switch ($verb) { |
|
62
|
|
|
case 'get': |
|
63
|
|
|
$endpoint = $data ? $endpoint . '?' . http_build_query($data) : $endpoint; |
|
64
|
|
|
$response = $this->get($endpoint, $headers)->response; |
|
|
|
|
|
|
65
|
|
|
break; |
|
66
|
|
|
case 'post': |
|
67
|
|
|
case 'put': |
|
68
|
|
|
case 'patch': |
|
69
|
|
|
case 'delete': |
|
70
|
|
|
$response = $this->{$verb}($endpoint, $data, $headers)->response; |
|
71
|
|
|
break; |
|
72
|
|
|
default: |
|
73
|
|
|
throw new UndefinedMethodException('Undefined HTTP Verb (' . $verb . ').'); |
|
|
|
|
|
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
return $response; |
|
77
|
|
|
} |
|
78
|
|
|
|
|
79
|
|
|
/** |
|
80
|
|
|
* @param \Dingo\Api\Http\Response $response |
|
81
|
|
|
* @param array $messages |
|
82
|
|
|
*/ |
|
83
|
|
|
public function assertValidationErrorContain(DingoAPIResponse $response, array $messages) |
|
84
|
|
|
{ |
|
85
|
|
|
|
|
86
|
|
|
$arrayResponse = json_decode($response->getContent()); |
|
87
|
|
|
|
|
88
|
|
|
foreach ($messages as $key => $value) { |
|
89
|
|
|
$this->assertEquals($arrayResponse->errors->{$key}[0], $value); |
|
|
|
|
|
|
90
|
|
|
} |
|
91
|
|
|
} |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* get teh current logged in user. |
|
95
|
|
|
* |
|
96
|
|
|
* @return \App\Port\Tests\PHPUnit\Traits\User|mixed |
|
97
|
|
|
*/ |
|
98
|
|
|
public function getLoggedInTestingUser() |
|
99
|
|
|
{ |
|
100
|
|
|
$user = $this->loggedInTestingUser; |
|
101
|
|
|
|
|
102
|
|
|
if (!$user) { |
|
103
|
|
|
$user = $this->registerAndLoginTestingUser(); |
|
104
|
|
|
} |
|
105
|
|
|
|
|
106
|
|
|
return $user; |
|
107
|
|
|
} |
|
108
|
|
|
|
|
109
|
|
|
/** |
|
110
|
|
|
* This returned visitor is a normal user, with `visitor_id` means |
|
111
|
|
|
* before he became a registered user (can login) was a visitor. |
|
112
|
|
|
* So this can be used to test endpoints that are protected by visitors |
|
113
|
|
|
* access. |
|
114
|
|
|
* |
|
115
|
|
|
* @return \App\Port\Tests\PHPUnit\Traits\User|mixed |
|
116
|
|
|
*/ |
|
117
|
|
|
public function getVisitor() |
|
118
|
|
|
{ |
|
119
|
|
|
$user = $this->getLoggedInTestingUser(); |
|
120
|
|
|
|
|
121
|
|
|
$user->visitor_id = str_random('20'); |
|
122
|
|
|
unset($user->token); |
|
123
|
|
|
$user->save(); |
|
124
|
|
|
|
|
125
|
|
|
return $user; |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
/** |
|
129
|
|
|
* @return \App\Port\Tests\PHPUnit\Traits\User|mixed |
|
130
|
|
|
*/ |
|
131
|
|
|
public function getLoggedInTestingAdmin() |
|
132
|
|
|
{ |
|
133
|
|
|
$user = $this->getLoggedInTestingUser(); |
|
134
|
|
|
|
|
135
|
|
|
$user = $this->makeAdmin($user); |
|
136
|
|
|
|
|
137
|
|
|
return $user; |
|
138
|
|
|
} |
|
139
|
|
|
|
|
140
|
|
|
/** |
|
141
|
|
|
* @param $user |
|
142
|
|
|
* |
|
143
|
|
|
* @return mixed |
|
144
|
|
|
*/ |
|
145
|
|
|
public function makeAdmin($user) |
|
146
|
|
|
{ |
|
147
|
|
|
$adminRole = Role::where('name', 'admin')->first(); |
|
148
|
|
|
|
|
149
|
|
|
$user->attachRole($adminRole); |
|
150
|
|
|
|
|
151
|
|
|
return $user; |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
/** |
|
155
|
|
|
* get teh current logged in user token. |
|
156
|
|
|
* |
|
157
|
|
|
* @return string |
|
158
|
|
|
*/ |
|
159
|
|
|
public function getLoggedInTestingUserToken() |
|
160
|
|
|
{ |
|
161
|
|
|
return $this->getLoggedInTestingUser()->token; |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
|
|
/** |
|
165
|
|
|
* @param null $userDetails |
|
166
|
|
|
* |
|
167
|
|
|
* @return mixed |
|
168
|
|
|
*/ |
|
169
|
|
|
public function registerAndLoginTestingUser($userDetails = null) |
|
170
|
|
|
{ |
|
171
|
|
|
// if no user detail provided, use the default details. |
|
172
|
|
|
if (!$userDetails) { |
|
173
|
|
|
$userDetails = [ |
|
174
|
|
|
'name' => 'Mahmoud Zalt', |
|
175
|
|
|
'email' => '[email protected]', |
|
176
|
|
|
'password' => 'secret.Pass7', |
|
177
|
|
|
]; |
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
|
|
$createUserAction = App::make(CreateUserAction::class); |
|
181
|
|
|
|
|
182
|
|
|
// create new user and login (true) |
|
|
|
|
|
|
183
|
|
|
$user = $createUserAction->run( |
|
184
|
|
|
$userDetails['email'], |
|
185
|
|
|
$userDetails['password'], |
|
186
|
|
|
$userDetails['name'], |
|
187
|
|
|
null, |
|
188
|
|
|
null, |
|
189
|
|
|
true |
|
190
|
|
|
); |
|
191
|
|
|
|
|
192
|
|
|
return $this->loggedInTestingUser = $user; |
|
193
|
|
|
} |
|
194
|
|
|
|
|
195
|
|
|
/** |
|
196
|
|
|
* @param null $userDetails |
|
197
|
|
|
* |
|
198
|
|
|
* @return mixed |
|
199
|
|
|
*/ |
|
200
|
|
|
public function registerAndLoginTestingAdmin($userDetails = null) |
|
201
|
|
|
{ |
|
202
|
|
|
$user = $this->registerAndLoginTestingUser($userDetails); |
|
203
|
|
|
|
|
204
|
|
|
$user = $this->makeAdmin($user); |
|
205
|
|
|
|
|
206
|
|
|
return $user; |
|
207
|
|
|
} |
|
208
|
|
|
|
|
209
|
|
|
/** |
|
210
|
|
|
* @param $keys |
|
211
|
|
|
* @param $response |
|
212
|
|
|
*/ |
|
213
|
|
|
public function assertResponseContainKeys($keys, $response) |
|
214
|
|
|
{ |
|
215
|
|
|
if (!is_array($keys)) { |
|
216
|
|
|
$keys = (array)$keys; |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
foreach ($keys as $key) { |
|
220
|
|
|
$this->assertTrue(array_key_exists($key, $this->responseToArray($response))); |
|
|
|
|
|
|
221
|
|
|
} |
|
222
|
|
|
} |
|
223
|
|
|
|
|
224
|
|
|
/** |
|
225
|
|
|
* @param $values |
|
226
|
|
|
* @param $response |
|
227
|
|
|
*/ |
|
228
|
|
|
public function assertResponseContainValues($values, $response) |
|
229
|
|
|
{ |
|
230
|
|
|
if (!is_array($values)) { |
|
231
|
|
|
$values = (array)$values; |
|
232
|
|
|
} |
|
233
|
|
|
|
|
234
|
|
|
foreach ($values as $value) { |
|
235
|
|
|
$this->assertTrue(in_array($value, $this->responseToArray($response))); |
|
|
|
|
|
|
236
|
|
|
} |
|
237
|
|
|
} |
|
238
|
|
|
|
|
239
|
|
|
/** |
|
240
|
|
|
* @param $data |
|
241
|
|
|
* @param $response |
|
242
|
|
|
*/ |
|
243
|
|
|
public function assertResponseContainKeyValue($data, $response) |
|
244
|
|
|
{ |
|
245
|
|
|
$response = json_encode(LaravelArr::sortRecursive( |
|
246
|
|
|
(array)$this->responseToArray($response) |
|
247
|
|
|
)); |
|
248
|
|
|
|
|
249
|
|
|
foreach (LaravelArr::sortRecursive($data) as $key => $value) { |
|
250
|
|
|
$expected = $this->formatToExpectedJson($key, $value); |
|
|
|
|
|
|
251
|
|
|
$this->assertTrue(LaravelStr::contains($response, $expected), |
|
|
|
|
|
|
252
|
|
|
"The JSON fragment [ {$expected} ] does not exist in the response [ {$response} ]."); |
|
253
|
|
|
} |
|
254
|
|
|
} |
|
255
|
|
|
|
|
256
|
|
|
/** |
|
257
|
|
|
* Migrate the database. |
|
258
|
|
|
*/ |
|
259
|
|
|
public function migrateDatabase() |
|
260
|
|
|
{ |
|
261
|
|
|
\Artisan::call('migrate'); |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
/** |
|
265
|
|
|
* @param $response |
|
266
|
|
|
* |
|
267
|
|
|
* @return mixed |
|
268
|
|
|
*/ |
|
269
|
|
|
private function responseToArray($response) |
|
270
|
|
|
{ |
|
271
|
|
|
if ($response instanceof \Illuminate\Http\Response) { |
|
272
|
|
|
$response = json_decode($response->getContent(), true); |
|
273
|
|
|
} |
|
274
|
|
|
|
|
275
|
|
|
if (array_key_exists('data', $response)) { |
|
276
|
|
|
$response = $response['data']; |
|
277
|
|
|
} |
|
278
|
|
|
|
|
279
|
|
|
return $response; |
|
280
|
|
|
} |
|
281
|
|
|
|
|
282
|
|
|
/** |
|
283
|
|
|
* Format the given key and value into a JSON string for expectation checks. |
|
284
|
|
|
* |
|
285
|
|
|
* @param string $key |
|
286
|
|
|
* @param mixed $value |
|
287
|
|
|
* |
|
288
|
|
|
* @return string |
|
289
|
|
|
*/ |
|
290
|
|
|
private function formatToKeyValueToString($key, $value) |
|
291
|
|
|
{ |
|
292
|
|
|
$expected = json_encode([$key => $value]); |
|
293
|
|
|
|
|
294
|
|
|
if (LaravelStr::startsWith($expected, '{')) { |
|
295
|
|
|
$expected = substr($expected, 1); |
|
296
|
|
|
} |
|
297
|
|
|
|
|
298
|
|
|
if (LaravelStr::endsWith($expected, '}')) { |
|
299
|
|
|
$expected = substr($expected, 0, -1); |
|
300
|
|
|
} |
|
301
|
|
|
|
|
302
|
|
|
return $expected; |
|
303
|
|
|
} |
|
304
|
|
|
|
|
305
|
|
|
/** |
|
306
|
|
|
* Mocking helper |
|
307
|
|
|
* |
|
308
|
|
|
* @param $class |
|
309
|
|
|
* |
|
310
|
|
|
* @return \Mockery\MockInterface |
|
311
|
|
|
*/ |
|
312
|
|
|
public function mock($class) |
|
313
|
|
|
{ |
|
314
|
|
|
$mock = Mockery::mock($class); |
|
315
|
|
|
App::instance($class, $mock); |
|
316
|
|
|
|
|
317
|
|
|
return $mock; |
|
318
|
|
|
} |
|
319
|
|
|
|
|
320
|
|
|
/** |
|
321
|
|
|
* get response object, get the string content from it and convert it to an std object |
|
322
|
|
|
* making it easier to read |
|
323
|
|
|
* |
|
324
|
|
|
* @param $response |
|
325
|
|
|
* |
|
326
|
|
|
* @return mixed |
|
327
|
|
|
*/ |
|
328
|
|
|
public function getResponseObject(Response $response) |
|
329
|
|
|
{ |
|
330
|
|
|
return json_decode($response->getContent()); |
|
331
|
|
|
} |
|
332
|
|
|
|
|
333
|
|
|
/** |
|
334
|
|
|
* Inject the ID in the Endpoint URI |
|
335
|
|
|
* |
|
336
|
|
|
* Example: you give it ('users/{id}/stores', 100) it returns 'users/100/stores' |
|
337
|
|
|
* |
|
338
|
|
|
* @param $endpoint |
|
339
|
|
|
* @param $id |
|
340
|
|
|
* |
|
341
|
|
|
* @return mixed |
|
342
|
|
|
*/ |
|
343
|
|
|
public function injectEndpointId($endpoint, $id) |
|
344
|
|
|
{ |
|
345
|
|
|
return str_replace("{id}", $id, $endpoint); |
|
346
|
|
|
} |
|
347
|
|
|
|
|
348
|
|
|
} |
|
349
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.